Roo/bootstrap/TabPanel.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 false;
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             //class="carousel-item carousel-item-next carousel-item-left"
18361             
18362             this.transition = true;
18363             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18364             var lr = dir == 'next' ? 'left' : 'right';
18365             pan.el.addClass(dir); // or prev
18366             pan.el.addClass('carousel-item-' + dir); // or prev
18367             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18368             cur.el.addClass(lr); // or right
18369             pan.el.addClass(lr);
18370             cur.el.addClass('carousel-item-' +lr); // or right
18371             pan.el.addClass('carousel-item-' +lr);
18372             
18373             
18374             var _this = this;
18375             cur.el.on('transitionend', function() {
18376                 Roo.log("trans end?");
18377                 
18378                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18379                 pan.setActive(true);
18380                 
18381                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18382                 cur.setActive(false);
18383                 
18384                 _this.transition = false;
18385                 
18386             }, this, { single:  true } );
18387             
18388             return true;
18389         }
18390         
18391         cur.setActive(false);
18392         pan.setActive(true);
18393         
18394         return true;
18395         
18396     },
18397     showPanelNext : function()
18398     {
18399         var i = this.indexOfPanel(this.getActivePanel());
18400         
18401         if (i >= this.tabs.length - 1 && !this.autoslide) {
18402             return;
18403         }
18404         
18405         if (i >= this.tabs.length - 1 && this.autoslide) {
18406             i = -1;
18407         }
18408         
18409         this.showPanel(this.tabs[i+1]);
18410     },
18411     
18412     showPanelPrev : function()
18413     {
18414         var i = this.indexOfPanel(this.getActivePanel());
18415         
18416         if (i  < 1 && !this.autoslide) {
18417             return;
18418         }
18419         
18420         if (i < 1 && this.autoslide) {
18421             i = this.tabs.length;
18422         }
18423         
18424         this.showPanel(this.tabs[i-1]);
18425     },
18426     
18427     
18428     addBullet: function()
18429     {
18430         if(!this.bullets || Roo.isTouch){
18431             return;
18432         }
18433         var ctr = this.el.select('.carousel-bullets',true).first();
18434         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18435         var bullet = ctr.createChild({
18436             cls : 'bullet bullet-' + i
18437         },ctr.dom.lastChild);
18438         
18439         
18440         var _this = this;
18441         
18442         bullet.on('click', (function(e, el, o, ii, t){
18443
18444             e.preventDefault();
18445
18446             this.showPanel(ii);
18447
18448             if(this.autoslide && this.slideFn){
18449                 clearInterval(this.slideFn);
18450                 this.slideFn = window.setInterval(function() {
18451                     _this.showPanelNext();
18452                 }, this.timer);
18453             }
18454
18455         }).createDelegate(this, [i, bullet], true));
18456                 
18457         
18458     },
18459      
18460     setActiveBullet : function(i)
18461     {
18462         if(Roo.isTouch){
18463             return;
18464         }
18465         
18466         Roo.each(this.el.select('.bullet', true).elements, function(el){
18467             el.removeClass('selected');
18468         });
18469
18470         var bullet = this.el.select('.bullet-' + i, true).first();
18471         
18472         if(!bullet){
18473             return;
18474         }
18475         
18476         bullet.addClass('selected');
18477     }
18478     
18479     
18480   
18481 });
18482
18483  
18484
18485  
18486  
18487 Roo.apply(Roo.bootstrap.TabGroup, {
18488     
18489     groups: {},
18490      /**
18491     * register a Navigation Group
18492     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18493     */
18494     register : function(navgrp)
18495     {
18496         this.groups[navgrp.navId] = navgrp;
18497         
18498     },
18499     /**
18500     * fetch a Navigation Group based on the navigation ID
18501     * if one does not exist , it will get created.
18502     * @param {string} the navgroup to add
18503     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18504     */
18505     get: function(navId) {
18506         if (typeof(this.groups[navId]) == 'undefined') {
18507             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18508         }
18509         return this.groups[navId] ;
18510     }
18511     
18512     
18513     
18514 });
18515
18516  /*
18517  * - LGPL
18518  *
18519  * TabPanel
18520  * 
18521  */
18522
18523 /**
18524  * @class Roo.bootstrap.TabPanel
18525  * @extends Roo.bootstrap.Component
18526  * Bootstrap TabPanel class
18527  * @cfg {Boolean} active panel active
18528  * @cfg {String} html panel content
18529  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18530  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18531  * @cfg {String} href click to link..
18532  * 
18533  * 
18534  * @constructor
18535  * Create a new TabPanel
18536  * @param {Object} config The config object
18537  */
18538
18539 Roo.bootstrap.TabPanel = function(config){
18540     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18541     this.addEvents({
18542         /**
18543              * @event changed
18544              * Fires when the active status changes
18545              * @param {Roo.bootstrap.TabPanel} this
18546              * @param {Boolean} state the new state
18547             
18548          */
18549         'changed': true,
18550         /**
18551              * @event beforedeactivate
18552              * Fires before a tab is de-activated - can be used to do validation on a form.
18553              * @param {Roo.bootstrap.TabPanel} this
18554              * @return {Boolean} false if there is an error
18555             
18556          */
18557         'beforedeactivate': true
18558      });
18559     
18560     this.tabId = this.tabId || Roo.id();
18561   
18562 };
18563
18564 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18565     
18566     active: false,
18567     html: false,
18568     tabId: false,
18569     navId : false,
18570     href : '',
18571     
18572     getAutoCreate : function(){
18573         
18574         
18575         var cfg = {
18576             tag: 'div',
18577             // item is needed for carousel - not sure if it has any effect otherwise
18578             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18579             html: this.html || ''
18580         };
18581         
18582         if(this.active){
18583             cfg.cls += ' active';
18584         }
18585         
18586         if(this.tabId){
18587             cfg.tabId = this.tabId;
18588         }
18589         
18590         
18591         
18592         return cfg;
18593     },
18594     
18595     initEvents:  function()
18596     {
18597         var p = this.parent();
18598         
18599         this.navId = this.navId || p.navId;
18600         
18601         if (typeof(this.navId) != 'undefined') {
18602             // not really needed.. but just in case.. parent should be a NavGroup.
18603             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18604             
18605             tg.register(this);
18606             
18607             var i = tg.tabs.length - 1;
18608             
18609             if(this.active && tg.bullets > 0 && i < tg.bullets){
18610                 tg.setActiveBullet(i);
18611             }
18612         }
18613         
18614         this.el.on('click', this.onClick, this);
18615         
18616         if(Roo.isTouch){
18617             this.el.on("touchstart", this.onTouchStart, this);
18618             this.el.on("touchmove", this.onTouchMove, this);
18619             this.el.on("touchend", this.onTouchEnd, this);
18620         }
18621         
18622     },
18623     
18624     onRender : function(ct, position)
18625     {
18626         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18627     },
18628     
18629     setActive : function(state)
18630     {
18631         Roo.log("panel - set active " + this.tabId + "=" + state);
18632         
18633         this.active = state;
18634         if (!state) {
18635             this.el.removeClass('active');
18636             
18637         } else  if (!this.el.hasClass('active')) {
18638             this.el.addClass('active');
18639         }
18640         
18641         this.fireEvent('changed', this, state);
18642     },
18643     
18644     onClick : function(e)
18645     {
18646         e.preventDefault();
18647         
18648         if(!this.href.length){
18649             return;
18650         }
18651         
18652         window.location.href = this.href;
18653     },
18654     
18655     startX : 0,
18656     startY : 0,
18657     endX : 0,
18658     endY : 0,
18659     swiping : false,
18660     
18661     onTouchStart : function(e)
18662     {
18663         this.swiping = false;
18664         
18665         this.startX = e.browserEvent.touches[0].clientX;
18666         this.startY = e.browserEvent.touches[0].clientY;
18667     },
18668     
18669     onTouchMove : function(e)
18670     {
18671         this.swiping = true;
18672         
18673         this.endX = e.browserEvent.touches[0].clientX;
18674         this.endY = e.browserEvent.touches[0].clientY;
18675     },
18676     
18677     onTouchEnd : function(e)
18678     {
18679         if(!this.swiping){
18680             this.onClick(e);
18681             return;
18682         }
18683         
18684         var tabGroup = this.parent();
18685         
18686         if(this.endX > this.startX){ // swiping right
18687             tabGroup.showPanelPrev();
18688             return;
18689         }
18690         
18691         if(this.startX > this.endX){ // swiping left
18692             tabGroup.showPanelNext();
18693             return;
18694         }
18695     }
18696     
18697     
18698 });
18699  
18700
18701  
18702
18703  /*
18704  * - LGPL
18705  *
18706  * DateField
18707  * 
18708  */
18709
18710 /**
18711  * @class Roo.bootstrap.DateField
18712  * @extends Roo.bootstrap.Input
18713  * Bootstrap DateField class
18714  * @cfg {Number} weekStart default 0
18715  * @cfg {String} viewMode default empty, (months|years)
18716  * @cfg {String} minViewMode default empty, (months|years)
18717  * @cfg {Number} startDate default -Infinity
18718  * @cfg {Number} endDate default Infinity
18719  * @cfg {Boolean} todayHighlight default false
18720  * @cfg {Boolean} todayBtn default false
18721  * @cfg {Boolean} calendarWeeks default false
18722  * @cfg {Object} daysOfWeekDisabled default empty
18723  * @cfg {Boolean} singleMode default false (true | false)
18724  * 
18725  * @cfg {Boolean} keyboardNavigation default true
18726  * @cfg {String} language default en
18727  * 
18728  * @constructor
18729  * Create a new DateField
18730  * @param {Object} config The config object
18731  */
18732
18733 Roo.bootstrap.DateField = function(config){
18734     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18735      this.addEvents({
18736             /**
18737              * @event show
18738              * Fires when this field show.
18739              * @param {Roo.bootstrap.DateField} this
18740              * @param {Mixed} date The date value
18741              */
18742             show : true,
18743             /**
18744              * @event show
18745              * Fires when this field hide.
18746              * @param {Roo.bootstrap.DateField} this
18747              * @param {Mixed} date The date value
18748              */
18749             hide : true,
18750             /**
18751              * @event select
18752              * Fires when select a date.
18753              * @param {Roo.bootstrap.DateField} this
18754              * @param {Mixed} date The date value
18755              */
18756             select : true,
18757             /**
18758              * @event beforeselect
18759              * Fires when before select a date.
18760              * @param {Roo.bootstrap.DateField} this
18761              * @param {Mixed} date The date value
18762              */
18763             beforeselect : true
18764         });
18765 };
18766
18767 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18768     
18769     /**
18770      * @cfg {String} format
18771      * The default date format string which can be overriden for localization support.  The format must be
18772      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18773      */
18774     format : "m/d/y",
18775     /**
18776      * @cfg {String} altFormats
18777      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18778      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18779      */
18780     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18781     
18782     weekStart : 0,
18783     
18784     viewMode : '',
18785     
18786     minViewMode : '',
18787     
18788     todayHighlight : false,
18789     
18790     todayBtn: false,
18791     
18792     language: 'en',
18793     
18794     keyboardNavigation: true,
18795     
18796     calendarWeeks: false,
18797     
18798     startDate: -Infinity,
18799     
18800     endDate: Infinity,
18801     
18802     daysOfWeekDisabled: [],
18803     
18804     _events: [],
18805     
18806     singleMode : false,
18807     
18808     UTCDate: function()
18809     {
18810         return new Date(Date.UTC.apply(Date, arguments));
18811     },
18812     
18813     UTCToday: function()
18814     {
18815         var today = new Date();
18816         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18817     },
18818     
18819     getDate: function() {
18820             var d = this.getUTCDate();
18821             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18822     },
18823     
18824     getUTCDate: function() {
18825             return this.date;
18826     },
18827     
18828     setDate: function(d) {
18829             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18830     },
18831     
18832     setUTCDate: function(d) {
18833             this.date = d;
18834             this.setValue(this.formatDate(this.date));
18835     },
18836         
18837     onRender: function(ct, position)
18838     {
18839         
18840         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18841         
18842         this.language = this.language || 'en';
18843         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18844         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18845         
18846         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18847         this.format = this.format || 'm/d/y';
18848         this.isInline = false;
18849         this.isInput = true;
18850         this.component = this.el.select('.add-on', true).first() || false;
18851         this.component = (this.component && this.component.length === 0) ? false : this.component;
18852         this.hasInput = this.component && this.inputEl().length;
18853         
18854         if (typeof(this.minViewMode === 'string')) {
18855             switch (this.minViewMode) {
18856                 case 'months':
18857                     this.minViewMode = 1;
18858                     break;
18859                 case 'years':
18860                     this.minViewMode = 2;
18861                     break;
18862                 default:
18863                     this.minViewMode = 0;
18864                     break;
18865             }
18866         }
18867         
18868         if (typeof(this.viewMode === 'string')) {
18869             switch (this.viewMode) {
18870                 case 'months':
18871                     this.viewMode = 1;
18872                     break;
18873                 case 'years':
18874                     this.viewMode = 2;
18875                     break;
18876                 default:
18877                     this.viewMode = 0;
18878                     break;
18879             }
18880         }
18881                 
18882         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18883         
18884 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18885         
18886         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18887         
18888         this.picker().on('mousedown', this.onMousedown, this);
18889         this.picker().on('click', this.onClick, this);
18890         
18891         this.picker().addClass('datepicker-dropdown');
18892         
18893         this.startViewMode = this.viewMode;
18894         
18895         if(this.singleMode){
18896             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18897                 v.setVisibilityMode(Roo.Element.DISPLAY);
18898                 v.hide();
18899             });
18900             
18901             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18902                 v.setStyle('width', '189px');
18903             });
18904         }
18905         
18906         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18907             if(!this.calendarWeeks){
18908                 v.remove();
18909                 return;
18910             }
18911             
18912             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18913             v.attr('colspan', function(i, val){
18914                 return parseInt(val) + 1;
18915             });
18916         });
18917                         
18918         
18919         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18920         
18921         this.setStartDate(this.startDate);
18922         this.setEndDate(this.endDate);
18923         
18924         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18925         
18926         this.fillDow();
18927         this.fillMonths();
18928         this.update();
18929         this.showMode();
18930         
18931         if(this.isInline) {
18932             this.showPopup();
18933         }
18934     },
18935     
18936     picker : function()
18937     {
18938         return this.pickerEl;
18939 //        return this.el.select('.datepicker', true).first();
18940     },
18941     
18942     fillDow: function()
18943     {
18944         var dowCnt = this.weekStart;
18945         
18946         var dow = {
18947             tag: 'tr',
18948             cn: [
18949                 
18950             ]
18951         };
18952         
18953         if(this.calendarWeeks){
18954             dow.cn.push({
18955                 tag: 'th',
18956                 cls: 'cw',
18957                 html: '&nbsp;'
18958             })
18959         }
18960         
18961         while (dowCnt < this.weekStart + 7) {
18962             dow.cn.push({
18963                 tag: 'th',
18964                 cls: 'dow',
18965                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18966             });
18967         }
18968         
18969         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18970     },
18971     
18972     fillMonths: function()
18973     {    
18974         var i = 0;
18975         var months = this.picker().select('>.datepicker-months td', true).first();
18976         
18977         months.dom.innerHTML = '';
18978         
18979         while (i < 12) {
18980             var month = {
18981                 tag: 'span',
18982                 cls: 'month',
18983                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18984             };
18985             
18986             months.createChild(month);
18987         }
18988         
18989     },
18990     
18991     update: function()
18992     {
18993         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;
18994         
18995         if (this.date < this.startDate) {
18996             this.viewDate = new Date(this.startDate);
18997         } else if (this.date > this.endDate) {
18998             this.viewDate = new Date(this.endDate);
18999         } else {
19000             this.viewDate = new Date(this.date);
19001         }
19002         
19003         this.fill();
19004     },
19005     
19006     fill: function() 
19007     {
19008         var d = new Date(this.viewDate),
19009                 year = d.getUTCFullYear(),
19010                 month = d.getUTCMonth(),
19011                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19012                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19013                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19014                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19015                 currentDate = this.date && this.date.valueOf(),
19016                 today = this.UTCToday();
19017         
19018         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19019         
19020 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19021         
19022 //        this.picker.select('>tfoot th.today').
19023 //                                              .text(dates[this.language].today)
19024 //                                              .toggle(this.todayBtn !== false);
19025     
19026         this.updateNavArrows();
19027         this.fillMonths();
19028                                                 
19029         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19030         
19031         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19032          
19033         prevMonth.setUTCDate(day);
19034         
19035         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19036         
19037         var nextMonth = new Date(prevMonth);
19038         
19039         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19040         
19041         nextMonth = nextMonth.valueOf();
19042         
19043         var fillMonths = false;
19044         
19045         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19046         
19047         while(prevMonth.valueOf() <= nextMonth) {
19048             var clsName = '';
19049             
19050             if (prevMonth.getUTCDay() === this.weekStart) {
19051                 if(fillMonths){
19052                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19053                 }
19054                     
19055                 fillMonths = {
19056                     tag: 'tr',
19057                     cn: []
19058                 };
19059                 
19060                 if(this.calendarWeeks){
19061                     // ISO 8601: First week contains first thursday.
19062                     // ISO also states week starts on Monday, but we can be more abstract here.
19063                     var
19064                     // Start of current week: based on weekstart/current date
19065                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19066                     // Thursday of this week
19067                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19068                     // First Thursday of year, year from thursday
19069                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19070                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19071                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19072                     
19073                     fillMonths.cn.push({
19074                         tag: 'td',
19075                         cls: 'cw',
19076                         html: calWeek
19077                     });
19078                 }
19079             }
19080             
19081             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19082                 clsName += ' old';
19083             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19084                 clsName += ' new';
19085             }
19086             if (this.todayHighlight &&
19087                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19088                 prevMonth.getUTCMonth() == today.getMonth() &&
19089                 prevMonth.getUTCDate() == today.getDate()) {
19090                 clsName += ' today';
19091             }
19092             
19093             if (currentDate && prevMonth.valueOf() === currentDate) {
19094                 clsName += ' active';
19095             }
19096             
19097             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19098                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19099                     clsName += ' disabled';
19100             }
19101             
19102             fillMonths.cn.push({
19103                 tag: 'td',
19104                 cls: 'day ' + clsName,
19105                 html: prevMonth.getDate()
19106             });
19107             
19108             prevMonth.setDate(prevMonth.getDate()+1);
19109         }
19110           
19111         var currentYear = this.date && this.date.getUTCFullYear();
19112         var currentMonth = this.date && this.date.getUTCMonth();
19113         
19114         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19115         
19116         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19117             v.removeClass('active');
19118             
19119             if(currentYear === year && k === currentMonth){
19120                 v.addClass('active');
19121             }
19122             
19123             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19124                 v.addClass('disabled');
19125             }
19126             
19127         });
19128         
19129         
19130         year = parseInt(year/10, 10) * 10;
19131         
19132         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19133         
19134         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19135         
19136         year -= 1;
19137         for (var i = -1; i < 11; i++) {
19138             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19139                 tag: 'span',
19140                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19141                 html: year
19142             });
19143             
19144             year += 1;
19145         }
19146     },
19147     
19148     showMode: function(dir) 
19149     {
19150         if (dir) {
19151             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19152         }
19153         
19154         Roo.each(this.picker().select('>div',true).elements, function(v){
19155             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19156             v.hide();
19157         });
19158         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19159     },
19160     
19161     place: function()
19162     {
19163         if(this.isInline) {
19164             return;
19165         }
19166         
19167         this.picker().removeClass(['bottom', 'top']);
19168         
19169         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19170             /*
19171              * place to the top of element!
19172              *
19173              */
19174             
19175             this.picker().addClass('top');
19176             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19177             
19178             return;
19179         }
19180         
19181         this.picker().addClass('bottom');
19182         
19183         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19184     },
19185     
19186     parseDate : function(value)
19187     {
19188         if(!value || value instanceof Date){
19189             return value;
19190         }
19191         var v = Date.parseDate(value, this.format);
19192         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19193             v = Date.parseDate(value, 'Y-m-d');
19194         }
19195         if(!v && this.altFormats){
19196             if(!this.altFormatsArray){
19197                 this.altFormatsArray = this.altFormats.split("|");
19198             }
19199             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19200                 v = Date.parseDate(value, this.altFormatsArray[i]);
19201             }
19202         }
19203         return v;
19204     },
19205     
19206     formatDate : function(date, fmt)
19207     {   
19208         return (!date || !(date instanceof Date)) ?
19209         date : date.dateFormat(fmt || this.format);
19210     },
19211     
19212     onFocus : function()
19213     {
19214         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19215         this.showPopup();
19216     },
19217     
19218     onBlur : function()
19219     {
19220         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19221         
19222         var d = this.inputEl().getValue();
19223         
19224         this.setValue(d);
19225                 
19226         this.hidePopup();
19227     },
19228     
19229     showPopup : function()
19230     {
19231         this.picker().show();
19232         this.update();
19233         this.place();
19234         
19235         this.fireEvent('showpopup', this, this.date);
19236     },
19237     
19238     hidePopup : function()
19239     {
19240         if(this.isInline) {
19241             return;
19242         }
19243         this.picker().hide();
19244         this.viewMode = this.startViewMode;
19245         this.showMode();
19246         
19247         this.fireEvent('hidepopup', this, this.date);
19248         
19249     },
19250     
19251     onMousedown: function(e)
19252     {
19253         e.stopPropagation();
19254         e.preventDefault();
19255     },
19256     
19257     keyup: function(e)
19258     {
19259         Roo.bootstrap.DateField.superclass.keyup.call(this);
19260         this.update();
19261     },
19262
19263     setValue: function(v)
19264     {
19265         if(this.fireEvent('beforeselect', this, v) !== false){
19266             var d = new Date(this.parseDate(v) ).clearTime();
19267         
19268             if(isNaN(d.getTime())){
19269                 this.date = this.viewDate = '';
19270                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19271                 return;
19272             }
19273
19274             v = this.formatDate(d);
19275
19276             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19277
19278             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19279
19280             this.update();
19281
19282             this.fireEvent('select', this, this.date);
19283         }
19284     },
19285     
19286     getValue: function()
19287     {
19288         return this.formatDate(this.date);
19289     },
19290     
19291     fireKey: function(e)
19292     {
19293         if (!this.picker().isVisible()){
19294             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19295                 this.showPopup();
19296             }
19297             return;
19298         }
19299         
19300         var dateChanged = false,
19301         dir, day, month,
19302         newDate, newViewDate;
19303         
19304         switch(e.keyCode){
19305             case 27: // escape
19306                 this.hidePopup();
19307                 e.preventDefault();
19308                 break;
19309             case 37: // left
19310             case 39: // right
19311                 if (!this.keyboardNavigation) {
19312                     break;
19313                 }
19314                 dir = e.keyCode == 37 ? -1 : 1;
19315                 
19316                 if (e.ctrlKey){
19317                     newDate = this.moveYear(this.date, dir);
19318                     newViewDate = this.moveYear(this.viewDate, dir);
19319                 } else if (e.shiftKey){
19320                     newDate = this.moveMonth(this.date, dir);
19321                     newViewDate = this.moveMonth(this.viewDate, dir);
19322                 } else {
19323                     newDate = new Date(this.date);
19324                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19325                     newViewDate = new Date(this.viewDate);
19326                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19327                 }
19328                 if (this.dateWithinRange(newDate)){
19329                     this.date = newDate;
19330                     this.viewDate = newViewDate;
19331                     this.setValue(this.formatDate(this.date));
19332 //                    this.update();
19333                     e.preventDefault();
19334                     dateChanged = true;
19335                 }
19336                 break;
19337             case 38: // up
19338             case 40: // down
19339                 if (!this.keyboardNavigation) {
19340                     break;
19341                 }
19342                 dir = e.keyCode == 38 ? -1 : 1;
19343                 if (e.ctrlKey){
19344                     newDate = this.moveYear(this.date, dir);
19345                     newViewDate = this.moveYear(this.viewDate, dir);
19346                 } else if (e.shiftKey){
19347                     newDate = this.moveMonth(this.date, dir);
19348                     newViewDate = this.moveMonth(this.viewDate, dir);
19349                 } else {
19350                     newDate = new Date(this.date);
19351                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19352                     newViewDate = new Date(this.viewDate);
19353                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19354                 }
19355                 if (this.dateWithinRange(newDate)){
19356                     this.date = newDate;
19357                     this.viewDate = newViewDate;
19358                     this.setValue(this.formatDate(this.date));
19359 //                    this.update();
19360                     e.preventDefault();
19361                     dateChanged = true;
19362                 }
19363                 break;
19364             case 13: // enter
19365                 this.setValue(this.formatDate(this.date));
19366                 this.hidePopup();
19367                 e.preventDefault();
19368                 break;
19369             case 9: // tab
19370                 this.setValue(this.formatDate(this.date));
19371                 this.hidePopup();
19372                 break;
19373             case 16: // shift
19374             case 17: // ctrl
19375             case 18: // alt
19376                 break;
19377             default :
19378                 this.hidePopup();
19379                 
19380         }
19381     },
19382     
19383     
19384     onClick: function(e) 
19385     {
19386         e.stopPropagation();
19387         e.preventDefault();
19388         
19389         var target = e.getTarget();
19390         
19391         if(target.nodeName.toLowerCase() === 'i'){
19392             target = Roo.get(target).dom.parentNode;
19393         }
19394         
19395         var nodeName = target.nodeName;
19396         var className = target.className;
19397         var html = target.innerHTML;
19398         //Roo.log(nodeName);
19399         
19400         switch(nodeName.toLowerCase()) {
19401             case 'th':
19402                 switch(className) {
19403                     case 'switch':
19404                         this.showMode(1);
19405                         break;
19406                     case 'prev':
19407                     case 'next':
19408                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19409                         switch(this.viewMode){
19410                                 case 0:
19411                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19412                                         break;
19413                                 case 1:
19414                                 case 2:
19415                                         this.viewDate = this.moveYear(this.viewDate, dir);
19416                                         break;
19417                         }
19418                         this.fill();
19419                         break;
19420                     case 'today':
19421                         var date = new Date();
19422                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19423 //                        this.fill()
19424                         this.setValue(this.formatDate(this.date));
19425                         
19426                         this.hidePopup();
19427                         break;
19428                 }
19429                 break;
19430             case 'span':
19431                 if (className.indexOf('disabled') < 0) {
19432                     this.viewDate.setUTCDate(1);
19433                     if (className.indexOf('month') > -1) {
19434                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19435                     } else {
19436                         var year = parseInt(html, 10) || 0;
19437                         this.viewDate.setUTCFullYear(year);
19438                         
19439                     }
19440                     
19441                     if(this.singleMode){
19442                         this.setValue(this.formatDate(this.viewDate));
19443                         this.hidePopup();
19444                         return;
19445                     }
19446                     
19447                     this.showMode(-1);
19448                     this.fill();
19449                 }
19450                 break;
19451                 
19452             case 'td':
19453                 //Roo.log(className);
19454                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19455                     var day = parseInt(html, 10) || 1;
19456                     var year = this.viewDate.getUTCFullYear(),
19457                         month = this.viewDate.getUTCMonth();
19458
19459                     if (className.indexOf('old') > -1) {
19460                         if(month === 0 ){
19461                             month = 11;
19462                             year -= 1;
19463                         }else{
19464                             month -= 1;
19465                         }
19466                     } else if (className.indexOf('new') > -1) {
19467                         if (month == 11) {
19468                             month = 0;
19469                             year += 1;
19470                         } else {
19471                             month += 1;
19472                         }
19473                     }
19474                     //Roo.log([year,month,day]);
19475                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19476                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19477 //                    this.fill();
19478                     //Roo.log(this.formatDate(this.date));
19479                     this.setValue(this.formatDate(this.date));
19480                     this.hidePopup();
19481                 }
19482                 break;
19483         }
19484     },
19485     
19486     setStartDate: function(startDate)
19487     {
19488         this.startDate = startDate || -Infinity;
19489         if (this.startDate !== -Infinity) {
19490             this.startDate = this.parseDate(this.startDate);
19491         }
19492         this.update();
19493         this.updateNavArrows();
19494     },
19495
19496     setEndDate: function(endDate)
19497     {
19498         this.endDate = endDate || Infinity;
19499         if (this.endDate !== Infinity) {
19500             this.endDate = this.parseDate(this.endDate);
19501         }
19502         this.update();
19503         this.updateNavArrows();
19504     },
19505     
19506     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19507     {
19508         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19509         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19510             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19511         }
19512         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19513             return parseInt(d, 10);
19514         });
19515         this.update();
19516         this.updateNavArrows();
19517     },
19518     
19519     updateNavArrows: function() 
19520     {
19521         if(this.singleMode){
19522             return;
19523         }
19524         
19525         var d = new Date(this.viewDate),
19526         year = d.getUTCFullYear(),
19527         month = d.getUTCMonth();
19528         
19529         Roo.each(this.picker().select('.prev', true).elements, function(v){
19530             v.show();
19531             switch (this.viewMode) {
19532                 case 0:
19533
19534                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19535                         v.hide();
19536                     }
19537                     break;
19538                 case 1:
19539                 case 2:
19540                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19541                         v.hide();
19542                     }
19543                     break;
19544             }
19545         });
19546         
19547         Roo.each(this.picker().select('.next', true).elements, function(v){
19548             v.show();
19549             switch (this.viewMode) {
19550                 case 0:
19551
19552                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19553                         v.hide();
19554                     }
19555                     break;
19556                 case 1:
19557                 case 2:
19558                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19559                         v.hide();
19560                     }
19561                     break;
19562             }
19563         })
19564     },
19565     
19566     moveMonth: function(date, dir)
19567     {
19568         if (!dir) {
19569             return date;
19570         }
19571         var new_date = new Date(date.valueOf()),
19572         day = new_date.getUTCDate(),
19573         month = new_date.getUTCMonth(),
19574         mag = Math.abs(dir),
19575         new_month, test;
19576         dir = dir > 0 ? 1 : -1;
19577         if (mag == 1){
19578             test = dir == -1
19579             // If going back one month, make sure month is not current month
19580             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19581             ? function(){
19582                 return new_date.getUTCMonth() == month;
19583             }
19584             // If going forward one month, make sure month is as expected
19585             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19586             : function(){
19587                 return new_date.getUTCMonth() != new_month;
19588             };
19589             new_month = month + dir;
19590             new_date.setUTCMonth(new_month);
19591             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19592             if (new_month < 0 || new_month > 11) {
19593                 new_month = (new_month + 12) % 12;
19594             }
19595         } else {
19596             // For magnitudes >1, move one month at a time...
19597             for (var i=0; i<mag; i++) {
19598                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19599                 new_date = this.moveMonth(new_date, dir);
19600             }
19601             // ...then reset the day, keeping it in the new month
19602             new_month = new_date.getUTCMonth();
19603             new_date.setUTCDate(day);
19604             test = function(){
19605                 return new_month != new_date.getUTCMonth();
19606             };
19607         }
19608         // Common date-resetting loop -- if date is beyond end of month, make it
19609         // end of month
19610         while (test()){
19611             new_date.setUTCDate(--day);
19612             new_date.setUTCMonth(new_month);
19613         }
19614         return new_date;
19615     },
19616
19617     moveYear: function(date, dir)
19618     {
19619         return this.moveMonth(date, dir*12);
19620     },
19621
19622     dateWithinRange: function(date)
19623     {
19624         return date >= this.startDate && date <= this.endDate;
19625     },
19626
19627     
19628     remove: function() 
19629     {
19630         this.picker().remove();
19631     },
19632     
19633     validateValue : function(value)
19634     {
19635         if(this.getVisibilityEl().hasClass('hidden')){
19636             return true;
19637         }
19638         
19639         if(value.length < 1)  {
19640             if(this.allowBlank){
19641                 return true;
19642             }
19643             return false;
19644         }
19645         
19646         if(value.length < this.minLength){
19647             return false;
19648         }
19649         if(value.length > this.maxLength){
19650             return false;
19651         }
19652         if(this.vtype){
19653             var vt = Roo.form.VTypes;
19654             if(!vt[this.vtype](value, this)){
19655                 return false;
19656             }
19657         }
19658         if(typeof this.validator == "function"){
19659             var msg = this.validator(value);
19660             if(msg !== true){
19661                 return false;
19662             }
19663         }
19664         
19665         if(this.regex && !this.regex.test(value)){
19666             return false;
19667         }
19668         
19669         if(typeof(this.parseDate(value)) == 'undefined'){
19670             return false;
19671         }
19672         
19673         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19674             return false;
19675         }      
19676         
19677         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19678             return false;
19679         } 
19680         
19681         
19682         return true;
19683     },
19684     
19685     reset : function()
19686     {
19687         this.date = this.viewDate = '';
19688         
19689         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19690     }
19691    
19692 });
19693
19694 Roo.apply(Roo.bootstrap.DateField,  {
19695     
19696     head : {
19697         tag: 'thead',
19698         cn: [
19699         {
19700             tag: 'tr',
19701             cn: [
19702             {
19703                 tag: 'th',
19704                 cls: 'prev',
19705                 html: '<i class="fa fa-arrow-left"/>'
19706             },
19707             {
19708                 tag: 'th',
19709                 cls: 'switch',
19710                 colspan: '5'
19711             },
19712             {
19713                 tag: 'th',
19714                 cls: 'next',
19715                 html: '<i class="fa fa-arrow-right"/>'
19716             }
19717
19718             ]
19719         }
19720         ]
19721     },
19722     
19723     content : {
19724         tag: 'tbody',
19725         cn: [
19726         {
19727             tag: 'tr',
19728             cn: [
19729             {
19730                 tag: 'td',
19731                 colspan: '7'
19732             }
19733             ]
19734         }
19735         ]
19736     },
19737     
19738     footer : {
19739         tag: 'tfoot',
19740         cn: [
19741         {
19742             tag: 'tr',
19743             cn: [
19744             {
19745                 tag: 'th',
19746                 colspan: '7',
19747                 cls: 'today'
19748             }
19749                     
19750             ]
19751         }
19752         ]
19753     },
19754     
19755     dates:{
19756         en: {
19757             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19758             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19759             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19760             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19761             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19762             today: "Today"
19763         }
19764     },
19765     
19766     modes: [
19767     {
19768         clsName: 'days',
19769         navFnc: 'Month',
19770         navStep: 1
19771     },
19772     {
19773         clsName: 'months',
19774         navFnc: 'FullYear',
19775         navStep: 1
19776     },
19777     {
19778         clsName: 'years',
19779         navFnc: 'FullYear',
19780         navStep: 10
19781     }]
19782 });
19783
19784 Roo.apply(Roo.bootstrap.DateField,  {
19785   
19786     template : {
19787         tag: 'div',
19788         cls: 'datepicker dropdown-menu roo-dynamic',
19789         cn: [
19790         {
19791             tag: 'div',
19792             cls: 'datepicker-days',
19793             cn: [
19794             {
19795                 tag: 'table',
19796                 cls: 'table-condensed',
19797                 cn:[
19798                 Roo.bootstrap.DateField.head,
19799                 {
19800                     tag: 'tbody'
19801                 },
19802                 Roo.bootstrap.DateField.footer
19803                 ]
19804             }
19805             ]
19806         },
19807         {
19808             tag: 'div',
19809             cls: 'datepicker-months',
19810             cn: [
19811             {
19812                 tag: 'table',
19813                 cls: 'table-condensed',
19814                 cn:[
19815                 Roo.bootstrap.DateField.head,
19816                 Roo.bootstrap.DateField.content,
19817                 Roo.bootstrap.DateField.footer
19818                 ]
19819             }
19820             ]
19821         },
19822         {
19823             tag: 'div',
19824             cls: 'datepicker-years',
19825             cn: [
19826             {
19827                 tag: 'table',
19828                 cls: 'table-condensed',
19829                 cn:[
19830                 Roo.bootstrap.DateField.head,
19831                 Roo.bootstrap.DateField.content,
19832                 Roo.bootstrap.DateField.footer
19833                 ]
19834             }
19835             ]
19836         }
19837         ]
19838     }
19839 });
19840
19841  
19842
19843  /*
19844  * - LGPL
19845  *
19846  * TimeField
19847  * 
19848  */
19849
19850 /**
19851  * @class Roo.bootstrap.TimeField
19852  * @extends Roo.bootstrap.Input
19853  * Bootstrap DateField class
19854  * 
19855  * 
19856  * @constructor
19857  * Create a new TimeField
19858  * @param {Object} config The config object
19859  */
19860
19861 Roo.bootstrap.TimeField = function(config){
19862     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19863     this.addEvents({
19864             /**
19865              * @event show
19866              * Fires when this field show.
19867              * @param {Roo.bootstrap.DateField} thisthis
19868              * @param {Mixed} date The date value
19869              */
19870             show : true,
19871             /**
19872              * @event show
19873              * Fires when this field hide.
19874              * @param {Roo.bootstrap.DateField} this
19875              * @param {Mixed} date The date value
19876              */
19877             hide : true,
19878             /**
19879              * @event select
19880              * Fires when select a date.
19881              * @param {Roo.bootstrap.DateField} this
19882              * @param {Mixed} date The date value
19883              */
19884             select : true
19885         });
19886 };
19887
19888 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19889     
19890     /**
19891      * @cfg {String} format
19892      * The default time format string which can be overriden for localization support.  The format must be
19893      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19894      */
19895     format : "H:i",
19896        
19897     onRender: function(ct, position)
19898     {
19899         
19900         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19901                 
19902         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19903         
19904         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19905         
19906         this.pop = this.picker().select('>.datepicker-time',true).first();
19907         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19908         
19909         this.picker().on('mousedown', this.onMousedown, this);
19910         this.picker().on('click', this.onClick, this);
19911         
19912         this.picker().addClass('datepicker-dropdown');
19913     
19914         this.fillTime();
19915         this.update();
19916             
19917         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19918         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19919         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19920         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19921         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19922         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19923
19924     },
19925     
19926     fireKey: function(e){
19927         if (!this.picker().isVisible()){
19928             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19929                 this.show();
19930             }
19931             return;
19932         }
19933
19934         e.preventDefault();
19935         
19936         switch(e.keyCode){
19937             case 27: // escape
19938                 this.hide();
19939                 break;
19940             case 37: // left
19941             case 39: // right
19942                 this.onTogglePeriod();
19943                 break;
19944             case 38: // up
19945                 this.onIncrementMinutes();
19946                 break;
19947             case 40: // down
19948                 this.onDecrementMinutes();
19949                 break;
19950             case 13: // enter
19951             case 9: // tab
19952                 this.setTime();
19953                 break;
19954         }
19955     },
19956     
19957     onClick: function(e) {
19958         e.stopPropagation();
19959         e.preventDefault();
19960     },
19961     
19962     picker : function()
19963     {
19964         return this.el.select('.datepicker', true).first();
19965     },
19966     
19967     fillTime: function()
19968     {    
19969         var time = this.pop.select('tbody', true).first();
19970         
19971         time.dom.innerHTML = '';
19972         
19973         time.createChild({
19974             tag: 'tr',
19975             cn: [
19976                 {
19977                     tag: 'td',
19978                     cn: [
19979                         {
19980                             tag: 'a',
19981                             href: '#',
19982                             cls: 'btn',
19983                             cn: [
19984                                 {
19985                                     tag: 'span',
19986                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19987                                 }
19988                             ]
19989                         } 
19990                     ]
19991                 },
19992                 {
19993                     tag: 'td',
19994                     cls: 'separator'
19995                 },
19996                 {
19997                     tag: 'td',
19998                     cn: [
19999                         {
20000                             tag: 'a',
20001                             href: '#',
20002                             cls: 'btn',
20003                             cn: [
20004                                 {
20005                                     tag: 'span',
20006                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
20007                                 }
20008                             ]
20009                         }
20010                     ]
20011                 },
20012                 {
20013                     tag: 'td',
20014                     cls: 'separator'
20015                 }
20016             ]
20017         });
20018         
20019         time.createChild({
20020             tag: 'tr',
20021             cn: [
20022                 {
20023                     tag: 'td',
20024                     cn: [
20025                         {
20026                             tag: 'span',
20027                             cls: 'timepicker-hour',
20028                             html: '00'
20029                         }  
20030                     ]
20031                 },
20032                 {
20033                     tag: 'td',
20034                     cls: 'separator',
20035                     html: ':'
20036                 },
20037                 {
20038                     tag: 'td',
20039                     cn: [
20040                         {
20041                             tag: 'span',
20042                             cls: 'timepicker-minute',
20043                             html: '00'
20044                         }  
20045                     ]
20046                 },
20047                 {
20048                     tag: 'td',
20049                     cls: 'separator'
20050                 },
20051                 {
20052                     tag: 'td',
20053                     cn: [
20054                         {
20055                             tag: 'button',
20056                             type: 'button',
20057                             cls: 'btn btn-primary period',
20058                             html: 'AM'
20059                             
20060                         }
20061                     ]
20062                 }
20063             ]
20064         });
20065         
20066         time.createChild({
20067             tag: 'tr',
20068             cn: [
20069                 {
20070                     tag: 'td',
20071                     cn: [
20072                         {
20073                             tag: 'a',
20074                             href: '#',
20075                             cls: 'btn',
20076                             cn: [
20077                                 {
20078                                     tag: 'span',
20079                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20080                                 }
20081                             ]
20082                         }
20083                     ]
20084                 },
20085                 {
20086                     tag: 'td',
20087                     cls: 'separator'
20088                 },
20089                 {
20090                     tag: 'td',
20091                     cn: [
20092                         {
20093                             tag: 'a',
20094                             href: '#',
20095                             cls: 'btn',
20096                             cn: [
20097                                 {
20098                                     tag: 'span',
20099                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20100                                 }
20101                             ]
20102                         }
20103                     ]
20104                 },
20105                 {
20106                     tag: 'td',
20107                     cls: 'separator'
20108                 }
20109             ]
20110         });
20111         
20112     },
20113     
20114     update: function()
20115     {
20116         
20117         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20118         
20119         this.fill();
20120     },
20121     
20122     fill: function() 
20123     {
20124         var hours = this.time.getHours();
20125         var minutes = this.time.getMinutes();
20126         var period = 'AM';
20127         
20128         if(hours > 11){
20129             period = 'PM';
20130         }
20131         
20132         if(hours == 0){
20133             hours = 12;
20134         }
20135         
20136         
20137         if(hours > 12){
20138             hours = hours - 12;
20139         }
20140         
20141         if(hours < 10){
20142             hours = '0' + hours;
20143         }
20144         
20145         if(minutes < 10){
20146             minutes = '0' + minutes;
20147         }
20148         
20149         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20150         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20151         this.pop.select('button', true).first().dom.innerHTML = period;
20152         
20153     },
20154     
20155     place: function()
20156     {   
20157         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20158         
20159         var cls = ['bottom'];
20160         
20161         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20162             cls.pop();
20163             cls.push('top');
20164         }
20165         
20166         cls.push('right');
20167         
20168         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20169             cls.pop();
20170             cls.push('left');
20171         }
20172         
20173         this.picker().addClass(cls.join('-'));
20174         
20175         var _this = this;
20176         
20177         Roo.each(cls, function(c){
20178             if(c == 'bottom'){
20179                 _this.picker().setTop(_this.inputEl().getHeight());
20180                 return;
20181             }
20182             if(c == 'top'){
20183                 _this.picker().setTop(0 - _this.picker().getHeight());
20184                 return;
20185             }
20186             
20187             if(c == 'left'){
20188                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20189                 return;
20190             }
20191             if(c == 'right'){
20192                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20193                 return;
20194             }
20195         });
20196         
20197     },
20198   
20199     onFocus : function()
20200     {
20201         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20202         this.show();
20203     },
20204     
20205     onBlur : function()
20206     {
20207         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20208         this.hide();
20209     },
20210     
20211     show : function()
20212     {
20213         this.picker().show();
20214         this.pop.show();
20215         this.update();
20216         this.place();
20217         
20218         this.fireEvent('show', this, this.date);
20219     },
20220     
20221     hide : function()
20222     {
20223         this.picker().hide();
20224         this.pop.hide();
20225         
20226         this.fireEvent('hide', this, this.date);
20227     },
20228     
20229     setTime : function()
20230     {
20231         this.hide();
20232         this.setValue(this.time.format(this.format));
20233         
20234         this.fireEvent('select', this, this.date);
20235         
20236         
20237     },
20238     
20239     onMousedown: function(e){
20240         e.stopPropagation();
20241         e.preventDefault();
20242     },
20243     
20244     onIncrementHours: function()
20245     {
20246         Roo.log('onIncrementHours');
20247         this.time = this.time.add(Date.HOUR, 1);
20248         this.update();
20249         
20250     },
20251     
20252     onDecrementHours: function()
20253     {
20254         Roo.log('onDecrementHours');
20255         this.time = this.time.add(Date.HOUR, -1);
20256         this.update();
20257     },
20258     
20259     onIncrementMinutes: function()
20260     {
20261         Roo.log('onIncrementMinutes');
20262         this.time = this.time.add(Date.MINUTE, 1);
20263         this.update();
20264     },
20265     
20266     onDecrementMinutes: function()
20267     {
20268         Roo.log('onDecrementMinutes');
20269         this.time = this.time.add(Date.MINUTE, -1);
20270         this.update();
20271     },
20272     
20273     onTogglePeriod: function()
20274     {
20275         Roo.log('onTogglePeriod');
20276         this.time = this.time.add(Date.HOUR, 12);
20277         this.update();
20278     }
20279     
20280    
20281 });
20282
20283 Roo.apply(Roo.bootstrap.TimeField,  {
20284     
20285     content : {
20286         tag: 'tbody',
20287         cn: [
20288             {
20289                 tag: 'tr',
20290                 cn: [
20291                 {
20292                     tag: 'td',
20293                     colspan: '7'
20294                 }
20295                 ]
20296             }
20297         ]
20298     },
20299     
20300     footer : {
20301         tag: 'tfoot',
20302         cn: [
20303             {
20304                 tag: 'tr',
20305                 cn: [
20306                 {
20307                     tag: 'th',
20308                     colspan: '7',
20309                     cls: '',
20310                     cn: [
20311                         {
20312                             tag: 'button',
20313                             cls: 'btn btn-info ok',
20314                             html: 'OK'
20315                         }
20316                     ]
20317                 }
20318
20319                 ]
20320             }
20321         ]
20322     }
20323 });
20324
20325 Roo.apply(Roo.bootstrap.TimeField,  {
20326   
20327     template : {
20328         tag: 'div',
20329         cls: 'datepicker dropdown-menu',
20330         cn: [
20331             {
20332                 tag: 'div',
20333                 cls: 'datepicker-time',
20334                 cn: [
20335                 {
20336                     tag: 'table',
20337                     cls: 'table-condensed',
20338                     cn:[
20339                     Roo.bootstrap.TimeField.content,
20340                     Roo.bootstrap.TimeField.footer
20341                     ]
20342                 }
20343                 ]
20344             }
20345         ]
20346     }
20347 });
20348
20349  
20350
20351  /*
20352  * - LGPL
20353  *
20354  * MonthField
20355  * 
20356  */
20357
20358 /**
20359  * @class Roo.bootstrap.MonthField
20360  * @extends Roo.bootstrap.Input
20361  * Bootstrap MonthField class
20362  * 
20363  * @cfg {String} language default en
20364  * 
20365  * @constructor
20366  * Create a new MonthField
20367  * @param {Object} config The config object
20368  */
20369
20370 Roo.bootstrap.MonthField = function(config){
20371     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20372     
20373     this.addEvents({
20374         /**
20375          * @event show
20376          * Fires when this field show.
20377          * @param {Roo.bootstrap.MonthField} this
20378          * @param {Mixed} date The date value
20379          */
20380         show : true,
20381         /**
20382          * @event show
20383          * Fires when this field hide.
20384          * @param {Roo.bootstrap.MonthField} this
20385          * @param {Mixed} date The date value
20386          */
20387         hide : true,
20388         /**
20389          * @event select
20390          * Fires when select a date.
20391          * @param {Roo.bootstrap.MonthField} this
20392          * @param {String} oldvalue The old value
20393          * @param {String} newvalue The new value
20394          */
20395         select : true
20396     });
20397 };
20398
20399 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20400     
20401     onRender: function(ct, position)
20402     {
20403         
20404         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20405         
20406         this.language = this.language || 'en';
20407         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20408         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20409         
20410         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20411         this.isInline = false;
20412         this.isInput = true;
20413         this.component = this.el.select('.add-on', true).first() || false;
20414         this.component = (this.component && this.component.length === 0) ? false : this.component;
20415         this.hasInput = this.component && this.inputEL().length;
20416         
20417         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20418         
20419         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20420         
20421         this.picker().on('mousedown', this.onMousedown, this);
20422         this.picker().on('click', this.onClick, this);
20423         
20424         this.picker().addClass('datepicker-dropdown');
20425         
20426         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20427             v.setStyle('width', '189px');
20428         });
20429         
20430         this.fillMonths();
20431         
20432         this.update();
20433         
20434         if(this.isInline) {
20435             this.show();
20436         }
20437         
20438     },
20439     
20440     setValue: function(v, suppressEvent)
20441     {   
20442         var o = this.getValue();
20443         
20444         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20445         
20446         this.update();
20447
20448         if(suppressEvent !== true){
20449             this.fireEvent('select', this, o, v);
20450         }
20451         
20452     },
20453     
20454     getValue: function()
20455     {
20456         return this.value;
20457     },
20458     
20459     onClick: function(e) 
20460     {
20461         e.stopPropagation();
20462         e.preventDefault();
20463         
20464         var target = e.getTarget();
20465         
20466         if(target.nodeName.toLowerCase() === 'i'){
20467             target = Roo.get(target).dom.parentNode;
20468         }
20469         
20470         var nodeName = target.nodeName;
20471         var className = target.className;
20472         var html = target.innerHTML;
20473         
20474         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20475             return;
20476         }
20477         
20478         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20479         
20480         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20481         
20482         this.hide();
20483                         
20484     },
20485     
20486     picker : function()
20487     {
20488         return this.pickerEl;
20489     },
20490     
20491     fillMonths: function()
20492     {    
20493         var i = 0;
20494         var months = this.picker().select('>.datepicker-months td', true).first();
20495         
20496         months.dom.innerHTML = '';
20497         
20498         while (i < 12) {
20499             var month = {
20500                 tag: 'span',
20501                 cls: 'month',
20502                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20503             };
20504             
20505             months.createChild(month);
20506         }
20507         
20508     },
20509     
20510     update: function()
20511     {
20512         var _this = this;
20513         
20514         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20515             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20516         }
20517         
20518         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20519             e.removeClass('active');
20520             
20521             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20522                 e.addClass('active');
20523             }
20524         })
20525     },
20526     
20527     place: function()
20528     {
20529         if(this.isInline) {
20530             return;
20531         }
20532         
20533         this.picker().removeClass(['bottom', 'top']);
20534         
20535         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20536             /*
20537              * place to the top of element!
20538              *
20539              */
20540             
20541             this.picker().addClass('top');
20542             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20543             
20544             return;
20545         }
20546         
20547         this.picker().addClass('bottom');
20548         
20549         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20550     },
20551     
20552     onFocus : function()
20553     {
20554         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20555         this.show();
20556     },
20557     
20558     onBlur : function()
20559     {
20560         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20561         
20562         var d = this.inputEl().getValue();
20563         
20564         this.setValue(d);
20565                 
20566         this.hide();
20567     },
20568     
20569     show : function()
20570     {
20571         this.picker().show();
20572         this.picker().select('>.datepicker-months', true).first().show();
20573         this.update();
20574         this.place();
20575         
20576         this.fireEvent('show', this, this.date);
20577     },
20578     
20579     hide : function()
20580     {
20581         if(this.isInline) {
20582             return;
20583         }
20584         this.picker().hide();
20585         this.fireEvent('hide', this, this.date);
20586         
20587     },
20588     
20589     onMousedown: function(e)
20590     {
20591         e.stopPropagation();
20592         e.preventDefault();
20593     },
20594     
20595     keyup: function(e)
20596     {
20597         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20598         this.update();
20599     },
20600
20601     fireKey: function(e)
20602     {
20603         if (!this.picker().isVisible()){
20604             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20605                 this.show();
20606             }
20607             return;
20608         }
20609         
20610         var dir;
20611         
20612         switch(e.keyCode){
20613             case 27: // escape
20614                 this.hide();
20615                 e.preventDefault();
20616                 break;
20617             case 37: // left
20618             case 39: // right
20619                 dir = e.keyCode == 37 ? -1 : 1;
20620                 
20621                 this.vIndex = this.vIndex + dir;
20622                 
20623                 if(this.vIndex < 0){
20624                     this.vIndex = 0;
20625                 }
20626                 
20627                 if(this.vIndex > 11){
20628                     this.vIndex = 11;
20629                 }
20630                 
20631                 if(isNaN(this.vIndex)){
20632                     this.vIndex = 0;
20633                 }
20634                 
20635                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20636                 
20637                 break;
20638             case 38: // up
20639             case 40: // down
20640                 
20641                 dir = e.keyCode == 38 ? -1 : 1;
20642                 
20643                 this.vIndex = this.vIndex + dir * 4;
20644                 
20645                 if(this.vIndex < 0){
20646                     this.vIndex = 0;
20647                 }
20648                 
20649                 if(this.vIndex > 11){
20650                     this.vIndex = 11;
20651                 }
20652                 
20653                 if(isNaN(this.vIndex)){
20654                     this.vIndex = 0;
20655                 }
20656                 
20657                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20658                 break;
20659                 
20660             case 13: // enter
20661                 
20662                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20663                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20664                 }
20665                 
20666                 this.hide();
20667                 e.preventDefault();
20668                 break;
20669             case 9: // tab
20670                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20671                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20672                 }
20673                 this.hide();
20674                 break;
20675             case 16: // shift
20676             case 17: // ctrl
20677             case 18: // alt
20678                 break;
20679             default :
20680                 this.hide();
20681                 
20682         }
20683     },
20684     
20685     remove: function() 
20686     {
20687         this.picker().remove();
20688     }
20689    
20690 });
20691
20692 Roo.apply(Roo.bootstrap.MonthField,  {
20693     
20694     content : {
20695         tag: 'tbody',
20696         cn: [
20697         {
20698             tag: 'tr',
20699             cn: [
20700             {
20701                 tag: 'td',
20702                 colspan: '7'
20703             }
20704             ]
20705         }
20706         ]
20707     },
20708     
20709     dates:{
20710         en: {
20711             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20712             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20713         }
20714     }
20715 });
20716
20717 Roo.apply(Roo.bootstrap.MonthField,  {
20718   
20719     template : {
20720         tag: 'div',
20721         cls: 'datepicker dropdown-menu roo-dynamic',
20722         cn: [
20723             {
20724                 tag: 'div',
20725                 cls: 'datepicker-months',
20726                 cn: [
20727                 {
20728                     tag: 'table',
20729                     cls: 'table-condensed',
20730                     cn:[
20731                         Roo.bootstrap.DateField.content
20732                     ]
20733                 }
20734                 ]
20735             }
20736         ]
20737     }
20738 });
20739
20740  
20741
20742  
20743  /*
20744  * - LGPL
20745  *
20746  * CheckBox
20747  * 
20748  */
20749
20750 /**
20751  * @class Roo.bootstrap.CheckBox
20752  * @extends Roo.bootstrap.Input
20753  * Bootstrap CheckBox class
20754  * 
20755  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20756  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20757  * @cfg {String} boxLabel The text that appears beside the checkbox
20758  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20759  * @cfg {Boolean} checked initnal the element
20760  * @cfg {Boolean} inline inline the element (default false)
20761  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20762  * @cfg {String} tooltip label tooltip
20763  * 
20764  * @constructor
20765  * Create a new CheckBox
20766  * @param {Object} config The config object
20767  */
20768
20769 Roo.bootstrap.CheckBox = function(config){
20770     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20771    
20772     this.addEvents({
20773         /**
20774         * @event check
20775         * Fires when the element is checked or unchecked.
20776         * @param {Roo.bootstrap.CheckBox} this This input
20777         * @param {Boolean} checked The new checked value
20778         */
20779        check : true,
20780        /**
20781         * @event click
20782         * Fires when the element is click.
20783         * @param {Roo.bootstrap.CheckBox} this This input
20784         */
20785        click : true
20786     });
20787     
20788 };
20789
20790 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20791   
20792     inputType: 'checkbox',
20793     inputValue: 1,
20794     valueOff: 0,
20795     boxLabel: false,
20796     checked: false,
20797     weight : false,
20798     inline: false,
20799     tooltip : '',
20800     
20801     getAutoCreate : function()
20802     {
20803         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20804         
20805         var id = Roo.id();
20806         
20807         var cfg = {};
20808         
20809         cfg.cls = 'form-group ' + this.inputType; //input-group
20810         
20811         if(this.inline){
20812             cfg.cls += ' ' + this.inputType + '-inline';
20813         }
20814         
20815         var input =  {
20816             tag: 'input',
20817             id : id,
20818             type : this.inputType,
20819             value : this.inputValue,
20820             cls : 'roo-' + this.inputType, //'form-box',
20821             placeholder : this.placeholder || ''
20822             
20823         };
20824         
20825         if(this.inputType != 'radio'){
20826             var hidden =  {
20827                 tag: 'input',
20828                 type : 'hidden',
20829                 cls : 'roo-hidden-value',
20830                 value : this.checked ? this.inputValue : this.valueOff
20831             };
20832         }
20833         
20834             
20835         if (this.weight) { // Validity check?
20836             cfg.cls += " " + this.inputType + "-" + this.weight;
20837         }
20838         
20839         if (this.disabled) {
20840             input.disabled=true;
20841         }
20842         
20843         if(this.checked){
20844             input.checked = this.checked;
20845         }
20846         
20847         if (this.name) {
20848             
20849             input.name = this.name;
20850             
20851             if(this.inputType != 'radio'){
20852                 hidden.name = this.name;
20853                 input.name = '_hidden_' + this.name;
20854             }
20855         }
20856         
20857         if (this.size) {
20858             input.cls += ' input-' + this.size;
20859         }
20860         
20861         var settings=this;
20862         
20863         ['xs','sm','md','lg'].map(function(size){
20864             if (settings[size]) {
20865                 cfg.cls += ' col-' + size + '-' + settings[size];
20866             }
20867         });
20868         
20869         var inputblock = input;
20870          
20871         if (this.before || this.after) {
20872             
20873             inputblock = {
20874                 cls : 'input-group',
20875                 cn :  [] 
20876             };
20877             
20878             if (this.before) {
20879                 inputblock.cn.push({
20880                     tag :'span',
20881                     cls : 'input-group-addon',
20882                     html : this.before
20883                 });
20884             }
20885             
20886             inputblock.cn.push(input);
20887             
20888             if(this.inputType != 'radio'){
20889                 inputblock.cn.push(hidden);
20890             }
20891             
20892             if (this.after) {
20893                 inputblock.cn.push({
20894                     tag :'span',
20895                     cls : 'input-group-addon',
20896                     html : this.after
20897                 });
20898             }
20899             
20900         }
20901         
20902         if (align ==='left' && this.fieldLabel.length) {
20903 //                Roo.log("left and has label");
20904             cfg.cn = [
20905                 {
20906                     tag: 'label',
20907                     'for' :  id,
20908                     cls : 'control-label',
20909                     html : this.fieldLabel
20910                 },
20911                 {
20912                     cls : "", 
20913                     cn: [
20914                         inputblock
20915                     ]
20916                 }
20917             ];
20918             
20919             if(this.labelWidth > 12){
20920                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20921             }
20922             
20923             if(this.labelWidth < 13 && this.labelmd == 0){
20924                 this.labelmd = this.labelWidth;
20925             }
20926             
20927             if(this.labellg > 0){
20928                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20929                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20930             }
20931             
20932             if(this.labelmd > 0){
20933                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20934                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20935             }
20936             
20937             if(this.labelsm > 0){
20938                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20939                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20940             }
20941             
20942             if(this.labelxs > 0){
20943                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20944                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20945             }
20946             
20947         } else if ( this.fieldLabel.length) {
20948 //                Roo.log(" label");
20949                 cfg.cn = [
20950                    
20951                     {
20952                         tag: this.boxLabel ? 'span' : 'label',
20953                         'for': id,
20954                         cls: 'control-label box-input-label',
20955                         //cls : 'input-group-addon',
20956                         html : this.fieldLabel
20957                     },
20958                     
20959                     inputblock
20960                     
20961                 ];
20962
20963         } else {
20964             
20965 //                Roo.log(" no label && no align");
20966                 cfg.cn = [  inputblock ] ;
20967                 
20968                 
20969         }
20970         
20971         if(this.boxLabel){
20972              var boxLabelCfg = {
20973                 tag: 'label',
20974                 //'for': id, // box label is handled by onclick - so no for...
20975                 cls: 'box-label',
20976                 html: this.boxLabel
20977             };
20978             
20979             if(this.tooltip){
20980                 boxLabelCfg.tooltip = this.tooltip;
20981             }
20982              
20983             cfg.cn.push(boxLabelCfg);
20984         }
20985         
20986         if(this.inputType != 'radio'){
20987             cfg.cn.push(hidden);
20988         }
20989         
20990         return cfg;
20991         
20992     },
20993     
20994     /**
20995      * return the real input element.
20996      */
20997     inputEl: function ()
20998     {
20999         return this.el.select('input.roo-' + this.inputType,true).first();
21000     },
21001     hiddenEl: function ()
21002     {
21003         return this.el.select('input.roo-hidden-value',true).first();
21004     },
21005     
21006     labelEl: function()
21007     {
21008         return this.el.select('label.control-label',true).first();
21009     },
21010     /* depricated... */
21011     
21012     label: function()
21013     {
21014         return this.labelEl();
21015     },
21016     
21017     boxLabelEl: function()
21018     {
21019         return this.el.select('label.box-label',true).first();
21020     },
21021     
21022     initEvents : function()
21023     {
21024 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21025         
21026         this.inputEl().on('click', this.onClick,  this);
21027         
21028         if (this.boxLabel) { 
21029             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
21030         }
21031         
21032         this.startValue = this.getValue();
21033         
21034         if(this.groupId){
21035             Roo.bootstrap.CheckBox.register(this);
21036         }
21037     },
21038     
21039     onClick : function(e)
21040     {   
21041         if(this.fireEvent('click', this, e) !== false){
21042             this.setChecked(!this.checked);
21043         }
21044         
21045     },
21046     
21047     setChecked : function(state,suppressEvent)
21048     {
21049         this.startValue = this.getValue();
21050
21051         if(this.inputType == 'radio'){
21052             
21053             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21054                 e.dom.checked = false;
21055             });
21056             
21057             this.inputEl().dom.checked = true;
21058             
21059             this.inputEl().dom.value = this.inputValue;
21060             
21061             if(suppressEvent !== true){
21062                 this.fireEvent('check', this, true);
21063             }
21064             
21065             this.validate();
21066             
21067             return;
21068         }
21069         
21070         this.checked = state;
21071         
21072         this.inputEl().dom.checked = state;
21073         
21074         
21075         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21076         
21077         if(suppressEvent !== true){
21078             this.fireEvent('check', this, state);
21079         }
21080         
21081         this.validate();
21082     },
21083     
21084     getValue : function()
21085     {
21086         if(this.inputType == 'radio'){
21087             return this.getGroupValue();
21088         }
21089         
21090         return this.hiddenEl().dom.value;
21091         
21092     },
21093     
21094     getGroupValue : function()
21095     {
21096         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21097             return '';
21098         }
21099         
21100         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21101     },
21102     
21103     setValue : function(v,suppressEvent)
21104     {
21105         if(this.inputType == 'radio'){
21106             this.setGroupValue(v, suppressEvent);
21107             return;
21108         }
21109         
21110         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21111         
21112         this.validate();
21113     },
21114     
21115     setGroupValue : function(v, suppressEvent)
21116     {
21117         this.startValue = this.getValue();
21118         
21119         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21120             e.dom.checked = false;
21121             
21122             if(e.dom.value == v){
21123                 e.dom.checked = true;
21124             }
21125         });
21126         
21127         if(suppressEvent !== true){
21128             this.fireEvent('check', this, true);
21129         }
21130
21131         this.validate();
21132         
21133         return;
21134     },
21135     
21136     validate : function()
21137     {
21138         if(this.getVisibilityEl().hasClass('hidden')){
21139             return true;
21140         }
21141         
21142         if(
21143                 this.disabled || 
21144                 (this.inputType == 'radio' && this.validateRadio()) ||
21145                 (this.inputType == 'checkbox' && this.validateCheckbox())
21146         ){
21147             this.markValid();
21148             return true;
21149         }
21150         
21151         this.markInvalid();
21152         return false;
21153     },
21154     
21155     validateRadio : function()
21156     {
21157         if(this.getVisibilityEl().hasClass('hidden')){
21158             return true;
21159         }
21160         
21161         if(this.allowBlank){
21162             return true;
21163         }
21164         
21165         var valid = false;
21166         
21167         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21168             if(!e.dom.checked){
21169                 return;
21170             }
21171             
21172             valid = true;
21173             
21174             return false;
21175         });
21176         
21177         return valid;
21178     },
21179     
21180     validateCheckbox : function()
21181     {
21182         if(!this.groupId){
21183             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21184             //return (this.getValue() == this.inputValue) ? true : false;
21185         }
21186         
21187         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21188         
21189         if(!group){
21190             return false;
21191         }
21192         
21193         var r = false;
21194         
21195         for(var i in group){
21196             if(group[i].el.isVisible(true)){
21197                 r = false;
21198                 break;
21199             }
21200             
21201             r = true;
21202         }
21203         
21204         for(var i in group){
21205             if(r){
21206                 break;
21207             }
21208             
21209             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21210         }
21211         
21212         return r;
21213     },
21214     
21215     /**
21216      * Mark this field as valid
21217      */
21218     markValid : function()
21219     {
21220         var _this = this;
21221         
21222         this.fireEvent('valid', this);
21223         
21224         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21225         
21226         if(this.groupId){
21227             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21228         }
21229         
21230         if(label){
21231             label.markValid();
21232         }
21233
21234         if(this.inputType == 'radio'){
21235             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21236                 var fg = e.findParent('.form-group', false, true);
21237                 if (Roo.bootstrap.version == 3) {
21238                     fg.removeClass([_this.invalidClass, _this.validClass]);
21239                     fg.addClass(_this.validClass);
21240                 } else {
21241                     fg.removeClass(['is-valid', 'is-invalid']);
21242                     fg.addClass('is-valid');
21243                 }
21244             });
21245             
21246             return;
21247         }
21248
21249         if(!this.groupId){
21250             var fg = this.el.findParent('.form-group', false, true);
21251             if (Roo.bootstrap.version == 3) {
21252                 fg.removeClass([this.invalidClass, this.validClass]);
21253                 fg.addClass(this.validClass);
21254             } else {
21255                 fg.removeClass(['is-valid', 'is-invalid']);
21256                 fg.addClass('is-valid');
21257             }
21258             return;
21259         }
21260         
21261         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21262         
21263         if(!group){
21264             return;
21265         }
21266         
21267         for(var i in group){
21268             var fg = group[i].el.findParent('.form-group', false, true);
21269             if (Roo.bootstrap.version == 3) {
21270                 fg.removeClass([this.invalidClass, this.validClass]);
21271                 fg.addClass(this.validClass);
21272             } else {
21273                 fg.removeClass(['is-valid', 'is-invalid']);
21274                 fg.addClass('is-valid');
21275             }
21276         }
21277     },
21278     
21279      /**
21280      * Mark this field as invalid
21281      * @param {String} msg The validation message
21282      */
21283     markInvalid : function(msg)
21284     {
21285         if(this.allowBlank){
21286             return;
21287         }
21288         
21289         var _this = this;
21290         
21291         this.fireEvent('invalid', this, msg);
21292         
21293         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21294         
21295         if(this.groupId){
21296             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21297         }
21298         
21299         if(label){
21300             label.markInvalid();
21301         }
21302             
21303         if(this.inputType == 'radio'){
21304             
21305             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21306                 var fg = e.findParent('.form-group', false, true);
21307                 if (Roo.bootstrap.version == 3) {
21308                     fg.removeClass([_this.invalidClass, _this.validClass]);
21309                     fg.addClass(_this.invalidClass);
21310                 } else {
21311                     fg.removeClass(['is-invalid', 'is-valid']);
21312                     fg.addClass('is-invalid');
21313                 }
21314             });
21315             
21316             return;
21317         }
21318         
21319         if(!this.groupId){
21320             var fg = this.el.findParent('.form-group', false, true);
21321             if (Roo.bootstrap.version == 3) {
21322                 fg.removeClass([_this.invalidClass, _this.validClass]);
21323                 fg.addClass(_this.invalidClass);
21324             } else {
21325                 fg.removeClass(['is-invalid', 'is-valid']);
21326                 fg.addClass('is-invalid');
21327             }
21328             return;
21329         }
21330         
21331         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21332         
21333         if(!group){
21334             return;
21335         }
21336         
21337         for(var i in group){
21338             var fg = group[i].el.findParent('.form-group', false, true);
21339             if (Roo.bootstrap.version == 3) {
21340                 fg.removeClass([_this.invalidClass, _this.validClass]);
21341                 fg.addClass(_this.invalidClass);
21342             } else {
21343                 fg.removeClass(['is-invalid', 'is-valid']);
21344                 fg.addClass('is-invalid');
21345             }
21346         }
21347         
21348     },
21349     
21350     clearInvalid : function()
21351     {
21352         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21353         
21354         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21355         
21356         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21357         
21358         if (label && label.iconEl) {
21359             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21360             label.iconEl.removeClass(['is-invalid', 'is-valid']);
21361         }
21362     },
21363     
21364     disable : function()
21365     {
21366         if(this.inputType != 'radio'){
21367             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21368             return;
21369         }
21370         
21371         var _this = this;
21372         
21373         if(this.rendered){
21374             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21375                 _this.getActionEl().addClass(this.disabledClass);
21376                 e.dom.disabled = true;
21377             });
21378         }
21379         
21380         this.disabled = true;
21381         this.fireEvent("disable", this);
21382         return this;
21383     },
21384
21385     enable : function()
21386     {
21387         if(this.inputType != 'radio'){
21388             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21389             return;
21390         }
21391         
21392         var _this = this;
21393         
21394         if(this.rendered){
21395             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21396                 _this.getActionEl().removeClass(this.disabledClass);
21397                 e.dom.disabled = false;
21398             });
21399         }
21400         
21401         this.disabled = false;
21402         this.fireEvent("enable", this);
21403         return this;
21404     },
21405     
21406     setBoxLabel : function(v)
21407     {
21408         this.boxLabel = v;
21409         
21410         if(this.rendered){
21411             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21412         }
21413     }
21414
21415 });
21416
21417 Roo.apply(Roo.bootstrap.CheckBox, {
21418     
21419     groups: {},
21420     
21421      /**
21422     * register a CheckBox Group
21423     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21424     */
21425     register : function(checkbox)
21426     {
21427         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21428             this.groups[checkbox.groupId] = {};
21429         }
21430         
21431         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21432             return;
21433         }
21434         
21435         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21436         
21437     },
21438     /**
21439     * fetch a CheckBox Group based on the group ID
21440     * @param {string} the group ID
21441     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21442     */
21443     get: function(groupId) {
21444         if (typeof(this.groups[groupId]) == 'undefined') {
21445             return false;
21446         }
21447         
21448         return this.groups[groupId] ;
21449     }
21450     
21451     
21452 });
21453 /*
21454  * - LGPL
21455  *
21456  * RadioItem
21457  * 
21458  */
21459
21460 /**
21461  * @class Roo.bootstrap.Radio
21462  * @extends Roo.bootstrap.Component
21463  * Bootstrap Radio class
21464  * @cfg {String} boxLabel - the label associated
21465  * @cfg {String} value - the value of radio
21466  * 
21467  * @constructor
21468  * Create a new Radio
21469  * @param {Object} config The config object
21470  */
21471 Roo.bootstrap.Radio = function(config){
21472     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21473     
21474 };
21475
21476 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21477     
21478     boxLabel : '',
21479     
21480     value : '',
21481     
21482     getAutoCreate : function()
21483     {
21484         var cfg = {
21485             tag : 'div',
21486             cls : 'form-group radio',
21487             cn : [
21488                 {
21489                     tag : 'label',
21490                     cls : 'box-label',
21491                     html : this.boxLabel
21492                 }
21493             ]
21494         };
21495         
21496         return cfg;
21497     },
21498     
21499     initEvents : function() 
21500     {
21501         this.parent().register(this);
21502         
21503         this.el.on('click', this.onClick, this);
21504         
21505     },
21506     
21507     onClick : function(e)
21508     {
21509         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21510             this.setChecked(true);
21511         }
21512     },
21513     
21514     setChecked : function(state, suppressEvent)
21515     {
21516         this.parent().setValue(this.value, suppressEvent);
21517         
21518     },
21519     
21520     setBoxLabel : function(v)
21521     {
21522         this.boxLabel = v;
21523         
21524         if(this.rendered){
21525             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21526         }
21527     }
21528     
21529 });
21530  
21531
21532  /*
21533  * - LGPL
21534  *
21535  * Input
21536  * 
21537  */
21538
21539 /**
21540  * @class Roo.bootstrap.SecurePass
21541  * @extends Roo.bootstrap.Input
21542  * Bootstrap SecurePass class
21543  *
21544  * 
21545  * @constructor
21546  * Create a new SecurePass
21547  * @param {Object} config The config object
21548  */
21549  
21550 Roo.bootstrap.SecurePass = function (config) {
21551     // these go here, so the translation tool can replace them..
21552     this.errors = {
21553         PwdEmpty: "Please type a password, and then retype it to confirm.",
21554         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21555         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21556         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21557         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21558         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21559         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21560         TooWeak: "Your password is Too Weak."
21561     },
21562     this.meterLabel = "Password strength:";
21563     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21564     this.meterClass = [
21565         "roo-password-meter-tooweak", 
21566         "roo-password-meter-weak", 
21567         "roo-password-meter-medium", 
21568         "roo-password-meter-strong", 
21569         "roo-password-meter-grey"
21570     ];
21571     
21572     this.errors = {};
21573     
21574     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21575 }
21576
21577 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21578     /**
21579      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21580      * {
21581      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21582      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21583      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21584      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21585      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21586      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21587      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21588      * })
21589      */
21590     // private
21591     
21592     meterWidth: 300,
21593     errorMsg :'',    
21594     errors: false,
21595     imageRoot: '/',
21596     /**
21597      * @cfg {String/Object} Label for the strength meter (defaults to
21598      * 'Password strength:')
21599      */
21600     // private
21601     meterLabel: '',
21602     /**
21603      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21604      * ['Weak', 'Medium', 'Strong'])
21605      */
21606     // private    
21607     pwdStrengths: false,    
21608     // private
21609     strength: 0,
21610     // private
21611     _lastPwd: null,
21612     // private
21613     kCapitalLetter: 0,
21614     kSmallLetter: 1,
21615     kDigit: 2,
21616     kPunctuation: 3,
21617     
21618     insecure: false,
21619     // private
21620     initEvents: function ()
21621     {
21622         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21623
21624         if (this.el.is('input[type=password]') && Roo.isSafari) {
21625             this.el.on('keydown', this.SafariOnKeyDown, this);
21626         }
21627
21628         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21629     },
21630     // private
21631     onRender: function (ct, position)
21632     {
21633         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21634         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21635         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21636
21637         this.trigger.createChild({
21638                    cn: [
21639                     {
21640                     //id: 'PwdMeter',
21641                     tag: 'div',
21642                     cls: 'roo-password-meter-grey col-xs-12',
21643                     style: {
21644                         //width: 0,
21645                         //width: this.meterWidth + 'px'                                                
21646                         }
21647                     },
21648                     {                            
21649                          cls: 'roo-password-meter-text'                          
21650                     }
21651                 ]            
21652         });
21653
21654          
21655         if (this.hideTrigger) {
21656             this.trigger.setDisplayed(false);
21657         }
21658         this.setSize(this.width || '', this.height || '');
21659     },
21660     // private
21661     onDestroy: function ()
21662     {
21663         if (this.trigger) {
21664             this.trigger.removeAllListeners();
21665             this.trigger.remove();
21666         }
21667         if (this.wrap) {
21668             this.wrap.remove();
21669         }
21670         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21671     },
21672     // private
21673     checkStrength: function ()
21674     {
21675         var pwd = this.inputEl().getValue();
21676         if (pwd == this._lastPwd) {
21677             return;
21678         }
21679
21680         var strength;
21681         if (this.ClientSideStrongPassword(pwd)) {
21682             strength = 3;
21683         } else if (this.ClientSideMediumPassword(pwd)) {
21684             strength = 2;
21685         } else if (this.ClientSideWeakPassword(pwd)) {
21686             strength = 1;
21687         } else {
21688             strength = 0;
21689         }
21690         
21691         Roo.log('strength1: ' + strength);
21692         
21693         //var pm = this.trigger.child('div/div/div').dom;
21694         var pm = this.trigger.child('div/div');
21695         pm.removeClass(this.meterClass);
21696         pm.addClass(this.meterClass[strength]);
21697                 
21698         
21699         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21700                 
21701         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21702         
21703         this._lastPwd = pwd;
21704     },
21705     reset: function ()
21706     {
21707         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21708         
21709         this._lastPwd = '';
21710         
21711         var pm = this.trigger.child('div/div');
21712         pm.removeClass(this.meterClass);
21713         pm.addClass('roo-password-meter-grey');        
21714         
21715         
21716         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21717         
21718         pt.innerHTML = '';
21719         this.inputEl().dom.type='password';
21720     },
21721     // private
21722     validateValue: function (value)
21723     {
21724         
21725         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21726             return false;
21727         }
21728         if (value.length == 0) {
21729             if (this.allowBlank) {
21730                 this.clearInvalid();
21731                 return true;
21732             }
21733
21734             this.markInvalid(this.errors.PwdEmpty);
21735             this.errorMsg = this.errors.PwdEmpty;
21736             return false;
21737         }
21738         
21739         if(this.insecure){
21740             return true;
21741         }
21742         
21743         if ('[\x21-\x7e]*'.match(value)) {
21744             this.markInvalid(this.errors.PwdBadChar);
21745             this.errorMsg = this.errors.PwdBadChar;
21746             return false;
21747         }
21748         if (value.length < 6) {
21749             this.markInvalid(this.errors.PwdShort);
21750             this.errorMsg = this.errors.PwdShort;
21751             return false;
21752         }
21753         if (value.length > 16) {
21754             this.markInvalid(this.errors.PwdLong);
21755             this.errorMsg = this.errors.PwdLong;
21756             return false;
21757         }
21758         var strength;
21759         if (this.ClientSideStrongPassword(value)) {
21760             strength = 3;
21761         } else if (this.ClientSideMediumPassword(value)) {
21762             strength = 2;
21763         } else if (this.ClientSideWeakPassword(value)) {
21764             strength = 1;
21765         } else {
21766             strength = 0;
21767         }
21768
21769         
21770         if (strength < 2) {
21771             //this.markInvalid(this.errors.TooWeak);
21772             this.errorMsg = this.errors.TooWeak;
21773             //return false;
21774         }
21775         
21776         
21777         console.log('strength2: ' + strength);
21778         
21779         //var pm = this.trigger.child('div/div/div').dom;
21780         
21781         var pm = this.trigger.child('div/div');
21782         pm.removeClass(this.meterClass);
21783         pm.addClass(this.meterClass[strength]);
21784                 
21785         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21786                 
21787         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21788         
21789         this.errorMsg = ''; 
21790         return true;
21791     },
21792     // private
21793     CharacterSetChecks: function (type)
21794     {
21795         this.type = type;
21796         this.fResult = false;
21797     },
21798     // private
21799     isctype: function (character, type)
21800     {
21801         switch (type) {  
21802             case this.kCapitalLetter:
21803                 if (character >= 'A' && character <= 'Z') {
21804                     return true;
21805                 }
21806                 break;
21807             
21808             case this.kSmallLetter:
21809                 if (character >= 'a' && character <= 'z') {
21810                     return true;
21811                 }
21812                 break;
21813             
21814             case this.kDigit:
21815                 if (character >= '0' && character <= '9') {
21816                     return true;
21817                 }
21818                 break;
21819             
21820             case this.kPunctuation:
21821                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21822                     return true;
21823                 }
21824                 break;
21825             
21826             default:
21827                 return false;
21828         }
21829
21830     },
21831     // private
21832     IsLongEnough: function (pwd, size)
21833     {
21834         return !(pwd == null || isNaN(size) || pwd.length < size);
21835     },
21836     // private
21837     SpansEnoughCharacterSets: function (word, nb)
21838     {
21839         if (!this.IsLongEnough(word, nb))
21840         {
21841             return false;
21842         }
21843
21844         var characterSetChecks = new Array(
21845             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21846             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21847         );
21848         
21849         for (var index = 0; index < word.length; ++index) {
21850             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21851                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21852                     characterSetChecks[nCharSet].fResult = true;
21853                     break;
21854                 }
21855             }
21856         }
21857
21858         var nCharSets = 0;
21859         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21860             if (characterSetChecks[nCharSet].fResult) {
21861                 ++nCharSets;
21862             }
21863         }
21864
21865         if (nCharSets < nb) {
21866             return false;
21867         }
21868         return true;
21869     },
21870     // private
21871     ClientSideStrongPassword: function (pwd)
21872     {
21873         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21874     },
21875     // private
21876     ClientSideMediumPassword: function (pwd)
21877     {
21878         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21879     },
21880     // private
21881     ClientSideWeakPassword: function (pwd)
21882     {
21883         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21884     }
21885           
21886 })//<script type="text/javascript">
21887
21888 /*
21889  * Based  Ext JS Library 1.1.1
21890  * Copyright(c) 2006-2007, Ext JS, LLC.
21891  * LGPL
21892  *
21893  */
21894  
21895 /**
21896  * @class Roo.HtmlEditorCore
21897  * @extends Roo.Component
21898  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21899  *
21900  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21901  */
21902
21903 Roo.HtmlEditorCore = function(config){
21904     
21905     
21906     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21907     
21908     
21909     this.addEvents({
21910         /**
21911          * @event initialize
21912          * Fires when the editor is fully initialized (including the iframe)
21913          * @param {Roo.HtmlEditorCore} this
21914          */
21915         initialize: true,
21916         /**
21917          * @event activate
21918          * Fires when the editor is first receives the focus. Any insertion must wait
21919          * until after this event.
21920          * @param {Roo.HtmlEditorCore} this
21921          */
21922         activate: true,
21923          /**
21924          * @event beforesync
21925          * Fires before the textarea is updated with content from the editor iframe. Return false
21926          * to cancel the sync.
21927          * @param {Roo.HtmlEditorCore} this
21928          * @param {String} html
21929          */
21930         beforesync: true,
21931          /**
21932          * @event beforepush
21933          * Fires before the iframe editor is updated with content from the textarea. Return false
21934          * to cancel the push.
21935          * @param {Roo.HtmlEditorCore} this
21936          * @param {String} html
21937          */
21938         beforepush: true,
21939          /**
21940          * @event sync
21941          * Fires when the textarea is updated with content from the editor iframe.
21942          * @param {Roo.HtmlEditorCore} this
21943          * @param {String} html
21944          */
21945         sync: true,
21946          /**
21947          * @event push
21948          * Fires when the iframe editor is updated with content from the textarea.
21949          * @param {Roo.HtmlEditorCore} this
21950          * @param {String} html
21951          */
21952         push: true,
21953         
21954         /**
21955          * @event editorevent
21956          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21957          * @param {Roo.HtmlEditorCore} this
21958          */
21959         editorevent: true
21960         
21961     });
21962     
21963     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21964     
21965     // defaults : white / black...
21966     this.applyBlacklists();
21967     
21968     
21969     
21970 };
21971
21972
21973 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21974
21975
21976      /**
21977      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21978      */
21979     
21980     owner : false,
21981     
21982      /**
21983      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21984      *                        Roo.resizable.
21985      */
21986     resizable : false,
21987      /**
21988      * @cfg {Number} height (in pixels)
21989      */   
21990     height: 300,
21991    /**
21992      * @cfg {Number} width (in pixels)
21993      */   
21994     width: 500,
21995     
21996     /**
21997      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21998      * 
21999      */
22000     stylesheets: false,
22001     
22002     // id of frame..
22003     frameId: false,
22004     
22005     // private properties
22006     validationEvent : false,
22007     deferHeight: true,
22008     initialized : false,
22009     activated : false,
22010     sourceEditMode : false,
22011     onFocus : Roo.emptyFn,
22012     iframePad:3,
22013     hideMode:'offsets',
22014     
22015     clearUp: true,
22016     
22017     // blacklist + whitelisted elements..
22018     black: false,
22019     white: false,
22020      
22021     bodyCls : '',
22022
22023     /**
22024      * Protected method that will not generally be called directly. It
22025      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22026      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22027      */
22028     getDocMarkup : function(){
22029         // body styles..
22030         var st = '';
22031         
22032         // inherit styels from page...?? 
22033         if (this.stylesheets === false) {
22034             
22035             Roo.get(document.head).select('style').each(function(node) {
22036                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22037             });
22038             
22039             Roo.get(document.head).select('link').each(function(node) { 
22040                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22041             });
22042             
22043         } else if (!this.stylesheets.length) {
22044                 // simple..
22045                 st = '<style type="text/css">' +
22046                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22047                    '</style>';
22048         } else { 
22049             st = '<style type="text/css">' +
22050                     this.stylesheets +
22051                 '</style>';
22052         }
22053         
22054         st +=  '<style type="text/css">' +
22055             'IMG { cursor: pointer } ' +
22056         '</style>';
22057
22058         var cls = 'roo-htmleditor-body';
22059         
22060         if(this.bodyCls.length){
22061             cls += ' ' + this.bodyCls;
22062         }
22063         
22064         return '<html><head>' + st  +
22065             //<style type="text/css">' +
22066             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22067             //'</style>' +
22068             ' </head><body class="' +  cls + '"></body></html>';
22069     },
22070
22071     // private
22072     onRender : function(ct, position)
22073     {
22074         var _t = this;
22075         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22076         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22077         
22078         
22079         this.el.dom.style.border = '0 none';
22080         this.el.dom.setAttribute('tabIndex', -1);
22081         this.el.addClass('x-hidden hide');
22082         
22083         
22084         
22085         if(Roo.isIE){ // fix IE 1px bogus margin
22086             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22087         }
22088        
22089         
22090         this.frameId = Roo.id();
22091         
22092          
22093         
22094         var iframe = this.owner.wrap.createChild({
22095             tag: 'iframe',
22096             cls: 'form-control', // bootstrap..
22097             id: this.frameId,
22098             name: this.frameId,
22099             frameBorder : 'no',
22100             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22101         }, this.el
22102         );
22103         
22104         
22105         this.iframe = iframe.dom;
22106
22107          this.assignDocWin();
22108         
22109         this.doc.designMode = 'on';
22110        
22111         this.doc.open();
22112         this.doc.write(this.getDocMarkup());
22113         this.doc.close();
22114
22115         
22116         var task = { // must defer to wait for browser to be ready
22117             run : function(){
22118                 //console.log("run task?" + this.doc.readyState);
22119                 this.assignDocWin();
22120                 if(this.doc.body || this.doc.readyState == 'complete'){
22121                     try {
22122                         this.doc.designMode="on";
22123                     } catch (e) {
22124                         return;
22125                     }
22126                     Roo.TaskMgr.stop(task);
22127                     this.initEditor.defer(10, this);
22128                 }
22129             },
22130             interval : 10,
22131             duration: 10000,
22132             scope: this
22133         };
22134         Roo.TaskMgr.start(task);
22135
22136     },
22137
22138     // private
22139     onResize : function(w, h)
22140     {
22141          Roo.log('resize: ' +w + ',' + h );
22142         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22143         if(!this.iframe){
22144             return;
22145         }
22146         if(typeof w == 'number'){
22147             
22148             this.iframe.style.width = w + 'px';
22149         }
22150         if(typeof h == 'number'){
22151             
22152             this.iframe.style.height = h + 'px';
22153             if(this.doc){
22154                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22155             }
22156         }
22157         
22158     },
22159
22160     /**
22161      * Toggles the editor between standard and source edit mode.
22162      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22163      */
22164     toggleSourceEdit : function(sourceEditMode){
22165         
22166         this.sourceEditMode = sourceEditMode === true;
22167         
22168         if(this.sourceEditMode){
22169  
22170             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22171             
22172         }else{
22173             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22174             //this.iframe.className = '';
22175             this.deferFocus();
22176         }
22177         //this.setSize(this.owner.wrap.getSize());
22178         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22179     },
22180
22181     
22182   
22183
22184     /**
22185      * Protected method that will not generally be called directly. If you need/want
22186      * custom HTML cleanup, this is the method you should override.
22187      * @param {String} html The HTML to be cleaned
22188      * return {String} The cleaned HTML
22189      */
22190     cleanHtml : function(html){
22191         html = String(html);
22192         if(html.length > 5){
22193             if(Roo.isSafari){ // strip safari nonsense
22194                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22195             }
22196         }
22197         if(html == '&nbsp;'){
22198             html = '';
22199         }
22200         return html;
22201     },
22202
22203     /**
22204      * HTML Editor -> Textarea
22205      * Protected method that will not generally be called directly. Syncs the contents
22206      * of the editor iframe with the textarea.
22207      */
22208     syncValue : function(){
22209         if(this.initialized){
22210             var bd = (this.doc.body || this.doc.documentElement);
22211             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22212             var html = bd.innerHTML;
22213             if(Roo.isSafari){
22214                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22215                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22216                 if(m && m[1]){
22217                     html = '<div style="'+m[0]+'">' + html + '</div>';
22218                 }
22219             }
22220             html = this.cleanHtml(html);
22221             // fix up the special chars.. normaly like back quotes in word...
22222             // however we do not want to do this with chinese..
22223             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22224                 var cc = b.charCodeAt();
22225                 if (
22226                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22227                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22228                     (cc >= 0xf900 && cc < 0xfb00 )
22229                 ) {
22230                         return b;
22231                 }
22232                 return "&#"+cc+";" 
22233             });
22234             if(this.owner.fireEvent('beforesync', this, html) !== false){
22235                 this.el.dom.value = html;
22236                 this.owner.fireEvent('sync', this, html);
22237             }
22238         }
22239     },
22240
22241     /**
22242      * Protected method that will not generally be called directly. Pushes the value of the textarea
22243      * into the iframe editor.
22244      */
22245     pushValue : function(){
22246         if(this.initialized){
22247             var v = this.el.dom.value.trim();
22248             
22249 //            if(v.length < 1){
22250 //                v = '&#160;';
22251 //            }
22252             
22253             if(this.owner.fireEvent('beforepush', this, v) !== false){
22254                 var d = (this.doc.body || this.doc.documentElement);
22255                 d.innerHTML = v;
22256                 this.cleanUpPaste();
22257                 this.el.dom.value = d.innerHTML;
22258                 this.owner.fireEvent('push', this, v);
22259             }
22260         }
22261     },
22262
22263     // private
22264     deferFocus : function(){
22265         this.focus.defer(10, this);
22266     },
22267
22268     // doc'ed in Field
22269     focus : function(){
22270         if(this.win && !this.sourceEditMode){
22271             this.win.focus();
22272         }else{
22273             this.el.focus();
22274         }
22275     },
22276     
22277     assignDocWin: function()
22278     {
22279         var iframe = this.iframe;
22280         
22281          if(Roo.isIE){
22282             this.doc = iframe.contentWindow.document;
22283             this.win = iframe.contentWindow;
22284         } else {
22285 //            if (!Roo.get(this.frameId)) {
22286 //                return;
22287 //            }
22288 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22289 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22290             
22291             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22292                 return;
22293             }
22294             
22295             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22296             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22297         }
22298     },
22299     
22300     // private
22301     initEditor : function(){
22302         //console.log("INIT EDITOR");
22303         this.assignDocWin();
22304         
22305         
22306         
22307         this.doc.designMode="on";
22308         this.doc.open();
22309         this.doc.write(this.getDocMarkup());
22310         this.doc.close();
22311         
22312         var dbody = (this.doc.body || this.doc.documentElement);
22313         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22314         // this copies styles from the containing element into thsi one..
22315         // not sure why we need all of this..
22316         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22317         
22318         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22319         //ss['background-attachment'] = 'fixed'; // w3c
22320         dbody.bgProperties = 'fixed'; // ie
22321         //Roo.DomHelper.applyStyles(dbody, ss);
22322         Roo.EventManager.on(this.doc, {
22323             //'mousedown': this.onEditorEvent,
22324             'mouseup': this.onEditorEvent,
22325             'dblclick': this.onEditorEvent,
22326             'click': this.onEditorEvent,
22327             'keyup': this.onEditorEvent,
22328             buffer:100,
22329             scope: this
22330         });
22331         if(Roo.isGecko){
22332             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22333         }
22334         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22335             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22336         }
22337         this.initialized = true;
22338
22339         this.owner.fireEvent('initialize', this);
22340         this.pushValue();
22341     },
22342
22343     // private
22344     onDestroy : function(){
22345         
22346         
22347         
22348         if(this.rendered){
22349             
22350             //for (var i =0; i < this.toolbars.length;i++) {
22351             //    // fixme - ask toolbars for heights?
22352             //    this.toolbars[i].onDestroy();
22353            // }
22354             
22355             //this.wrap.dom.innerHTML = '';
22356             //this.wrap.remove();
22357         }
22358     },
22359
22360     // private
22361     onFirstFocus : function(){
22362         
22363         this.assignDocWin();
22364         
22365         
22366         this.activated = true;
22367          
22368     
22369         if(Roo.isGecko){ // prevent silly gecko errors
22370             this.win.focus();
22371             var s = this.win.getSelection();
22372             if(!s.focusNode || s.focusNode.nodeType != 3){
22373                 var r = s.getRangeAt(0);
22374                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22375                 r.collapse(true);
22376                 this.deferFocus();
22377             }
22378             try{
22379                 this.execCmd('useCSS', true);
22380                 this.execCmd('styleWithCSS', false);
22381             }catch(e){}
22382         }
22383         this.owner.fireEvent('activate', this);
22384     },
22385
22386     // private
22387     adjustFont: function(btn){
22388         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22389         //if(Roo.isSafari){ // safari
22390         //    adjust *= 2;
22391        // }
22392         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22393         if(Roo.isSafari){ // safari
22394             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22395             v =  (v < 10) ? 10 : v;
22396             v =  (v > 48) ? 48 : v;
22397             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22398             
22399         }
22400         
22401         
22402         v = Math.max(1, v+adjust);
22403         
22404         this.execCmd('FontSize', v  );
22405     },
22406
22407     onEditorEvent : function(e)
22408     {
22409         this.owner.fireEvent('editorevent', this, e);
22410       //  this.updateToolbar();
22411         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22412     },
22413
22414     insertTag : function(tg)
22415     {
22416         // could be a bit smarter... -> wrap the current selected tRoo..
22417         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22418             
22419             range = this.createRange(this.getSelection());
22420             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22421             wrappingNode.appendChild(range.extractContents());
22422             range.insertNode(wrappingNode);
22423
22424             return;
22425             
22426             
22427             
22428         }
22429         this.execCmd("formatblock",   tg);
22430         
22431     },
22432     
22433     insertText : function(txt)
22434     {
22435         
22436         
22437         var range = this.createRange();
22438         range.deleteContents();
22439                //alert(Sender.getAttribute('label'));
22440                
22441         range.insertNode(this.doc.createTextNode(txt));
22442     } ,
22443     
22444      
22445
22446     /**
22447      * Executes a Midas editor command on the editor document and performs necessary focus and
22448      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22449      * @param {String} cmd The Midas command
22450      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22451      */
22452     relayCmd : function(cmd, value){
22453         this.win.focus();
22454         this.execCmd(cmd, value);
22455         this.owner.fireEvent('editorevent', this);
22456         //this.updateToolbar();
22457         this.owner.deferFocus();
22458     },
22459
22460     /**
22461      * Executes a Midas editor command directly on the editor document.
22462      * For visual commands, you should use {@link #relayCmd} instead.
22463      * <b>This should only be called after the editor is initialized.</b>
22464      * @param {String} cmd The Midas command
22465      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22466      */
22467     execCmd : function(cmd, value){
22468         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22469         this.syncValue();
22470     },
22471  
22472  
22473    
22474     /**
22475      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22476      * to insert tRoo.
22477      * @param {String} text | dom node.. 
22478      */
22479     insertAtCursor : function(text)
22480     {
22481         
22482         if(!this.activated){
22483             return;
22484         }
22485         /*
22486         if(Roo.isIE){
22487             this.win.focus();
22488             var r = this.doc.selection.createRange();
22489             if(r){
22490                 r.collapse(true);
22491                 r.pasteHTML(text);
22492                 this.syncValue();
22493                 this.deferFocus();
22494             
22495             }
22496             return;
22497         }
22498         */
22499         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22500             this.win.focus();
22501             
22502             
22503             // from jquery ui (MIT licenced)
22504             var range, node;
22505             var win = this.win;
22506             
22507             if (win.getSelection && win.getSelection().getRangeAt) {
22508                 range = win.getSelection().getRangeAt(0);
22509                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22510                 range.insertNode(node);
22511             } else if (win.document.selection && win.document.selection.createRange) {
22512                 // no firefox support
22513                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22514                 win.document.selection.createRange().pasteHTML(txt);
22515             } else {
22516                 // no firefox support
22517                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22518                 this.execCmd('InsertHTML', txt);
22519             } 
22520             
22521             this.syncValue();
22522             
22523             this.deferFocus();
22524         }
22525     },
22526  // private
22527     mozKeyPress : function(e){
22528         if(e.ctrlKey){
22529             var c = e.getCharCode(), cmd;
22530           
22531             if(c > 0){
22532                 c = String.fromCharCode(c).toLowerCase();
22533                 switch(c){
22534                     case 'b':
22535                         cmd = 'bold';
22536                         break;
22537                     case 'i':
22538                         cmd = 'italic';
22539                         break;
22540                     
22541                     case 'u':
22542                         cmd = 'underline';
22543                         break;
22544                     
22545                     case 'v':
22546                         this.cleanUpPaste.defer(100, this);
22547                         return;
22548                         
22549                 }
22550                 if(cmd){
22551                     this.win.focus();
22552                     this.execCmd(cmd);
22553                     this.deferFocus();
22554                     e.preventDefault();
22555                 }
22556                 
22557             }
22558         }
22559     },
22560
22561     // private
22562     fixKeys : function(){ // load time branching for fastest keydown performance
22563         if(Roo.isIE){
22564             return function(e){
22565                 var k = e.getKey(), r;
22566                 if(k == e.TAB){
22567                     e.stopEvent();
22568                     r = this.doc.selection.createRange();
22569                     if(r){
22570                         r.collapse(true);
22571                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22572                         this.deferFocus();
22573                     }
22574                     return;
22575                 }
22576                 
22577                 if(k == e.ENTER){
22578                     r = this.doc.selection.createRange();
22579                     if(r){
22580                         var target = r.parentElement();
22581                         if(!target || target.tagName.toLowerCase() != 'li'){
22582                             e.stopEvent();
22583                             r.pasteHTML('<br />');
22584                             r.collapse(false);
22585                             r.select();
22586                         }
22587                     }
22588                 }
22589                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22590                     this.cleanUpPaste.defer(100, this);
22591                     return;
22592                 }
22593                 
22594                 
22595             };
22596         }else if(Roo.isOpera){
22597             return function(e){
22598                 var k = e.getKey();
22599                 if(k == e.TAB){
22600                     e.stopEvent();
22601                     this.win.focus();
22602                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22603                     this.deferFocus();
22604                 }
22605                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22606                     this.cleanUpPaste.defer(100, this);
22607                     return;
22608                 }
22609                 
22610             };
22611         }else if(Roo.isSafari){
22612             return function(e){
22613                 var k = e.getKey();
22614                 
22615                 if(k == e.TAB){
22616                     e.stopEvent();
22617                     this.execCmd('InsertText','\t');
22618                     this.deferFocus();
22619                     return;
22620                 }
22621                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22622                     this.cleanUpPaste.defer(100, this);
22623                     return;
22624                 }
22625                 
22626              };
22627         }
22628     }(),
22629     
22630     getAllAncestors: function()
22631     {
22632         var p = this.getSelectedNode();
22633         var a = [];
22634         if (!p) {
22635             a.push(p); // push blank onto stack..
22636             p = this.getParentElement();
22637         }
22638         
22639         
22640         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22641             a.push(p);
22642             p = p.parentNode;
22643         }
22644         a.push(this.doc.body);
22645         return a;
22646     },
22647     lastSel : false,
22648     lastSelNode : false,
22649     
22650     
22651     getSelection : function() 
22652     {
22653         this.assignDocWin();
22654         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22655     },
22656     
22657     getSelectedNode: function() 
22658     {
22659         // this may only work on Gecko!!!
22660         
22661         // should we cache this!!!!
22662         
22663         
22664         
22665          
22666         var range = this.createRange(this.getSelection()).cloneRange();
22667         
22668         if (Roo.isIE) {
22669             var parent = range.parentElement();
22670             while (true) {
22671                 var testRange = range.duplicate();
22672                 testRange.moveToElementText(parent);
22673                 if (testRange.inRange(range)) {
22674                     break;
22675                 }
22676                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22677                     break;
22678                 }
22679                 parent = parent.parentElement;
22680             }
22681             return parent;
22682         }
22683         
22684         // is ancestor a text element.
22685         var ac =  range.commonAncestorContainer;
22686         if (ac.nodeType == 3) {
22687             ac = ac.parentNode;
22688         }
22689         
22690         var ar = ac.childNodes;
22691          
22692         var nodes = [];
22693         var other_nodes = [];
22694         var has_other_nodes = false;
22695         for (var i=0;i<ar.length;i++) {
22696             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22697                 continue;
22698             }
22699             // fullly contained node.
22700             
22701             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22702                 nodes.push(ar[i]);
22703                 continue;
22704             }
22705             
22706             // probably selected..
22707             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22708                 other_nodes.push(ar[i]);
22709                 continue;
22710             }
22711             // outer..
22712             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22713                 continue;
22714             }
22715             
22716             
22717             has_other_nodes = true;
22718         }
22719         if (!nodes.length && other_nodes.length) {
22720             nodes= other_nodes;
22721         }
22722         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22723             return false;
22724         }
22725         
22726         return nodes[0];
22727     },
22728     createRange: function(sel)
22729     {
22730         // this has strange effects when using with 
22731         // top toolbar - not sure if it's a great idea.
22732         //this.editor.contentWindow.focus();
22733         if (typeof sel != "undefined") {
22734             try {
22735                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22736             } catch(e) {
22737                 return this.doc.createRange();
22738             }
22739         } else {
22740             return this.doc.createRange();
22741         }
22742     },
22743     getParentElement: function()
22744     {
22745         
22746         this.assignDocWin();
22747         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22748         
22749         var range = this.createRange(sel);
22750          
22751         try {
22752             var p = range.commonAncestorContainer;
22753             while (p.nodeType == 3) { // text node
22754                 p = p.parentNode;
22755             }
22756             return p;
22757         } catch (e) {
22758             return null;
22759         }
22760     
22761     },
22762     /***
22763      *
22764      * Range intersection.. the hard stuff...
22765      *  '-1' = before
22766      *  '0' = hits..
22767      *  '1' = after.
22768      *         [ -- selected range --- ]
22769      *   [fail]                        [fail]
22770      *
22771      *    basically..
22772      *      if end is before start or  hits it. fail.
22773      *      if start is after end or hits it fail.
22774      *
22775      *   if either hits (but other is outside. - then it's not 
22776      *   
22777      *    
22778      **/
22779     
22780     
22781     // @see http://www.thismuchiknow.co.uk/?p=64.
22782     rangeIntersectsNode : function(range, node)
22783     {
22784         var nodeRange = node.ownerDocument.createRange();
22785         try {
22786             nodeRange.selectNode(node);
22787         } catch (e) {
22788             nodeRange.selectNodeContents(node);
22789         }
22790     
22791         var rangeStartRange = range.cloneRange();
22792         rangeStartRange.collapse(true);
22793     
22794         var rangeEndRange = range.cloneRange();
22795         rangeEndRange.collapse(false);
22796     
22797         var nodeStartRange = nodeRange.cloneRange();
22798         nodeStartRange.collapse(true);
22799     
22800         var nodeEndRange = nodeRange.cloneRange();
22801         nodeEndRange.collapse(false);
22802     
22803         return rangeStartRange.compareBoundaryPoints(
22804                  Range.START_TO_START, nodeEndRange) == -1 &&
22805                rangeEndRange.compareBoundaryPoints(
22806                  Range.START_TO_START, nodeStartRange) == 1;
22807         
22808          
22809     },
22810     rangeCompareNode : function(range, node)
22811     {
22812         var nodeRange = node.ownerDocument.createRange();
22813         try {
22814             nodeRange.selectNode(node);
22815         } catch (e) {
22816             nodeRange.selectNodeContents(node);
22817         }
22818         
22819         
22820         range.collapse(true);
22821     
22822         nodeRange.collapse(true);
22823      
22824         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22825         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22826          
22827         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22828         
22829         var nodeIsBefore   =  ss == 1;
22830         var nodeIsAfter    = ee == -1;
22831         
22832         if (nodeIsBefore && nodeIsAfter) {
22833             return 0; // outer
22834         }
22835         if (!nodeIsBefore && nodeIsAfter) {
22836             return 1; //right trailed.
22837         }
22838         
22839         if (nodeIsBefore && !nodeIsAfter) {
22840             return 2;  // left trailed.
22841         }
22842         // fully contined.
22843         return 3;
22844     },
22845
22846     // private? - in a new class?
22847     cleanUpPaste :  function()
22848     {
22849         // cleans up the whole document..
22850         Roo.log('cleanuppaste');
22851         
22852         this.cleanUpChildren(this.doc.body);
22853         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22854         if (clean != this.doc.body.innerHTML) {
22855             this.doc.body.innerHTML = clean;
22856         }
22857         
22858     },
22859     
22860     cleanWordChars : function(input) {// change the chars to hex code
22861         var he = Roo.HtmlEditorCore;
22862         
22863         var output = input;
22864         Roo.each(he.swapCodes, function(sw) { 
22865             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22866             
22867             output = output.replace(swapper, sw[1]);
22868         });
22869         
22870         return output;
22871     },
22872     
22873     
22874     cleanUpChildren : function (n)
22875     {
22876         if (!n.childNodes.length) {
22877             return;
22878         }
22879         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22880            this.cleanUpChild(n.childNodes[i]);
22881         }
22882     },
22883     
22884     
22885         
22886     
22887     cleanUpChild : function (node)
22888     {
22889         var ed = this;
22890         //console.log(node);
22891         if (node.nodeName == "#text") {
22892             // clean up silly Windows -- stuff?
22893             return; 
22894         }
22895         if (node.nodeName == "#comment") {
22896             node.parentNode.removeChild(node);
22897             // clean up silly Windows -- stuff?
22898             return; 
22899         }
22900         var lcname = node.tagName.toLowerCase();
22901         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22902         // whitelist of tags..
22903         
22904         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22905             // remove node.
22906             node.parentNode.removeChild(node);
22907             return;
22908             
22909         }
22910         
22911         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22912         
22913         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22914         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22915         
22916         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22917         //    remove_keep_children = true;
22918         //}
22919         
22920         if (remove_keep_children) {
22921             this.cleanUpChildren(node);
22922             // inserts everything just before this node...
22923             while (node.childNodes.length) {
22924                 var cn = node.childNodes[0];
22925                 node.removeChild(cn);
22926                 node.parentNode.insertBefore(cn, node);
22927             }
22928             node.parentNode.removeChild(node);
22929             return;
22930         }
22931         
22932         if (!node.attributes || !node.attributes.length) {
22933             this.cleanUpChildren(node);
22934             return;
22935         }
22936         
22937         function cleanAttr(n,v)
22938         {
22939             
22940             if (v.match(/^\./) || v.match(/^\//)) {
22941                 return;
22942             }
22943             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22944                 return;
22945             }
22946             if (v.match(/^#/)) {
22947                 return;
22948             }
22949 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22950             node.removeAttribute(n);
22951             
22952         }
22953         
22954         var cwhite = this.cwhite;
22955         var cblack = this.cblack;
22956             
22957         function cleanStyle(n,v)
22958         {
22959             if (v.match(/expression/)) { //XSS?? should we even bother..
22960                 node.removeAttribute(n);
22961                 return;
22962             }
22963             
22964             var parts = v.split(/;/);
22965             var clean = [];
22966             
22967             Roo.each(parts, function(p) {
22968                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22969                 if (!p.length) {
22970                     return true;
22971                 }
22972                 var l = p.split(':').shift().replace(/\s+/g,'');
22973                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22974                 
22975                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22976 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22977                     //node.removeAttribute(n);
22978                     return true;
22979                 }
22980                 //Roo.log()
22981                 // only allow 'c whitelisted system attributes'
22982                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22983 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22984                     //node.removeAttribute(n);
22985                     return true;
22986                 }
22987                 
22988                 
22989                  
22990                 
22991                 clean.push(p);
22992                 return true;
22993             });
22994             if (clean.length) { 
22995                 node.setAttribute(n, clean.join(';'));
22996             } else {
22997                 node.removeAttribute(n);
22998             }
22999             
23000         }
23001         
23002         
23003         for (var i = node.attributes.length-1; i > -1 ; i--) {
23004             var a = node.attributes[i];
23005             //console.log(a);
23006             
23007             if (a.name.toLowerCase().substr(0,2)=='on')  {
23008                 node.removeAttribute(a.name);
23009                 continue;
23010             }
23011             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23012                 node.removeAttribute(a.name);
23013                 continue;
23014             }
23015             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23016                 cleanAttr(a.name,a.value); // fixme..
23017                 continue;
23018             }
23019             if (a.name == 'style') {
23020                 cleanStyle(a.name,a.value);
23021                 continue;
23022             }
23023             /// clean up MS crap..
23024             // tecnically this should be a list of valid class'es..
23025             
23026             
23027             if (a.name == 'class') {
23028                 if (a.value.match(/^Mso/)) {
23029                     node.className = '';
23030                 }
23031                 
23032                 if (a.value.match(/^body$/)) {
23033                     node.className = '';
23034                 }
23035                 continue;
23036             }
23037             
23038             // style cleanup!?
23039             // class cleanup?
23040             
23041         }
23042         
23043         
23044         this.cleanUpChildren(node);
23045         
23046         
23047     },
23048     
23049     /**
23050      * Clean up MS wordisms...
23051      */
23052     cleanWord : function(node)
23053     {
23054         
23055         
23056         if (!node) {
23057             this.cleanWord(this.doc.body);
23058             return;
23059         }
23060         if (node.nodeName == "#text") {
23061             // clean up silly Windows -- stuff?
23062             return; 
23063         }
23064         if (node.nodeName == "#comment") {
23065             node.parentNode.removeChild(node);
23066             // clean up silly Windows -- stuff?
23067             return; 
23068         }
23069         
23070         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23071             node.parentNode.removeChild(node);
23072             return;
23073         }
23074         
23075         // remove - but keep children..
23076         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
23077             while (node.childNodes.length) {
23078                 var cn = node.childNodes[0];
23079                 node.removeChild(cn);
23080                 node.parentNode.insertBefore(cn, node);
23081             }
23082             node.parentNode.removeChild(node);
23083             this.iterateChildren(node, this.cleanWord);
23084             return;
23085         }
23086         // clean styles
23087         if (node.className.length) {
23088             
23089             var cn = node.className.split(/\W+/);
23090             var cna = [];
23091             Roo.each(cn, function(cls) {
23092                 if (cls.match(/Mso[a-zA-Z]+/)) {
23093                     return;
23094                 }
23095                 cna.push(cls);
23096             });
23097             node.className = cna.length ? cna.join(' ') : '';
23098             if (!cna.length) {
23099                 node.removeAttribute("class");
23100             }
23101         }
23102         
23103         if (node.hasAttribute("lang")) {
23104             node.removeAttribute("lang");
23105         }
23106         
23107         if (node.hasAttribute("style")) {
23108             
23109             var styles = node.getAttribute("style").split(";");
23110             var nstyle = [];
23111             Roo.each(styles, function(s) {
23112                 if (!s.match(/:/)) {
23113                     return;
23114                 }
23115                 var kv = s.split(":");
23116                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23117                     return;
23118                 }
23119                 // what ever is left... we allow.
23120                 nstyle.push(s);
23121             });
23122             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23123             if (!nstyle.length) {
23124                 node.removeAttribute('style');
23125             }
23126         }
23127         this.iterateChildren(node, this.cleanWord);
23128         
23129         
23130         
23131     },
23132     /**
23133      * iterateChildren of a Node, calling fn each time, using this as the scole..
23134      * @param {DomNode} node node to iterate children of.
23135      * @param {Function} fn method of this class to call on each item.
23136      */
23137     iterateChildren : function(node, fn)
23138     {
23139         if (!node.childNodes.length) {
23140                 return;
23141         }
23142         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23143            fn.call(this, node.childNodes[i])
23144         }
23145     },
23146     
23147     
23148     /**
23149      * cleanTableWidths.
23150      *
23151      * Quite often pasting from word etc.. results in tables with column and widths.
23152      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23153      *
23154      */
23155     cleanTableWidths : function(node)
23156     {
23157          
23158          
23159         if (!node) {
23160             this.cleanTableWidths(this.doc.body);
23161             return;
23162         }
23163         
23164         // ignore list...
23165         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23166             return; 
23167         }
23168         Roo.log(node.tagName);
23169         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23170             this.iterateChildren(node, this.cleanTableWidths);
23171             return;
23172         }
23173         if (node.hasAttribute('width')) {
23174             node.removeAttribute('width');
23175         }
23176         
23177          
23178         if (node.hasAttribute("style")) {
23179             // pretty basic...
23180             
23181             var styles = node.getAttribute("style").split(";");
23182             var nstyle = [];
23183             Roo.each(styles, function(s) {
23184                 if (!s.match(/:/)) {
23185                     return;
23186                 }
23187                 var kv = s.split(":");
23188                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23189                     return;
23190                 }
23191                 // what ever is left... we allow.
23192                 nstyle.push(s);
23193             });
23194             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23195             if (!nstyle.length) {
23196                 node.removeAttribute('style');
23197             }
23198         }
23199         
23200         this.iterateChildren(node, this.cleanTableWidths);
23201         
23202         
23203     },
23204     
23205     
23206     
23207     
23208     domToHTML : function(currentElement, depth, nopadtext) {
23209         
23210         depth = depth || 0;
23211         nopadtext = nopadtext || false;
23212     
23213         if (!currentElement) {
23214             return this.domToHTML(this.doc.body);
23215         }
23216         
23217         //Roo.log(currentElement);
23218         var j;
23219         var allText = false;
23220         var nodeName = currentElement.nodeName;
23221         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23222         
23223         if  (nodeName == '#text') {
23224             
23225             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23226         }
23227         
23228         
23229         var ret = '';
23230         if (nodeName != 'BODY') {
23231              
23232             var i = 0;
23233             // Prints the node tagName, such as <A>, <IMG>, etc
23234             if (tagName) {
23235                 var attr = [];
23236                 for(i = 0; i < currentElement.attributes.length;i++) {
23237                     // quoting?
23238                     var aname = currentElement.attributes.item(i).name;
23239                     if (!currentElement.attributes.item(i).value.length) {
23240                         continue;
23241                     }
23242                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23243                 }
23244                 
23245                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23246             } 
23247             else {
23248                 
23249                 // eack
23250             }
23251         } else {
23252             tagName = false;
23253         }
23254         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23255             return ret;
23256         }
23257         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23258             nopadtext = true;
23259         }
23260         
23261         
23262         // Traverse the tree
23263         i = 0;
23264         var currentElementChild = currentElement.childNodes.item(i);
23265         var allText = true;
23266         var innerHTML  = '';
23267         lastnode = '';
23268         while (currentElementChild) {
23269             // Formatting code (indent the tree so it looks nice on the screen)
23270             var nopad = nopadtext;
23271             if (lastnode == 'SPAN') {
23272                 nopad  = true;
23273             }
23274             // text
23275             if  (currentElementChild.nodeName == '#text') {
23276                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23277                 toadd = nopadtext ? toadd : toadd.trim();
23278                 if (!nopad && toadd.length > 80) {
23279                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23280                 }
23281                 innerHTML  += toadd;
23282                 
23283                 i++;
23284                 currentElementChild = currentElement.childNodes.item(i);
23285                 lastNode = '';
23286                 continue;
23287             }
23288             allText = false;
23289             
23290             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23291                 
23292             // Recursively traverse the tree structure of the child node
23293             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23294             lastnode = currentElementChild.nodeName;
23295             i++;
23296             currentElementChild=currentElement.childNodes.item(i);
23297         }
23298         
23299         ret += innerHTML;
23300         
23301         if (!allText) {
23302                 // The remaining code is mostly for formatting the tree
23303             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23304         }
23305         
23306         
23307         if (tagName) {
23308             ret+= "</"+tagName+">";
23309         }
23310         return ret;
23311         
23312     },
23313         
23314     applyBlacklists : function()
23315     {
23316         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23317         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23318         
23319         this.white = [];
23320         this.black = [];
23321         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23322             if (b.indexOf(tag) > -1) {
23323                 return;
23324             }
23325             this.white.push(tag);
23326             
23327         }, this);
23328         
23329         Roo.each(w, function(tag) {
23330             if (b.indexOf(tag) > -1) {
23331                 return;
23332             }
23333             if (this.white.indexOf(tag) > -1) {
23334                 return;
23335             }
23336             this.white.push(tag);
23337             
23338         }, this);
23339         
23340         
23341         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23342             if (w.indexOf(tag) > -1) {
23343                 return;
23344             }
23345             this.black.push(tag);
23346             
23347         }, this);
23348         
23349         Roo.each(b, function(tag) {
23350             if (w.indexOf(tag) > -1) {
23351                 return;
23352             }
23353             if (this.black.indexOf(tag) > -1) {
23354                 return;
23355             }
23356             this.black.push(tag);
23357             
23358         }, this);
23359         
23360         
23361         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23362         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23363         
23364         this.cwhite = [];
23365         this.cblack = [];
23366         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23367             if (b.indexOf(tag) > -1) {
23368                 return;
23369             }
23370             this.cwhite.push(tag);
23371             
23372         }, this);
23373         
23374         Roo.each(w, function(tag) {
23375             if (b.indexOf(tag) > -1) {
23376                 return;
23377             }
23378             if (this.cwhite.indexOf(tag) > -1) {
23379                 return;
23380             }
23381             this.cwhite.push(tag);
23382             
23383         }, this);
23384         
23385         
23386         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23387             if (w.indexOf(tag) > -1) {
23388                 return;
23389             }
23390             this.cblack.push(tag);
23391             
23392         }, this);
23393         
23394         Roo.each(b, function(tag) {
23395             if (w.indexOf(tag) > -1) {
23396                 return;
23397             }
23398             if (this.cblack.indexOf(tag) > -1) {
23399                 return;
23400             }
23401             this.cblack.push(tag);
23402             
23403         }, this);
23404     },
23405     
23406     setStylesheets : function(stylesheets)
23407     {
23408         if(typeof(stylesheets) == 'string'){
23409             Roo.get(this.iframe.contentDocument.head).createChild({
23410                 tag : 'link',
23411                 rel : 'stylesheet',
23412                 type : 'text/css',
23413                 href : stylesheets
23414             });
23415             
23416             return;
23417         }
23418         var _this = this;
23419      
23420         Roo.each(stylesheets, function(s) {
23421             if(!s.length){
23422                 return;
23423             }
23424             
23425             Roo.get(_this.iframe.contentDocument.head).createChild({
23426                 tag : 'link',
23427                 rel : 'stylesheet',
23428                 type : 'text/css',
23429                 href : s
23430             });
23431         });
23432
23433         
23434     },
23435     
23436     removeStylesheets : function()
23437     {
23438         var _this = this;
23439         
23440         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23441             s.remove();
23442         });
23443     },
23444     
23445     setStyle : function(style)
23446     {
23447         Roo.get(this.iframe.contentDocument.head).createChild({
23448             tag : 'style',
23449             type : 'text/css',
23450             html : style
23451         });
23452
23453         return;
23454     }
23455     
23456     // hide stuff that is not compatible
23457     /**
23458      * @event blur
23459      * @hide
23460      */
23461     /**
23462      * @event change
23463      * @hide
23464      */
23465     /**
23466      * @event focus
23467      * @hide
23468      */
23469     /**
23470      * @event specialkey
23471      * @hide
23472      */
23473     /**
23474      * @cfg {String} fieldClass @hide
23475      */
23476     /**
23477      * @cfg {String} focusClass @hide
23478      */
23479     /**
23480      * @cfg {String} autoCreate @hide
23481      */
23482     /**
23483      * @cfg {String} inputType @hide
23484      */
23485     /**
23486      * @cfg {String} invalidClass @hide
23487      */
23488     /**
23489      * @cfg {String} invalidText @hide
23490      */
23491     /**
23492      * @cfg {String} msgFx @hide
23493      */
23494     /**
23495      * @cfg {String} validateOnBlur @hide
23496      */
23497 });
23498
23499 Roo.HtmlEditorCore.white = [
23500         'area', 'br', 'img', 'input', 'hr', 'wbr',
23501         
23502        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23503        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23504        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23505        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23506        'table',   'ul',         'xmp', 
23507        
23508        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23509       'thead',   'tr', 
23510      
23511       'dir', 'menu', 'ol', 'ul', 'dl',
23512        
23513       'embed',  'object'
23514 ];
23515
23516
23517 Roo.HtmlEditorCore.black = [
23518     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23519         'applet', // 
23520         'base',   'basefont', 'bgsound', 'blink',  'body', 
23521         'frame',  'frameset', 'head',    'html',   'ilayer', 
23522         'iframe', 'layer',  'link',     'meta',    'object',   
23523         'script', 'style' ,'title',  'xml' // clean later..
23524 ];
23525 Roo.HtmlEditorCore.clean = [
23526     'script', 'style', 'title', 'xml'
23527 ];
23528 Roo.HtmlEditorCore.remove = [
23529     'font'
23530 ];
23531 // attributes..
23532
23533 Roo.HtmlEditorCore.ablack = [
23534     'on'
23535 ];
23536     
23537 Roo.HtmlEditorCore.aclean = [ 
23538     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23539 ];
23540
23541 // protocols..
23542 Roo.HtmlEditorCore.pwhite= [
23543         'http',  'https',  'mailto'
23544 ];
23545
23546 // white listed style attributes.
23547 Roo.HtmlEditorCore.cwhite= [
23548       //  'text-align', /// default is to allow most things..
23549       
23550          
23551 //        'font-size'//??
23552 ];
23553
23554 // black listed style attributes.
23555 Roo.HtmlEditorCore.cblack= [
23556       //  'font-size' -- this can be set by the project 
23557 ];
23558
23559
23560 Roo.HtmlEditorCore.swapCodes   =[ 
23561     [    8211, "--" ], 
23562     [    8212, "--" ], 
23563     [    8216,  "'" ],  
23564     [    8217, "'" ],  
23565     [    8220, '"' ],  
23566     [    8221, '"' ],  
23567     [    8226, "*" ],  
23568     [    8230, "..." ]
23569 ]; 
23570
23571     /*
23572  * - LGPL
23573  *
23574  * HtmlEditor
23575  * 
23576  */
23577
23578 /**
23579  * @class Roo.bootstrap.HtmlEditor
23580  * @extends Roo.bootstrap.TextArea
23581  * Bootstrap HtmlEditor class
23582
23583  * @constructor
23584  * Create a new HtmlEditor
23585  * @param {Object} config The config object
23586  */
23587
23588 Roo.bootstrap.HtmlEditor = function(config){
23589     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23590     if (!this.toolbars) {
23591         this.toolbars = [];
23592     }
23593     
23594     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23595     this.addEvents({
23596             /**
23597              * @event initialize
23598              * Fires when the editor is fully initialized (including the iframe)
23599              * @param {HtmlEditor} this
23600              */
23601             initialize: true,
23602             /**
23603              * @event activate
23604              * Fires when the editor is first receives the focus. Any insertion must wait
23605              * until after this event.
23606              * @param {HtmlEditor} this
23607              */
23608             activate: true,
23609              /**
23610              * @event beforesync
23611              * Fires before the textarea is updated with content from the editor iframe. Return false
23612              * to cancel the sync.
23613              * @param {HtmlEditor} this
23614              * @param {String} html
23615              */
23616             beforesync: true,
23617              /**
23618              * @event beforepush
23619              * Fires before the iframe editor is updated with content from the textarea. Return false
23620              * to cancel the push.
23621              * @param {HtmlEditor} this
23622              * @param {String} html
23623              */
23624             beforepush: true,
23625              /**
23626              * @event sync
23627              * Fires when the textarea is updated with content from the editor iframe.
23628              * @param {HtmlEditor} this
23629              * @param {String} html
23630              */
23631             sync: true,
23632              /**
23633              * @event push
23634              * Fires when the iframe editor is updated with content from the textarea.
23635              * @param {HtmlEditor} this
23636              * @param {String} html
23637              */
23638             push: true,
23639              /**
23640              * @event editmodechange
23641              * Fires when the editor switches edit modes
23642              * @param {HtmlEditor} this
23643              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23644              */
23645             editmodechange: true,
23646             /**
23647              * @event editorevent
23648              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23649              * @param {HtmlEditor} this
23650              */
23651             editorevent: true,
23652             /**
23653              * @event firstfocus
23654              * Fires when on first focus - needed by toolbars..
23655              * @param {HtmlEditor} this
23656              */
23657             firstfocus: true,
23658             /**
23659              * @event autosave
23660              * Auto save the htmlEditor value as a file into Events
23661              * @param {HtmlEditor} this
23662              */
23663             autosave: true,
23664             /**
23665              * @event savedpreview
23666              * preview the saved version of htmlEditor
23667              * @param {HtmlEditor} this
23668              */
23669             savedpreview: true
23670         });
23671 };
23672
23673
23674 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23675     
23676     
23677       /**
23678      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23679      */
23680     toolbars : false,
23681     
23682      /**
23683     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23684     */
23685     btns : [],
23686    
23687      /**
23688      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23689      *                        Roo.resizable.
23690      */
23691     resizable : false,
23692      /**
23693      * @cfg {Number} height (in pixels)
23694      */   
23695     height: 300,
23696    /**
23697      * @cfg {Number} width (in pixels)
23698      */   
23699     width: false,
23700     
23701     /**
23702      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23703      * 
23704      */
23705     stylesheets: false,
23706     
23707     // id of frame..
23708     frameId: false,
23709     
23710     // private properties
23711     validationEvent : false,
23712     deferHeight: true,
23713     initialized : false,
23714     activated : false,
23715     
23716     onFocus : Roo.emptyFn,
23717     iframePad:3,
23718     hideMode:'offsets',
23719     
23720     tbContainer : false,
23721     
23722     bodyCls : '',
23723     
23724     toolbarContainer :function() {
23725         return this.wrap.select('.x-html-editor-tb',true).first();
23726     },
23727
23728     /**
23729      * Protected method that will not generally be called directly. It
23730      * is called when the editor creates its toolbar. Override this method if you need to
23731      * add custom toolbar buttons.
23732      * @param {HtmlEditor} editor
23733      */
23734     createToolbar : function(){
23735         Roo.log('renewing');
23736         Roo.log("create toolbars");
23737         
23738         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23739         this.toolbars[0].render(this.toolbarContainer());
23740         
23741         return;
23742         
23743 //        if (!editor.toolbars || !editor.toolbars.length) {
23744 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23745 //        }
23746 //        
23747 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23748 //            editor.toolbars[i] = Roo.factory(
23749 //                    typeof(editor.toolbars[i]) == 'string' ?
23750 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23751 //                Roo.bootstrap.HtmlEditor);
23752 //            editor.toolbars[i].init(editor);
23753 //        }
23754     },
23755
23756      
23757     // private
23758     onRender : function(ct, position)
23759     {
23760        // Roo.log("Call onRender: " + this.xtype);
23761         var _t = this;
23762         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23763       
23764         this.wrap = this.inputEl().wrap({
23765             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23766         });
23767         
23768         this.editorcore.onRender(ct, position);
23769          
23770         if (this.resizable) {
23771             this.resizeEl = new Roo.Resizable(this.wrap, {
23772                 pinned : true,
23773                 wrap: true,
23774                 dynamic : true,
23775                 minHeight : this.height,
23776                 height: this.height,
23777                 handles : this.resizable,
23778                 width: this.width,
23779                 listeners : {
23780                     resize : function(r, w, h) {
23781                         _t.onResize(w,h); // -something
23782                     }
23783                 }
23784             });
23785             
23786         }
23787         this.createToolbar(this);
23788        
23789         
23790         if(!this.width && this.resizable){
23791             this.setSize(this.wrap.getSize());
23792         }
23793         if (this.resizeEl) {
23794             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23795             // should trigger onReize..
23796         }
23797         
23798     },
23799
23800     // private
23801     onResize : function(w, h)
23802     {
23803         Roo.log('resize: ' +w + ',' + h );
23804         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23805         var ew = false;
23806         var eh = false;
23807         
23808         if(this.inputEl() ){
23809             if(typeof w == 'number'){
23810                 var aw = w - this.wrap.getFrameWidth('lr');
23811                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23812                 ew = aw;
23813             }
23814             if(typeof h == 'number'){
23815                  var tbh = -11;  // fixme it needs to tool bar size!
23816                 for (var i =0; i < this.toolbars.length;i++) {
23817                     // fixme - ask toolbars for heights?
23818                     tbh += this.toolbars[i].el.getHeight();
23819                     //if (this.toolbars[i].footer) {
23820                     //    tbh += this.toolbars[i].footer.el.getHeight();
23821                     //}
23822                 }
23823               
23824                 
23825                 
23826                 
23827                 
23828                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23829                 ah -= 5; // knock a few pixes off for look..
23830                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23831                 var eh = ah;
23832             }
23833         }
23834         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23835         this.editorcore.onResize(ew,eh);
23836         
23837     },
23838
23839     /**
23840      * Toggles the editor between standard and source edit mode.
23841      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23842      */
23843     toggleSourceEdit : function(sourceEditMode)
23844     {
23845         this.editorcore.toggleSourceEdit(sourceEditMode);
23846         
23847         if(this.editorcore.sourceEditMode){
23848             Roo.log('editor - showing textarea');
23849             
23850 //            Roo.log('in');
23851 //            Roo.log(this.syncValue());
23852             this.syncValue();
23853             this.inputEl().removeClass(['hide', 'x-hidden']);
23854             this.inputEl().dom.removeAttribute('tabIndex');
23855             this.inputEl().focus();
23856         }else{
23857             Roo.log('editor - hiding textarea');
23858 //            Roo.log('out')
23859 //            Roo.log(this.pushValue()); 
23860             this.pushValue();
23861             
23862             this.inputEl().addClass(['hide', 'x-hidden']);
23863             this.inputEl().dom.setAttribute('tabIndex', -1);
23864             //this.deferFocus();
23865         }
23866          
23867         if(this.resizable){
23868             this.setSize(this.wrap.getSize());
23869         }
23870         
23871         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23872     },
23873  
23874     // private (for BoxComponent)
23875     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23876
23877     // private (for BoxComponent)
23878     getResizeEl : function(){
23879         return this.wrap;
23880     },
23881
23882     // private (for BoxComponent)
23883     getPositionEl : function(){
23884         return this.wrap;
23885     },
23886
23887     // private
23888     initEvents : function(){
23889         this.originalValue = this.getValue();
23890     },
23891
23892 //    /**
23893 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23894 //     * @method
23895 //     */
23896 //    markInvalid : Roo.emptyFn,
23897 //    /**
23898 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23899 //     * @method
23900 //     */
23901 //    clearInvalid : Roo.emptyFn,
23902
23903     setValue : function(v){
23904         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23905         this.editorcore.pushValue();
23906     },
23907
23908      
23909     // private
23910     deferFocus : function(){
23911         this.focus.defer(10, this);
23912     },
23913
23914     // doc'ed in Field
23915     focus : function(){
23916         this.editorcore.focus();
23917         
23918     },
23919       
23920
23921     // private
23922     onDestroy : function(){
23923         
23924         
23925         
23926         if(this.rendered){
23927             
23928             for (var i =0; i < this.toolbars.length;i++) {
23929                 // fixme - ask toolbars for heights?
23930                 this.toolbars[i].onDestroy();
23931             }
23932             
23933             this.wrap.dom.innerHTML = '';
23934             this.wrap.remove();
23935         }
23936     },
23937
23938     // private
23939     onFirstFocus : function(){
23940         //Roo.log("onFirstFocus");
23941         this.editorcore.onFirstFocus();
23942          for (var i =0; i < this.toolbars.length;i++) {
23943             this.toolbars[i].onFirstFocus();
23944         }
23945         
23946     },
23947     
23948     // private
23949     syncValue : function()
23950     {   
23951         this.editorcore.syncValue();
23952     },
23953     
23954     pushValue : function()
23955     {   
23956         this.editorcore.pushValue();
23957     }
23958      
23959     
23960     // hide stuff that is not compatible
23961     /**
23962      * @event blur
23963      * @hide
23964      */
23965     /**
23966      * @event change
23967      * @hide
23968      */
23969     /**
23970      * @event focus
23971      * @hide
23972      */
23973     /**
23974      * @event specialkey
23975      * @hide
23976      */
23977     /**
23978      * @cfg {String} fieldClass @hide
23979      */
23980     /**
23981      * @cfg {String} focusClass @hide
23982      */
23983     /**
23984      * @cfg {String} autoCreate @hide
23985      */
23986     /**
23987      * @cfg {String} inputType @hide
23988      */
23989      
23990     /**
23991      * @cfg {String} invalidText @hide
23992      */
23993     /**
23994      * @cfg {String} msgFx @hide
23995      */
23996     /**
23997      * @cfg {String} validateOnBlur @hide
23998      */
23999 });
24000  
24001     
24002    
24003    
24004    
24005       
24006 Roo.namespace('Roo.bootstrap.htmleditor');
24007 /**
24008  * @class Roo.bootstrap.HtmlEditorToolbar1
24009  * Basic Toolbar
24010  * 
24011  * Usage:
24012  *
24013  new Roo.bootstrap.HtmlEditor({
24014     ....
24015     toolbars : [
24016         new Roo.bootstrap.HtmlEditorToolbar1({
24017             disable : { fonts: 1 , format: 1, ..., ... , ...],
24018             btns : [ .... ]
24019         })
24020     }
24021      
24022  * 
24023  * @cfg {Object} disable List of elements to disable..
24024  * @cfg {Array} btns List of additional buttons.
24025  * 
24026  * 
24027  * NEEDS Extra CSS? 
24028  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24029  */
24030  
24031 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24032 {
24033     
24034     Roo.apply(this, config);
24035     
24036     // default disabled, based on 'good practice'..
24037     this.disable = this.disable || {};
24038     Roo.applyIf(this.disable, {
24039         fontSize : true,
24040         colors : true,
24041         specialElements : true
24042     });
24043     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24044     
24045     this.editor = config.editor;
24046     this.editorcore = config.editor.editorcore;
24047     
24048     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24049     
24050     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24051     // dont call parent... till later.
24052 }
24053 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
24054      
24055     bar : true,
24056     
24057     editor : false,
24058     editorcore : false,
24059     
24060     
24061     formats : [
24062         "p" ,  
24063         "h1","h2","h3","h4","h5","h6", 
24064         "pre", "code", 
24065         "abbr", "acronym", "address", "cite", "samp", "var",
24066         'div','span'
24067     ],
24068     
24069     onRender : function(ct, position)
24070     {
24071        // Roo.log("Call onRender: " + this.xtype);
24072         
24073        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24074        Roo.log(this.el);
24075        this.el.dom.style.marginBottom = '0';
24076        var _this = this;
24077        var editorcore = this.editorcore;
24078        var editor= this.editor;
24079        
24080        var children = [];
24081        var btn = function(id,cmd , toggle, handler, html){
24082        
24083             var  event = toggle ? 'toggle' : 'click';
24084        
24085             var a = {
24086                 size : 'sm',
24087                 xtype: 'Button',
24088                 xns: Roo.bootstrap,
24089                 //glyphicon : id,
24090                 fa: id,
24091                 cmd : id || cmd,
24092                 enableToggle:toggle !== false,
24093                 html : html || '',
24094                 pressed : toggle ? false : null,
24095                 listeners : {}
24096             };
24097             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24098                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24099             };
24100             children.push(a);
24101             return a;
24102        }
24103        
24104     //    var cb_box = function...
24105         
24106         var style = {
24107                 xtype: 'Button',
24108                 size : 'sm',
24109                 xns: Roo.bootstrap,
24110                 fa : 'font',
24111                 //html : 'submit'
24112                 menu : {
24113                     xtype: 'Menu',
24114                     xns: Roo.bootstrap,
24115                     items:  []
24116                 }
24117         };
24118         Roo.each(this.formats, function(f) {
24119             style.menu.items.push({
24120                 xtype :'MenuItem',
24121                 xns: Roo.bootstrap,
24122                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24123                 tagname : f,
24124                 listeners : {
24125                     click : function()
24126                     {
24127                         editorcore.insertTag(this.tagname);
24128                         editor.focus();
24129                     }
24130                 }
24131                 
24132             });
24133         });
24134         children.push(style);   
24135         
24136         btn('bold',false,true);
24137         btn('italic',false,true);
24138         btn('align-left', 'justifyleft',true);
24139         btn('align-center', 'justifycenter',true);
24140         btn('align-right' , 'justifyright',true);
24141         btn('link', false, false, function(btn) {
24142             //Roo.log("create link?");
24143             var url = prompt(this.createLinkText, this.defaultLinkValue);
24144             if(url && url != 'http:/'+'/'){
24145                 this.editorcore.relayCmd('createlink', url);
24146             }
24147         }),
24148         btn('list','insertunorderedlist',true);
24149         btn('pencil', false,true, function(btn){
24150                 Roo.log(this);
24151                 this.toggleSourceEdit(btn.pressed);
24152         });
24153         
24154         if (this.editor.btns.length > 0) {
24155             for (var i = 0; i<this.editor.btns.length; i++) {
24156                 children.push(this.editor.btns[i]);
24157             }
24158         }
24159         
24160         /*
24161         var cog = {
24162                 xtype: 'Button',
24163                 size : 'sm',
24164                 xns: Roo.bootstrap,
24165                 glyphicon : 'cog',
24166                 //html : 'submit'
24167                 menu : {
24168                     xtype: 'Menu',
24169                     xns: Roo.bootstrap,
24170                     items:  []
24171                 }
24172         };
24173         
24174         cog.menu.items.push({
24175             xtype :'MenuItem',
24176             xns: Roo.bootstrap,
24177             html : Clean styles,
24178             tagname : f,
24179             listeners : {
24180                 click : function()
24181                 {
24182                     editorcore.insertTag(this.tagname);
24183                     editor.focus();
24184                 }
24185             }
24186             
24187         });
24188        */
24189         
24190          
24191        this.xtype = 'NavSimplebar';
24192         
24193         for(var i=0;i< children.length;i++) {
24194             
24195             this.buttons.add(this.addxtypeChild(children[i]));
24196             
24197         }
24198         
24199         editor.on('editorevent', this.updateToolbar, this);
24200     },
24201     onBtnClick : function(id)
24202     {
24203        this.editorcore.relayCmd(id);
24204        this.editorcore.focus();
24205     },
24206     
24207     /**
24208      * Protected method that will not generally be called directly. It triggers
24209      * a toolbar update by reading the markup state of the current selection in the editor.
24210      */
24211     updateToolbar: function(){
24212
24213         if(!this.editorcore.activated){
24214             this.editor.onFirstFocus(); // is this neeed?
24215             return;
24216         }
24217
24218         var btns = this.buttons; 
24219         var doc = this.editorcore.doc;
24220         btns.get('bold').setActive(doc.queryCommandState('bold'));
24221         btns.get('italic').setActive(doc.queryCommandState('italic'));
24222         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24223         
24224         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24225         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24226         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24227         
24228         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24229         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24230          /*
24231         
24232         var ans = this.editorcore.getAllAncestors();
24233         if (this.formatCombo) {
24234             
24235             
24236             var store = this.formatCombo.store;
24237             this.formatCombo.setValue("");
24238             for (var i =0; i < ans.length;i++) {
24239                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24240                     // select it..
24241                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24242                     break;
24243                 }
24244             }
24245         }
24246         
24247         
24248         
24249         // hides menus... - so this cant be on a menu...
24250         Roo.bootstrap.MenuMgr.hideAll();
24251         */
24252         Roo.bootstrap.MenuMgr.hideAll();
24253         //this.editorsyncValue();
24254     },
24255     onFirstFocus: function() {
24256         this.buttons.each(function(item){
24257            item.enable();
24258         });
24259     },
24260     toggleSourceEdit : function(sourceEditMode){
24261         
24262           
24263         if(sourceEditMode){
24264             Roo.log("disabling buttons");
24265            this.buttons.each( function(item){
24266                 if(item.cmd != 'pencil'){
24267                     item.disable();
24268                 }
24269             });
24270           
24271         }else{
24272             Roo.log("enabling buttons");
24273             if(this.editorcore.initialized){
24274                 this.buttons.each( function(item){
24275                     item.enable();
24276                 });
24277             }
24278             
24279         }
24280         Roo.log("calling toggole on editor");
24281         // tell the editor that it's been pressed..
24282         this.editor.toggleSourceEdit(sourceEditMode);
24283        
24284     }
24285 });
24286
24287
24288
24289
24290
24291 /**
24292  * @class Roo.bootstrap.Table.AbstractSelectionModel
24293  * @extends Roo.util.Observable
24294  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24295  * implemented by descendant classes.  This class should not be directly instantiated.
24296  * @constructor
24297  */
24298 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24299     this.locked = false;
24300     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24301 };
24302
24303
24304 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24305     /** @ignore Called by the grid automatically. Do not call directly. */
24306     init : function(grid){
24307         this.grid = grid;
24308         this.initEvents();
24309     },
24310
24311     /**
24312      * Locks the selections.
24313      */
24314     lock : function(){
24315         this.locked = true;
24316     },
24317
24318     /**
24319      * Unlocks the selections.
24320      */
24321     unlock : function(){
24322         this.locked = false;
24323     },
24324
24325     /**
24326      * Returns true if the selections are locked.
24327      * @return {Boolean}
24328      */
24329     isLocked : function(){
24330         return this.locked;
24331     }
24332 });
24333 /**
24334  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24335  * @class Roo.bootstrap.Table.RowSelectionModel
24336  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24337  * It supports multiple selections and keyboard selection/navigation. 
24338  * @constructor
24339  * @param {Object} config
24340  */
24341
24342 Roo.bootstrap.Table.RowSelectionModel = function(config){
24343     Roo.apply(this, config);
24344     this.selections = new Roo.util.MixedCollection(false, function(o){
24345         return o.id;
24346     });
24347
24348     this.last = false;
24349     this.lastActive = false;
24350
24351     this.addEvents({
24352         /**
24353              * @event selectionchange
24354              * Fires when the selection changes
24355              * @param {SelectionModel} this
24356              */
24357             "selectionchange" : true,
24358         /**
24359              * @event afterselectionchange
24360              * Fires after the selection changes (eg. by key press or clicking)
24361              * @param {SelectionModel} this
24362              */
24363             "afterselectionchange" : true,
24364         /**
24365              * @event beforerowselect
24366              * Fires when a row is selected being selected, return false to cancel.
24367              * @param {SelectionModel} this
24368              * @param {Number} rowIndex The selected index
24369              * @param {Boolean} keepExisting False if other selections will be cleared
24370              */
24371             "beforerowselect" : true,
24372         /**
24373              * @event rowselect
24374              * Fires when a row is selected.
24375              * @param {SelectionModel} this
24376              * @param {Number} rowIndex The selected index
24377              * @param {Roo.data.Record} r The record
24378              */
24379             "rowselect" : true,
24380         /**
24381              * @event rowdeselect
24382              * Fires when a row is deselected.
24383              * @param {SelectionModel} this
24384              * @param {Number} rowIndex The selected index
24385              */
24386         "rowdeselect" : true
24387     });
24388     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24389     this.locked = false;
24390  };
24391
24392 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24393     /**
24394      * @cfg {Boolean} singleSelect
24395      * True to allow selection of only one row at a time (defaults to false)
24396      */
24397     singleSelect : false,
24398
24399     // private
24400     initEvents : function()
24401     {
24402
24403         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24404         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24405         //}else{ // allow click to work like normal
24406          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24407         //}
24408         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24409         this.grid.on("rowclick", this.handleMouseDown, this);
24410         
24411         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24412             "up" : function(e){
24413                 if(!e.shiftKey){
24414                     this.selectPrevious(e.shiftKey);
24415                 }else if(this.last !== false && this.lastActive !== false){
24416                     var last = this.last;
24417                     this.selectRange(this.last,  this.lastActive-1);
24418                     this.grid.getView().focusRow(this.lastActive);
24419                     if(last !== false){
24420                         this.last = last;
24421                     }
24422                 }else{
24423                     this.selectFirstRow();
24424                 }
24425                 this.fireEvent("afterselectionchange", this);
24426             },
24427             "down" : function(e){
24428                 if(!e.shiftKey){
24429                     this.selectNext(e.shiftKey);
24430                 }else if(this.last !== false && this.lastActive !== false){
24431                     var last = this.last;
24432                     this.selectRange(this.last,  this.lastActive+1);
24433                     this.grid.getView().focusRow(this.lastActive);
24434                     if(last !== false){
24435                         this.last = last;
24436                     }
24437                 }else{
24438                     this.selectFirstRow();
24439                 }
24440                 this.fireEvent("afterselectionchange", this);
24441             },
24442             scope: this
24443         });
24444         this.grid.store.on('load', function(){
24445             this.selections.clear();
24446         },this);
24447         /*
24448         var view = this.grid.view;
24449         view.on("refresh", this.onRefresh, this);
24450         view.on("rowupdated", this.onRowUpdated, this);
24451         view.on("rowremoved", this.onRemove, this);
24452         */
24453     },
24454
24455     // private
24456     onRefresh : function()
24457     {
24458         var ds = this.grid.store, i, v = this.grid.view;
24459         var s = this.selections;
24460         s.each(function(r){
24461             if((i = ds.indexOfId(r.id)) != -1){
24462                 v.onRowSelect(i);
24463             }else{
24464                 s.remove(r);
24465             }
24466         });
24467     },
24468
24469     // private
24470     onRemove : function(v, index, r){
24471         this.selections.remove(r);
24472     },
24473
24474     // private
24475     onRowUpdated : function(v, index, r){
24476         if(this.isSelected(r)){
24477             v.onRowSelect(index);
24478         }
24479     },
24480
24481     /**
24482      * Select records.
24483      * @param {Array} records The records to select
24484      * @param {Boolean} keepExisting (optional) True to keep existing selections
24485      */
24486     selectRecords : function(records, keepExisting)
24487     {
24488         if(!keepExisting){
24489             this.clearSelections();
24490         }
24491             var ds = this.grid.store;
24492         for(var i = 0, len = records.length; i < len; i++){
24493             this.selectRow(ds.indexOf(records[i]), true);
24494         }
24495     },
24496
24497     /**
24498      * Gets the number of selected rows.
24499      * @return {Number}
24500      */
24501     getCount : function(){
24502         return this.selections.length;
24503     },
24504
24505     /**
24506      * Selects the first row in the grid.
24507      */
24508     selectFirstRow : function(){
24509         this.selectRow(0);
24510     },
24511
24512     /**
24513      * Select the last row.
24514      * @param {Boolean} keepExisting (optional) True to keep existing selections
24515      */
24516     selectLastRow : function(keepExisting){
24517         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24518         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24519     },
24520
24521     /**
24522      * Selects the row immediately following the last selected row.
24523      * @param {Boolean} keepExisting (optional) True to keep existing selections
24524      */
24525     selectNext : function(keepExisting)
24526     {
24527             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24528             this.selectRow(this.last+1, keepExisting);
24529             this.grid.getView().focusRow(this.last);
24530         }
24531     },
24532
24533     /**
24534      * Selects the row that precedes the last selected row.
24535      * @param {Boolean} keepExisting (optional) True to keep existing selections
24536      */
24537     selectPrevious : function(keepExisting){
24538         if(this.last){
24539             this.selectRow(this.last-1, keepExisting);
24540             this.grid.getView().focusRow(this.last);
24541         }
24542     },
24543
24544     /**
24545      * Returns the selected records
24546      * @return {Array} Array of selected records
24547      */
24548     getSelections : function(){
24549         return [].concat(this.selections.items);
24550     },
24551
24552     /**
24553      * Returns the first selected record.
24554      * @return {Record}
24555      */
24556     getSelected : function(){
24557         return this.selections.itemAt(0);
24558     },
24559
24560
24561     /**
24562      * Clears all selections.
24563      */
24564     clearSelections : function(fast)
24565     {
24566         if(this.locked) {
24567             return;
24568         }
24569         if(fast !== true){
24570                 var ds = this.grid.store;
24571             var s = this.selections;
24572             s.each(function(r){
24573                 this.deselectRow(ds.indexOfId(r.id));
24574             }, this);
24575             s.clear();
24576         }else{
24577             this.selections.clear();
24578         }
24579         this.last = false;
24580     },
24581
24582
24583     /**
24584      * Selects all rows.
24585      */
24586     selectAll : function(){
24587         if(this.locked) {
24588             return;
24589         }
24590         this.selections.clear();
24591         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24592             this.selectRow(i, true);
24593         }
24594     },
24595
24596     /**
24597      * Returns True if there is a selection.
24598      * @return {Boolean}
24599      */
24600     hasSelection : function(){
24601         return this.selections.length > 0;
24602     },
24603
24604     /**
24605      * Returns True if the specified row is selected.
24606      * @param {Number/Record} record The record or index of the record to check
24607      * @return {Boolean}
24608      */
24609     isSelected : function(index){
24610             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24611         return (r && this.selections.key(r.id) ? true : false);
24612     },
24613
24614     /**
24615      * Returns True if the specified record id is selected.
24616      * @param {String} id The id of record to check
24617      * @return {Boolean}
24618      */
24619     isIdSelected : function(id){
24620         return (this.selections.key(id) ? true : false);
24621     },
24622
24623
24624     // private
24625     handleMouseDBClick : function(e, t){
24626         
24627     },
24628     // private
24629     handleMouseDown : function(e, t)
24630     {
24631             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24632         if(this.isLocked() || rowIndex < 0 ){
24633             return;
24634         };
24635         if(e.shiftKey && this.last !== false){
24636             var last = this.last;
24637             this.selectRange(last, rowIndex, e.ctrlKey);
24638             this.last = last; // reset the last
24639             t.focus();
24640     
24641         }else{
24642             var isSelected = this.isSelected(rowIndex);
24643             //Roo.log("select row:" + rowIndex);
24644             if(isSelected){
24645                 this.deselectRow(rowIndex);
24646             } else {
24647                         this.selectRow(rowIndex, true);
24648             }
24649     
24650             /*
24651                 if(e.button !== 0 && isSelected){
24652                 alert('rowIndex 2: ' + rowIndex);
24653                     view.focusRow(rowIndex);
24654                 }else if(e.ctrlKey && isSelected){
24655                     this.deselectRow(rowIndex);
24656                 }else if(!isSelected){
24657                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24658                     view.focusRow(rowIndex);
24659                 }
24660             */
24661         }
24662         this.fireEvent("afterselectionchange", this);
24663     },
24664     // private
24665     handleDragableRowClick :  function(grid, rowIndex, e) 
24666     {
24667         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24668             this.selectRow(rowIndex, false);
24669             grid.view.focusRow(rowIndex);
24670              this.fireEvent("afterselectionchange", this);
24671         }
24672     },
24673     
24674     /**
24675      * Selects multiple rows.
24676      * @param {Array} rows Array of the indexes of the row to select
24677      * @param {Boolean} keepExisting (optional) True to keep existing selections
24678      */
24679     selectRows : function(rows, keepExisting){
24680         if(!keepExisting){
24681             this.clearSelections();
24682         }
24683         for(var i = 0, len = rows.length; i < len; i++){
24684             this.selectRow(rows[i], true);
24685         }
24686     },
24687
24688     /**
24689      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24690      * @param {Number} startRow The index of the first row in the range
24691      * @param {Number} endRow The index of the last row in the range
24692      * @param {Boolean} keepExisting (optional) True to retain existing selections
24693      */
24694     selectRange : function(startRow, endRow, keepExisting){
24695         if(this.locked) {
24696             return;
24697         }
24698         if(!keepExisting){
24699             this.clearSelections();
24700         }
24701         if(startRow <= endRow){
24702             for(var i = startRow; i <= endRow; i++){
24703                 this.selectRow(i, true);
24704             }
24705         }else{
24706             for(var i = startRow; i >= endRow; i--){
24707                 this.selectRow(i, true);
24708             }
24709         }
24710     },
24711
24712     /**
24713      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24714      * @param {Number} startRow The index of the first row in the range
24715      * @param {Number} endRow The index of the last row in the range
24716      */
24717     deselectRange : function(startRow, endRow, preventViewNotify){
24718         if(this.locked) {
24719             return;
24720         }
24721         for(var i = startRow; i <= endRow; i++){
24722             this.deselectRow(i, preventViewNotify);
24723         }
24724     },
24725
24726     /**
24727      * Selects a row.
24728      * @param {Number} row The index of the row to select
24729      * @param {Boolean} keepExisting (optional) True to keep existing selections
24730      */
24731     selectRow : function(index, keepExisting, preventViewNotify)
24732     {
24733             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24734             return;
24735         }
24736         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24737             if(!keepExisting || this.singleSelect){
24738                 this.clearSelections();
24739             }
24740             
24741             var r = this.grid.store.getAt(index);
24742             //console.log('selectRow - record id :' + r.id);
24743             
24744             this.selections.add(r);
24745             this.last = this.lastActive = index;
24746             if(!preventViewNotify){
24747                 var proxy = new Roo.Element(
24748                                 this.grid.getRowDom(index)
24749                 );
24750                 proxy.addClass('bg-info info');
24751             }
24752             this.fireEvent("rowselect", this, index, r);
24753             this.fireEvent("selectionchange", this);
24754         }
24755     },
24756
24757     /**
24758      * Deselects a row.
24759      * @param {Number} row The index of the row to deselect
24760      */
24761     deselectRow : function(index, preventViewNotify)
24762     {
24763         if(this.locked) {
24764             return;
24765         }
24766         if(this.last == index){
24767             this.last = false;
24768         }
24769         if(this.lastActive == index){
24770             this.lastActive = false;
24771         }
24772         
24773         var r = this.grid.store.getAt(index);
24774         if (!r) {
24775             return;
24776         }
24777         
24778         this.selections.remove(r);
24779         //.console.log('deselectRow - record id :' + r.id);
24780         if(!preventViewNotify){
24781         
24782             var proxy = new Roo.Element(
24783                 this.grid.getRowDom(index)
24784             );
24785             proxy.removeClass('bg-info info');
24786         }
24787         this.fireEvent("rowdeselect", this, index);
24788         this.fireEvent("selectionchange", this);
24789     },
24790
24791     // private
24792     restoreLast : function(){
24793         if(this._last){
24794             this.last = this._last;
24795         }
24796     },
24797
24798     // private
24799     acceptsNav : function(row, col, cm){
24800         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24801     },
24802
24803     // private
24804     onEditorKey : function(field, e){
24805         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24806         if(k == e.TAB){
24807             e.stopEvent();
24808             ed.completeEdit();
24809             if(e.shiftKey){
24810                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24811             }else{
24812                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24813             }
24814         }else if(k == e.ENTER && !e.ctrlKey){
24815             e.stopEvent();
24816             ed.completeEdit();
24817             if(e.shiftKey){
24818                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24819             }else{
24820                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24821             }
24822         }else if(k == e.ESC){
24823             ed.cancelEdit();
24824         }
24825         if(newCell){
24826             g.startEditing(newCell[0], newCell[1]);
24827         }
24828     }
24829 });
24830 /*
24831  * Based on:
24832  * Ext JS Library 1.1.1
24833  * Copyright(c) 2006-2007, Ext JS, LLC.
24834  *
24835  * Originally Released Under LGPL - original licence link has changed is not relivant.
24836  *
24837  * Fork - LGPL
24838  * <script type="text/javascript">
24839  */
24840  
24841 /**
24842  * @class Roo.bootstrap.PagingToolbar
24843  * @extends Roo.bootstrap.NavSimplebar
24844  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24845  * @constructor
24846  * Create a new PagingToolbar
24847  * @param {Object} config The config object
24848  * @param {Roo.data.Store} store
24849  */
24850 Roo.bootstrap.PagingToolbar = function(config)
24851 {
24852     // old args format still supported... - xtype is prefered..
24853         // created from xtype...
24854     
24855     this.ds = config.dataSource;
24856     
24857     if (config.store && !this.ds) {
24858         this.store= Roo.factory(config.store, Roo.data);
24859         this.ds = this.store;
24860         this.ds.xmodule = this.xmodule || false;
24861     }
24862     
24863     this.toolbarItems = [];
24864     if (config.items) {
24865         this.toolbarItems = config.items;
24866     }
24867     
24868     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24869     
24870     this.cursor = 0;
24871     
24872     if (this.ds) { 
24873         this.bind(this.ds);
24874     }
24875     
24876     if (Roo.bootstrap.version == 4) {
24877         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24878     } else {
24879         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24880     }
24881     
24882 };
24883
24884 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24885     /**
24886      * @cfg {Roo.data.Store} dataSource
24887      * The underlying data store providing the paged data
24888      */
24889     /**
24890      * @cfg {String/HTMLElement/Element} container
24891      * container The id or element that will contain the toolbar
24892      */
24893     /**
24894      * @cfg {Boolean} displayInfo
24895      * True to display the displayMsg (defaults to false)
24896      */
24897     /**
24898      * @cfg {Number} pageSize
24899      * The number of records to display per page (defaults to 20)
24900      */
24901     pageSize: 20,
24902     /**
24903      * @cfg {String} displayMsg
24904      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24905      */
24906     displayMsg : 'Displaying {0} - {1} of {2}',
24907     /**
24908      * @cfg {String} emptyMsg
24909      * The message to display when no records are found (defaults to "No data to display")
24910      */
24911     emptyMsg : 'No data to display',
24912     /**
24913      * Customizable piece of the default paging text (defaults to "Page")
24914      * @type String
24915      */
24916     beforePageText : "Page",
24917     /**
24918      * Customizable piece of the default paging text (defaults to "of %0")
24919      * @type String
24920      */
24921     afterPageText : "of {0}",
24922     /**
24923      * Customizable piece of the default paging text (defaults to "First Page")
24924      * @type String
24925      */
24926     firstText : "First Page",
24927     /**
24928      * Customizable piece of the default paging text (defaults to "Previous Page")
24929      * @type String
24930      */
24931     prevText : "Previous Page",
24932     /**
24933      * Customizable piece of the default paging text (defaults to "Next Page")
24934      * @type String
24935      */
24936     nextText : "Next Page",
24937     /**
24938      * Customizable piece of the default paging text (defaults to "Last Page")
24939      * @type String
24940      */
24941     lastText : "Last Page",
24942     /**
24943      * Customizable piece of the default paging text (defaults to "Refresh")
24944      * @type String
24945      */
24946     refreshText : "Refresh",
24947
24948     buttons : false,
24949     // private
24950     onRender : function(ct, position) 
24951     {
24952         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24953         this.navgroup.parentId = this.id;
24954         this.navgroup.onRender(this.el, null);
24955         // add the buttons to the navgroup
24956         
24957         if(this.displayInfo){
24958             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24959             this.displayEl = this.el.select('.x-paging-info', true).first();
24960 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24961 //            this.displayEl = navel.el.select('span',true).first();
24962         }
24963         
24964         var _this = this;
24965         
24966         if(this.buttons){
24967             Roo.each(_this.buttons, function(e){ // this might need to use render????
24968                Roo.factory(e).render(_this.el);
24969             });
24970         }
24971             
24972         Roo.each(_this.toolbarItems, function(e) {
24973             _this.navgroup.addItem(e);
24974         });
24975         
24976         
24977         this.first = this.navgroup.addItem({
24978             tooltip: this.firstText,
24979             cls: "prev btn-outline-secondary",
24980             html : ' <i class="fa fa-step-backward"></i>',
24981             disabled: true,
24982             preventDefault: true,
24983             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24984         });
24985         
24986         this.prev =  this.navgroup.addItem({
24987             tooltip: this.prevText,
24988             cls: "prev btn-outline-secondary",
24989             html : ' <i class="fa fa-backward"></i>',
24990             disabled: true,
24991             preventDefault: true,
24992             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24993         });
24994     //this.addSeparator();
24995         
24996         
24997         var field = this.navgroup.addItem( {
24998             tagtype : 'span',
24999             cls : 'x-paging-position  btn-outline-secondary',
25000              disabled: true,
25001             html : this.beforePageText  +
25002                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25003                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
25004          } ); //?? escaped?
25005         
25006         this.field = field.el.select('input', true).first();
25007         this.field.on("keydown", this.onPagingKeydown, this);
25008         this.field.on("focus", function(){this.dom.select();});
25009     
25010     
25011         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
25012         //this.field.setHeight(18);
25013         //this.addSeparator();
25014         this.next = this.navgroup.addItem({
25015             tooltip: this.nextText,
25016             cls: "next btn-outline-secondary",
25017             html : ' <i class="fa fa-forward"></i>',
25018             disabled: true,
25019             preventDefault: true,
25020             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
25021         });
25022         this.last = this.navgroup.addItem({
25023             tooltip: this.lastText,
25024             html : ' <i class="fa fa-step-forward"></i>',
25025             cls: "next btn-outline-secondary",
25026             disabled: true,
25027             preventDefault: true,
25028             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
25029         });
25030     //this.addSeparator();
25031         this.loading = this.navgroup.addItem({
25032             tooltip: this.refreshText,
25033             cls: "btn-outline-secondary",
25034             html : ' <i class="fa fa-refresh"></i>',
25035             preventDefault: true,
25036             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25037         });
25038         
25039     },
25040
25041     // private
25042     updateInfo : function(){
25043         if(this.displayEl){
25044             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25045             var msg = count == 0 ?
25046                 this.emptyMsg :
25047                 String.format(
25048                     this.displayMsg,
25049                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
25050                 );
25051             this.displayEl.update(msg);
25052         }
25053     },
25054
25055     // private
25056     onLoad : function(ds, r, o)
25057     {
25058         this.cursor = o.params.start ? o.params.start : 0;
25059         
25060         var d = this.getPageData(),
25061             ap = d.activePage,
25062             ps = d.pages;
25063         
25064         
25065         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25066         this.field.dom.value = ap;
25067         this.first.setDisabled(ap == 1);
25068         this.prev.setDisabled(ap == 1);
25069         this.next.setDisabled(ap == ps);
25070         this.last.setDisabled(ap == ps);
25071         this.loading.enable();
25072         this.updateInfo();
25073     },
25074
25075     // private
25076     getPageData : function(){
25077         var total = this.ds.getTotalCount();
25078         return {
25079             total : total,
25080             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25081             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25082         };
25083     },
25084
25085     // private
25086     onLoadError : function(){
25087         this.loading.enable();
25088     },
25089
25090     // private
25091     onPagingKeydown : function(e){
25092         var k = e.getKey();
25093         var d = this.getPageData();
25094         if(k == e.RETURN){
25095             var v = this.field.dom.value, pageNum;
25096             if(!v || isNaN(pageNum = parseInt(v, 10))){
25097                 this.field.dom.value = d.activePage;
25098                 return;
25099             }
25100             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25101             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25102             e.stopEvent();
25103         }
25104         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))
25105         {
25106           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25107           this.field.dom.value = pageNum;
25108           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25109           e.stopEvent();
25110         }
25111         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25112         {
25113           var v = this.field.dom.value, pageNum; 
25114           var increment = (e.shiftKey) ? 10 : 1;
25115           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25116                 increment *= -1;
25117           }
25118           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25119             this.field.dom.value = d.activePage;
25120             return;
25121           }
25122           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25123           {
25124             this.field.dom.value = parseInt(v, 10) + increment;
25125             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25126             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25127           }
25128           e.stopEvent();
25129         }
25130     },
25131
25132     // private
25133     beforeLoad : function(){
25134         if(this.loading){
25135             this.loading.disable();
25136         }
25137     },
25138
25139     // private
25140     onClick : function(which){
25141         
25142         var ds = this.ds;
25143         if (!ds) {
25144             return;
25145         }
25146         
25147         switch(which){
25148             case "first":
25149                 ds.load({params:{start: 0, limit: this.pageSize}});
25150             break;
25151             case "prev":
25152                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25153             break;
25154             case "next":
25155                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25156             break;
25157             case "last":
25158                 var total = ds.getTotalCount();
25159                 var extra = total % this.pageSize;
25160                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25161                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25162             break;
25163             case "refresh":
25164                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25165             break;
25166         }
25167     },
25168
25169     /**
25170      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25171      * @param {Roo.data.Store} store The data store to unbind
25172      */
25173     unbind : function(ds){
25174         ds.un("beforeload", this.beforeLoad, this);
25175         ds.un("load", this.onLoad, this);
25176         ds.un("loadexception", this.onLoadError, this);
25177         ds.un("remove", this.updateInfo, this);
25178         ds.un("add", this.updateInfo, this);
25179         this.ds = undefined;
25180     },
25181
25182     /**
25183      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25184      * @param {Roo.data.Store} store The data store to bind
25185      */
25186     bind : function(ds){
25187         ds.on("beforeload", this.beforeLoad, this);
25188         ds.on("load", this.onLoad, this);
25189         ds.on("loadexception", this.onLoadError, this);
25190         ds.on("remove", this.updateInfo, this);
25191         ds.on("add", this.updateInfo, this);
25192         this.ds = ds;
25193     }
25194 });/*
25195  * - LGPL
25196  *
25197  * element
25198  * 
25199  */
25200
25201 /**
25202  * @class Roo.bootstrap.MessageBar
25203  * @extends Roo.bootstrap.Component
25204  * Bootstrap MessageBar class
25205  * @cfg {String} html contents of the MessageBar
25206  * @cfg {String} weight (info | success | warning | danger) default info
25207  * @cfg {String} beforeClass insert the bar before the given class
25208  * @cfg {Boolean} closable (true | false) default false
25209  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25210  * 
25211  * @constructor
25212  * Create a new Element
25213  * @param {Object} config The config object
25214  */
25215
25216 Roo.bootstrap.MessageBar = function(config){
25217     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25218 };
25219
25220 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25221     
25222     html: '',
25223     weight: 'info',
25224     closable: false,
25225     fixed: false,
25226     beforeClass: 'bootstrap-sticky-wrap',
25227     
25228     getAutoCreate : function(){
25229         
25230         var cfg = {
25231             tag: 'div',
25232             cls: 'alert alert-dismissable alert-' + this.weight,
25233             cn: [
25234                 {
25235                     tag: 'span',
25236                     cls: 'message',
25237                     html: this.html || ''
25238                 }
25239             ]
25240         };
25241         
25242         if(this.fixed){
25243             cfg.cls += ' alert-messages-fixed';
25244         }
25245         
25246         if(this.closable){
25247             cfg.cn.push({
25248                 tag: 'button',
25249                 cls: 'close',
25250                 html: 'x'
25251             });
25252         }
25253         
25254         return cfg;
25255     },
25256     
25257     onRender : function(ct, position)
25258     {
25259         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25260         
25261         if(!this.el){
25262             var cfg = Roo.apply({},  this.getAutoCreate());
25263             cfg.id = Roo.id();
25264             
25265             if (this.cls) {
25266                 cfg.cls += ' ' + this.cls;
25267             }
25268             if (this.style) {
25269                 cfg.style = this.style;
25270             }
25271             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25272             
25273             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25274         }
25275         
25276         this.el.select('>button.close').on('click', this.hide, this);
25277         
25278     },
25279     
25280     show : function()
25281     {
25282         if (!this.rendered) {
25283             this.render();
25284         }
25285         
25286         this.el.show();
25287         
25288         this.fireEvent('show', this);
25289         
25290     },
25291     
25292     hide : function()
25293     {
25294         if (!this.rendered) {
25295             this.render();
25296         }
25297         
25298         this.el.hide();
25299         
25300         this.fireEvent('hide', this);
25301     },
25302     
25303     update : function()
25304     {
25305 //        var e = this.el.dom.firstChild;
25306 //        
25307 //        if(this.closable){
25308 //            e = e.nextSibling;
25309 //        }
25310 //        
25311 //        e.data = this.html || '';
25312
25313         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25314     }
25315    
25316 });
25317
25318  
25319
25320      /*
25321  * - LGPL
25322  *
25323  * Graph
25324  * 
25325  */
25326
25327
25328 /**
25329  * @class Roo.bootstrap.Graph
25330  * @extends Roo.bootstrap.Component
25331  * Bootstrap Graph class
25332 > Prameters
25333  -sm {number} sm 4
25334  -md {number} md 5
25335  @cfg {String} graphtype  bar | vbar | pie
25336  @cfg {number} g_x coodinator | centre x (pie)
25337  @cfg {number} g_y coodinator | centre y (pie)
25338  @cfg {number} g_r radius (pie)
25339  @cfg {number} g_height height of the chart (respected by all elements in the set)
25340  @cfg {number} g_width width of the chart (respected by all elements in the set)
25341  @cfg {Object} title The title of the chart
25342     
25343  -{Array}  values
25344  -opts (object) options for the chart 
25345      o {
25346      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25347      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25348      o vgutter (number)
25349      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.
25350      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25351      o to
25352      o stretch (boolean)
25353      o }
25354  -opts (object) options for the pie
25355      o{
25356      o cut
25357      o startAngle (number)
25358      o endAngle (number)
25359      } 
25360  *
25361  * @constructor
25362  * Create a new Input
25363  * @param {Object} config The config object
25364  */
25365
25366 Roo.bootstrap.Graph = function(config){
25367     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25368     
25369     this.addEvents({
25370         // img events
25371         /**
25372          * @event click
25373          * The img click event for the img.
25374          * @param {Roo.EventObject} e
25375          */
25376         "click" : true
25377     });
25378 };
25379
25380 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25381     
25382     sm: 4,
25383     md: 5,
25384     graphtype: 'bar',
25385     g_height: 250,
25386     g_width: 400,
25387     g_x: 50,
25388     g_y: 50,
25389     g_r: 30,
25390     opts:{
25391         //g_colors: this.colors,
25392         g_type: 'soft',
25393         g_gutter: '20%'
25394
25395     },
25396     title : false,
25397
25398     getAutoCreate : function(){
25399         
25400         var cfg = {
25401             tag: 'div',
25402             html : null
25403         };
25404         
25405         
25406         return  cfg;
25407     },
25408
25409     onRender : function(ct,position){
25410         
25411         
25412         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25413         
25414         if (typeof(Raphael) == 'undefined') {
25415             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25416             return;
25417         }
25418         
25419         this.raphael = Raphael(this.el.dom);
25420         
25421                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25422                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25423                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25424                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25425                 /*
25426                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25427                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25428                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25429                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25430                 
25431                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25432                 r.barchart(330, 10, 300, 220, data1);
25433                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25434                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25435                 */
25436                 
25437                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25438                 // r.barchart(30, 30, 560, 250,  xdata, {
25439                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25440                 //     axis : "0 0 1 1",
25441                 //     axisxlabels :  xdata
25442                 //     //yvalues : cols,
25443                    
25444                 // });
25445 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25446 //        
25447 //        this.load(null,xdata,{
25448 //                axis : "0 0 1 1",
25449 //                axisxlabels :  xdata
25450 //                });
25451
25452     },
25453
25454     load : function(graphtype,xdata,opts)
25455     {
25456         this.raphael.clear();
25457         if(!graphtype) {
25458             graphtype = this.graphtype;
25459         }
25460         if(!opts){
25461             opts = this.opts;
25462         }
25463         var r = this.raphael,
25464             fin = function () {
25465                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25466             },
25467             fout = function () {
25468                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25469             },
25470             pfin = function() {
25471                 this.sector.stop();
25472                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25473
25474                 if (this.label) {
25475                     this.label[0].stop();
25476                     this.label[0].attr({ r: 7.5 });
25477                     this.label[1].attr({ "font-weight": 800 });
25478                 }
25479             },
25480             pfout = function() {
25481                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25482
25483                 if (this.label) {
25484                     this.label[0].animate({ r: 5 }, 500, "bounce");
25485                     this.label[1].attr({ "font-weight": 400 });
25486                 }
25487             };
25488
25489         switch(graphtype){
25490             case 'bar':
25491                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25492                 break;
25493             case 'hbar':
25494                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25495                 break;
25496             case 'pie':
25497 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25498 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25499 //            
25500                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25501                 
25502                 break;
25503
25504         }
25505         
25506         if(this.title){
25507             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25508         }
25509         
25510     },
25511     
25512     setTitle: function(o)
25513     {
25514         this.title = o;
25515     },
25516     
25517     initEvents: function() {
25518         
25519         if(!this.href){
25520             this.el.on('click', this.onClick, this);
25521         }
25522     },
25523     
25524     onClick : function(e)
25525     {
25526         Roo.log('img onclick');
25527         this.fireEvent('click', this, e);
25528     }
25529    
25530 });
25531
25532  
25533 /*
25534  * - LGPL
25535  *
25536  * numberBox
25537  * 
25538  */
25539 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25540
25541 /**
25542  * @class Roo.bootstrap.dash.NumberBox
25543  * @extends Roo.bootstrap.Component
25544  * Bootstrap NumberBox class
25545  * @cfg {String} headline Box headline
25546  * @cfg {String} content Box content
25547  * @cfg {String} icon Box icon
25548  * @cfg {String} footer Footer text
25549  * @cfg {String} fhref Footer href
25550  * 
25551  * @constructor
25552  * Create a new NumberBox
25553  * @param {Object} config The config object
25554  */
25555
25556
25557 Roo.bootstrap.dash.NumberBox = function(config){
25558     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25559     
25560 };
25561
25562 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25563     
25564     headline : '',
25565     content : '',
25566     icon : '',
25567     footer : '',
25568     fhref : '',
25569     ficon : '',
25570     
25571     getAutoCreate : function(){
25572         
25573         var cfg = {
25574             tag : 'div',
25575             cls : 'small-box ',
25576             cn : [
25577                 {
25578                     tag : 'div',
25579                     cls : 'inner',
25580                     cn :[
25581                         {
25582                             tag : 'h3',
25583                             cls : 'roo-headline',
25584                             html : this.headline
25585                         },
25586                         {
25587                             tag : 'p',
25588                             cls : 'roo-content',
25589                             html : this.content
25590                         }
25591                     ]
25592                 }
25593             ]
25594         };
25595         
25596         if(this.icon){
25597             cfg.cn.push({
25598                 tag : 'div',
25599                 cls : 'icon',
25600                 cn :[
25601                     {
25602                         tag : 'i',
25603                         cls : 'ion ' + this.icon
25604                     }
25605                 ]
25606             });
25607         }
25608         
25609         if(this.footer){
25610             var footer = {
25611                 tag : 'a',
25612                 cls : 'small-box-footer',
25613                 href : this.fhref || '#',
25614                 html : this.footer
25615             };
25616             
25617             cfg.cn.push(footer);
25618             
25619         }
25620         
25621         return  cfg;
25622     },
25623
25624     onRender : function(ct,position){
25625         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25626
25627
25628        
25629                 
25630     },
25631
25632     setHeadline: function (value)
25633     {
25634         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25635     },
25636     
25637     setFooter: function (value, href)
25638     {
25639         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25640         
25641         if(href){
25642             this.el.select('a.small-box-footer',true).first().attr('href', href);
25643         }
25644         
25645     },
25646
25647     setContent: function (value)
25648     {
25649         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25650     },
25651
25652     initEvents: function() 
25653     {   
25654         
25655     }
25656     
25657 });
25658
25659  
25660 /*
25661  * - LGPL
25662  *
25663  * TabBox
25664  * 
25665  */
25666 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25667
25668 /**
25669  * @class Roo.bootstrap.dash.TabBox
25670  * @extends Roo.bootstrap.Component
25671  * Bootstrap TabBox class
25672  * @cfg {String} title Title of the TabBox
25673  * @cfg {String} icon Icon of the TabBox
25674  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25675  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25676  * 
25677  * @constructor
25678  * Create a new TabBox
25679  * @param {Object} config The config object
25680  */
25681
25682
25683 Roo.bootstrap.dash.TabBox = function(config){
25684     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25685     this.addEvents({
25686         // raw events
25687         /**
25688          * @event addpane
25689          * When a pane is added
25690          * @param {Roo.bootstrap.dash.TabPane} pane
25691          */
25692         "addpane" : true,
25693         /**
25694          * @event activatepane
25695          * When a pane is activated
25696          * @param {Roo.bootstrap.dash.TabPane} pane
25697          */
25698         "activatepane" : true
25699         
25700          
25701     });
25702     
25703     this.panes = [];
25704 };
25705
25706 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25707
25708     title : '',
25709     icon : false,
25710     showtabs : true,
25711     tabScrollable : false,
25712     
25713     getChildContainer : function()
25714     {
25715         return this.el.select('.tab-content', true).first();
25716     },
25717     
25718     getAutoCreate : function(){
25719         
25720         var header = {
25721             tag: 'li',
25722             cls: 'pull-left header',
25723             html: this.title,
25724             cn : []
25725         };
25726         
25727         if(this.icon){
25728             header.cn.push({
25729                 tag: 'i',
25730                 cls: 'fa ' + this.icon
25731             });
25732         }
25733         
25734         var h = {
25735             tag: 'ul',
25736             cls: 'nav nav-tabs pull-right',
25737             cn: [
25738                 header
25739             ]
25740         };
25741         
25742         if(this.tabScrollable){
25743             h = {
25744                 tag: 'div',
25745                 cls: 'tab-header',
25746                 cn: [
25747                     {
25748                         tag: 'ul',
25749                         cls: 'nav nav-tabs pull-right',
25750                         cn: [
25751                             header
25752                         ]
25753                     }
25754                 ]
25755             };
25756         }
25757         
25758         var cfg = {
25759             tag: 'div',
25760             cls: 'nav-tabs-custom',
25761             cn: [
25762                 h,
25763                 {
25764                     tag: 'div',
25765                     cls: 'tab-content no-padding',
25766                     cn: []
25767                 }
25768             ]
25769         };
25770
25771         return  cfg;
25772     },
25773     initEvents : function()
25774     {
25775         //Roo.log('add add pane handler');
25776         this.on('addpane', this.onAddPane, this);
25777     },
25778      /**
25779      * Updates the box title
25780      * @param {String} html to set the title to.
25781      */
25782     setTitle : function(value)
25783     {
25784         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25785     },
25786     onAddPane : function(pane)
25787     {
25788         this.panes.push(pane);
25789         //Roo.log('addpane');
25790         //Roo.log(pane);
25791         // tabs are rendere left to right..
25792         if(!this.showtabs){
25793             return;
25794         }
25795         
25796         var ctr = this.el.select('.nav-tabs', true).first();
25797          
25798          
25799         var existing = ctr.select('.nav-tab',true);
25800         var qty = existing.getCount();;
25801         
25802         
25803         var tab = ctr.createChild({
25804             tag : 'li',
25805             cls : 'nav-tab' + (qty ? '' : ' active'),
25806             cn : [
25807                 {
25808                     tag : 'a',
25809                     href:'#',
25810                     html : pane.title
25811                 }
25812             ]
25813         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25814         pane.tab = tab;
25815         
25816         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25817         if (!qty) {
25818             pane.el.addClass('active');
25819         }
25820         
25821                 
25822     },
25823     onTabClick : function(ev,un,ob,pane)
25824     {
25825         //Roo.log('tab - prev default');
25826         ev.preventDefault();
25827         
25828         
25829         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25830         pane.tab.addClass('active');
25831         //Roo.log(pane.title);
25832         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25833         // technically we should have a deactivate event.. but maybe add later.
25834         // and it should not de-activate the selected tab...
25835         this.fireEvent('activatepane', pane);
25836         pane.el.addClass('active');
25837         pane.fireEvent('activate');
25838         
25839         
25840     },
25841     
25842     getActivePane : function()
25843     {
25844         var r = false;
25845         Roo.each(this.panes, function(p) {
25846             if(p.el.hasClass('active')){
25847                 r = p;
25848                 return false;
25849             }
25850             
25851             return;
25852         });
25853         
25854         return r;
25855     }
25856     
25857     
25858 });
25859
25860  
25861 /*
25862  * - LGPL
25863  *
25864  * Tab pane
25865  * 
25866  */
25867 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25868 /**
25869  * @class Roo.bootstrap.TabPane
25870  * @extends Roo.bootstrap.Component
25871  * Bootstrap TabPane class
25872  * @cfg {Boolean} active (false | true) Default false
25873  * @cfg {String} title title of panel
25874
25875  * 
25876  * @constructor
25877  * Create a new TabPane
25878  * @param {Object} config The config object
25879  */
25880
25881 Roo.bootstrap.dash.TabPane = function(config){
25882     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25883     
25884     this.addEvents({
25885         // raw events
25886         /**
25887          * @event activate
25888          * When a pane is activated
25889          * @param {Roo.bootstrap.dash.TabPane} pane
25890          */
25891         "activate" : true
25892          
25893     });
25894 };
25895
25896 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25897     
25898     active : false,
25899     title : '',
25900     
25901     // the tabBox that this is attached to.
25902     tab : false,
25903      
25904     getAutoCreate : function() 
25905     {
25906         var cfg = {
25907             tag: 'div',
25908             cls: 'tab-pane'
25909         };
25910         
25911         if(this.active){
25912             cfg.cls += ' active';
25913         }
25914         
25915         return cfg;
25916     },
25917     initEvents  : function()
25918     {
25919         //Roo.log('trigger add pane handler');
25920         this.parent().fireEvent('addpane', this)
25921     },
25922     
25923      /**
25924      * Updates the tab title 
25925      * @param {String} html to set the title to.
25926      */
25927     setTitle: function(str)
25928     {
25929         if (!this.tab) {
25930             return;
25931         }
25932         this.title = str;
25933         this.tab.select('a', true).first().dom.innerHTML = str;
25934         
25935     }
25936     
25937     
25938     
25939 });
25940
25941  
25942
25943
25944  /*
25945  * - LGPL
25946  *
25947  * menu
25948  * 
25949  */
25950 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25951
25952 /**
25953  * @class Roo.bootstrap.menu.Menu
25954  * @extends Roo.bootstrap.Component
25955  * Bootstrap Menu class - container for Menu
25956  * @cfg {String} html Text of the menu
25957  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25958  * @cfg {String} icon Font awesome icon
25959  * @cfg {String} pos Menu align to (top | bottom) default bottom
25960  * 
25961  * 
25962  * @constructor
25963  * Create a new Menu
25964  * @param {Object} config The config object
25965  */
25966
25967
25968 Roo.bootstrap.menu.Menu = function(config){
25969     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25970     
25971     this.addEvents({
25972         /**
25973          * @event beforeshow
25974          * Fires before this menu is displayed
25975          * @param {Roo.bootstrap.menu.Menu} this
25976          */
25977         beforeshow : true,
25978         /**
25979          * @event beforehide
25980          * Fires before this menu is hidden
25981          * @param {Roo.bootstrap.menu.Menu} this
25982          */
25983         beforehide : true,
25984         /**
25985          * @event show
25986          * Fires after this menu is displayed
25987          * @param {Roo.bootstrap.menu.Menu} this
25988          */
25989         show : true,
25990         /**
25991          * @event hide
25992          * Fires after this menu is hidden
25993          * @param {Roo.bootstrap.menu.Menu} this
25994          */
25995         hide : true,
25996         /**
25997          * @event click
25998          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25999          * @param {Roo.bootstrap.menu.Menu} this
26000          * @param {Roo.EventObject} e
26001          */
26002         click : true
26003     });
26004     
26005 };
26006
26007 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
26008     
26009     submenu : false,
26010     html : '',
26011     weight : 'default',
26012     icon : false,
26013     pos : 'bottom',
26014     
26015     
26016     getChildContainer : function() {
26017         if(this.isSubMenu){
26018             return this.el;
26019         }
26020         
26021         return this.el.select('ul.dropdown-menu', true).first();  
26022     },
26023     
26024     getAutoCreate : function()
26025     {
26026         var text = [
26027             {
26028                 tag : 'span',
26029                 cls : 'roo-menu-text',
26030                 html : this.html
26031             }
26032         ];
26033         
26034         if(this.icon){
26035             text.unshift({
26036                 tag : 'i',
26037                 cls : 'fa ' + this.icon
26038             })
26039         }
26040         
26041         
26042         var cfg = {
26043             tag : 'div',
26044             cls : 'btn-group',
26045             cn : [
26046                 {
26047                     tag : 'button',
26048                     cls : 'dropdown-button btn btn-' + this.weight,
26049                     cn : text
26050                 },
26051                 {
26052                     tag : 'button',
26053                     cls : 'dropdown-toggle btn btn-' + this.weight,
26054                     cn : [
26055                         {
26056                             tag : 'span',
26057                             cls : 'caret'
26058                         }
26059                     ]
26060                 },
26061                 {
26062                     tag : 'ul',
26063                     cls : 'dropdown-menu'
26064                 }
26065             ]
26066             
26067         };
26068         
26069         if(this.pos == 'top'){
26070             cfg.cls += ' dropup';
26071         }
26072         
26073         if(this.isSubMenu){
26074             cfg = {
26075                 tag : 'ul',
26076                 cls : 'dropdown-menu'
26077             }
26078         }
26079         
26080         return cfg;
26081     },
26082     
26083     onRender : function(ct, position)
26084     {
26085         this.isSubMenu = ct.hasClass('dropdown-submenu');
26086         
26087         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26088     },
26089     
26090     initEvents : function() 
26091     {
26092         if(this.isSubMenu){
26093             return;
26094         }
26095         
26096         this.hidden = true;
26097         
26098         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26099         this.triggerEl.on('click', this.onTriggerPress, this);
26100         
26101         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26102         this.buttonEl.on('click', this.onClick, this);
26103         
26104     },
26105     
26106     list : function()
26107     {
26108         if(this.isSubMenu){
26109             return this.el;
26110         }
26111         
26112         return this.el.select('ul.dropdown-menu', true).first();
26113     },
26114     
26115     onClick : function(e)
26116     {
26117         this.fireEvent("click", this, e);
26118     },
26119     
26120     onTriggerPress  : function(e)
26121     {   
26122         if (this.isVisible()) {
26123             this.hide();
26124         } else {
26125             this.show();
26126         }
26127     },
26128     
26129     isVisible : function(){
26130         return !this.hidden;
26131     },
26132     
26133     show : function()
26134     {
26135         this.fireEvent("beforeshow", this);
26136         
26137         this.hidden = false;
26138         this.el.addClass('open');
26139         
26140         Roo.get(document).on("mouseup", this.onMouseUp, this);
26141         
26142         this.fireEvent("show", this);
26143         
26144         
26145     },
26146     
26147     hide : function()
26148     {
26149         this.fireEvent("beforehide", this);
26150         
26151         this.hidden = true;
26152         this.el.removeClass('open');
26153         
26154         Roo.get(document).un("mouseup", this.onMouseUp);
26155         
26156         this.fireEvent("hide", this);
26157     },
26158     
26159     onMouseUp : function()
26160     {
26161         this.hide();
26162     }
26163     
26164 });
26165
26166  
26167  /*
26168  * - LGPL
26169  *
26170  * menu item
26171  * 
26172  */
26173 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26174
26175 /**
26176  * @class Roo.bootstrap.menu.Item
26177  * @extends Roo.bootstrap.Component
26178  * Bootstrap MenuItem class
26179  * @cfg {Boolean} submenu (true | false) default false
26180  * @cfg {String} html text of the item
26181  * @cfg {String} href the link
26182  * @cfg {Boolean} disable (true | false) default false
26183  * @cfg {Boolean} preventDefault (true | false) default true
26184  * @cfg {String} icon Font awesome icon
26185  * @cfg {String} pos Submenu align to (left | right) default right 
26186  * 
26187  * 
26188  * @constructor
26189  * Create a new Item
26190  * @param {Object} config The config object
26191  */
26192
26193
26194 Roo.bootstrap.menu.Item = function(config){
26195     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26196     this.addEvents({
26197         /**
26198          * @event mouseover
26199          * Fires when the mouse is hovering over this menu
26200          * @param {Roo.bootstrap.menu.Item} this
26201          * @param {Roo.EventObject} e
26202          */
26203         mouseover : true,
26204         /**
26205          * @event mouseout
26206          * Fires when the mouse exits this menu
26207          * @param {Roo.bootstrap.menu.Item} this
26208          * @param {Roo.EventObject} e
26209          */
26210         mouseout : true,
26211         // raw events
26212         /**
26213          * @event click
26214          * The raw click event for the entire grid.
26215          * @param {Roo.EventObject} e
26216          */
26217         click : true
26218     });
26219 };
26220
26221 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26222     
26223     submenu : false,
26224     href : '',
26225     html : '',
26226     preventDefault: true,
26227     disable : false,
26228     icon : false,
26229     pos : 'right',
26230     
26231     getAutoCreate : function()
26232     {
26233         var text = [
26234             {
26235                 tag : 'span',
26236                 cls : 'roo-menu-item-text',
26237                 html : this.html
26238             }
26239         ];
26240         
26241         if(this.icon){
26242             text.unshift({
26243                 tag : 'i',
26244                 cls : 'fa ' + this.icon
26245             })
26246         }
26247         
26248         var cfg = {
26249             tag : 'li',
26250             cn : [
26251                 {
26252                     tag : 'a',
26253                     href : this.href || '#',
26254                     cn : text
26255                 }
26256             ]
26257         };
26258         
26259         if(this.disable){
26260             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26261         }
26262         
26263         if(this.submenu){
26264             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26265             
26266             if(this.pos == 'left'){
26267                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26268             }
26269         }
26270         
26271         return cfg;
26272     },
26273     
26274     initEvents : function() 
26275     {
26276         this.el.on('mouseover', this.onMouseOver, this);
26277         this.el.on('mouseout', this.onMouseOut, this);
26278         
26279         this.el.select('a', true).first().on('click', this.onClick, this);
26280         
26281     },
26282     
26283     onClick : function(e)
26284     {
26285         if(this.preventDefault){
26286             e.preventDefault();
26287         }
26288         
26289         this.fireEvent("click", this, e);
26290     },
26291     
26292     onMouseOver : function(e)
26293     {
26294         if(this.submenu && this.pos == 'left'){
26295             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26296         }
26297         
26298         this.fireEvent("mouseover", this, e);
26299     },
26300     
26301     onMouseOut : function(e)
26302     {
26303         this.fireEvent("mouseout", this, e);
26304     }
26305 });
26306
26307  
26308
26309  /*
26310  * - LGPL
26311  *
26312  * menu separator
26313  * 
26314  */
26315 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26316
26317 /**
26318  * @class Roo.bootstrap.menu.Separator
26319  * @extends Roo.bootstrap.Component
26320  * Bootstrap Separator class
26321  * 
26322  * @constructor
26323  * Create a new Separator
26324  * @param {Object} config The config object
26325  */
26326
26327
26328 Roo.bootstrap.menu.Separator = function(config){
26329     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26330 };
26331
26332 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26333     
26334     getAutoCreate : function(){
26335         var cfg = {
26336             tag : 'li',
26337             cls: 'divider'
26338         };
26339         
26340         return cfg;
26341     }
26342    
26343 });
26344
26345  
26346
26347  /*
26348  * - LGPL
26349  *
26350  * Tooltip
26351  * 
26352  */
26353
26354 /**
26355  * @class Roo.bootstrap.Tooltip
26356  * Bootstrap Tooltip class
26357  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26358  * to determine which dom element triggers the tooltip.
26359  * 
26360  * It needs to add support for additional attributes like tooltip-position
26361  * 
26362  * @constructor
26363  * Create a new Toolti
26364  * @param {Object} config The config object
26365  */
26366
26367 Roo.bootstrap.Tooltip = function(config){
26368     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26369     
26370     this.alignment = Roo.bootstrap.Tooltip.alignment;
26371     
26372     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26373         this.alignment = config.alignment;
26374     }
26375     
26376 };
26377
26378 Roo.apply(Roo.bootstrap.Tooltip, {
26379     /**
26380      * @function init initialize tooltip monitoring.
26381      * @static
26382      */
26383     currentEl : false,
26384     currentTip : false,
26385     currentRegion : false,
26386     
26387     //  init : delay?
26388     
26389     init : function()
26390     {
26391         Roo.get(document).on('mouseover', this.enter ,this);
26392         Roo.get(document).on('mouseout', this.leave, this);
26393          
26394         
26395         this.currentTip = new Roo.bootstrap.Tooltip();
26396     },
26397     
26398     enter : function(ev)
26399     {
26400         var dom = ev.getTarget();
26401         
26402         //Roo.log(['enter',dom]);
26403         var el = Roo.fly(dom);
26404         if (this.currentEl) {
26405             //Roo.log(dom);
26406             //Roo.log(this.currentEl);
26407             //Roo.log(this.currentEl.contains(dom));
26408             if (this.currentEl == el) {
26409                 return;
26410             }
26411             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26412                 return;
26413             }
26414
26415         }
26416         
26417         if (this.currentTip.el) {
26418             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26419         }    
26420         //Roo.log(ev);
26421         
26422         if(!el || el.dom == document){
26423             return;
26424         }
26425         
26426         var bindEl = el;
26427         
26428         // you can not look for children, as if el is the body.. then everythign is the child..
26429         if (!el.attr('tooltip')) { //
26430             if (!el.select("[tooltip]").elements.length) {
26431                 return;
26432             }
26433             // is the mouse over this child...?
26434             bindEl = el.select("[tooltip]").first();
26435             var xy = ev.getXY();
26436             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26437                 //Roo.log("not in region.");
26438                 return;
26439             }
26440             //Roo.log("child element over..");
26441             
26442         }
26443         this.currentEl = bindEl;
26444         this.currentTip.bind(bindEl);
26445         this.currentRegion = Roo.lib.Region.getRegion(dom);
26446         this.currentTip.enter();
26447         
26448     },
26449     leave : function(ev)
26450     {
26451         var dom = ev.getTarget();
26452         //Roo.log(['leave',dom]);
26453         if (!this.currentEl) {
26454             return;
26455         }
26456         
26457         
26458         if (dom != this.currentEl.dom) {
26459             return;
26460         }
26461         var xy = ev.getXY();
26462         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26463             return;
26464         }
26465         // only activate leave if mouse cursor is outside... bounding box..
26466         
26467         
26468         
26469         
26470         if (this.currentTip) {
26471             this.currentTip.leave();
26472         }
26473         //Roo.log('clear currentEl');
26474         this.currentEl = false;
26475         
26476         
26477     },
26478     alignment : {
26479         'left' : ['r-l', [-2,0], 'right'],
26480         'right' : ['l-r', [2,0], 'left'],
26481         'bottom' : ['t-b', [0,2], 'top'],
26482         'top' : [ 'b-t', [0,-2], 'bottom']
26483     }
26484     
26485 });
26486
26487
26488 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26489     
26490     
26491     bindEl : false,
26492     
26493     delay : null, // can be { show : 300 , hide: 500}
26494     
26495     timeout : null,
26496     
26497     hoverState : null, //???
26498     
26499     placement : 'bottom', 
26500     
26501     alignment : false,
26502     
26503     getAutoCreate : function(){
26504     
26505         var cfg = {
26506            cls : 'tooltip',
26507            role : 'tooltip',
26508            cn : [
26509                 {
26510                     cls : 'tooltip-arrow'
26511                 },
26512                 {
26513                     cls : 'tooltip-inner'
26514                 }
26515            ]
26516         };
26517         
26518         return cfg;
26519     },
26520     bind : function(el)
26521     {
26522         this.bindEl = el;
26523     },
26524       
26525     
26526     enter : function () {
26527        
26528         if (this.timeout != null) {
26529             clearTimeout(this.timeout);
26530         }
26531         
26532         this.hoverState = 'in';
26533          //Roo.log("enter - show");
26534         if (!this.delay || !this.delay.show) {
26535             this.show();
26536             return;
26537         }
26538         var _t = this;
26539         this.timeout = setTimeout(function () {
26540             if (_t.hoverState == 'in') {
26541                 _t.show();
26542             }
26543         }, this.delay.show);
26544     },
26545     leave : function()
26546     {
26547         clearTimeout(this.timeout);
26548     
26549         this.hoverState = 'out';
26550          if (!this.delay || !this.delay.hide) {
26551             this.hide();
26552             return;
26553         }
26554        
26555         var _t = this;
26556         this.timeout = setTimeout(function () {
26557             //Roo.log("leave - timeout");
26558             
26559             if (_t.hoverState == 'out') {
26560                 _t.hide();
26561                 Roo.bootstrap.Tooltip.currentEl = false;
26562             }
26563         }, delay);
26564     },
26565     
26566     show : function (msg)
26567     {
26568         if (!this.el) {
26569             this.render(document.body);
26570         }
26571         // set content.
26572         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26573         
26574         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26575         
26576         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26577         
26578         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26579         
26580         var placement = typeof this.placement == 'function' ?
26581             this.placement.call(this, this.el, on_el) :
26582             this.placement;
26583             
26584         var autoToken = /\s?auto?\s?/i;
26585         var autoPlace = autoToken.test(placement);
26586         if (autoPlace) {
26587             placement = placement.replace(autoToken, '') || 'top';
26588         }
26589         
26590         //this.el.detach()
26591         //this.el.setXY([0,0]);
26592         this.el.show();
26593         //this.el.dom.style.display='block';
26594         
26595         //this.el.appendTo(on_el);
26596         
26597         var p = this.getPosition();
26598         var box = this.el.getBox();
26599         
26600         if (autoPlace) {
26601             // fixme..
26602         }
26603         
26604         var align = this.alignment[placement];
26605         
26606         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26607         
26608         if(placement == 'top' || placement == 'bottom'){
26609             if(xy[0] < 0){
26610                 placement = 'right';
26611             }
26612             
26613             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26614                 placement = 'left';
26615             }
26616             
26617             var scroll = Roo.select('body', true).first().getScroll();
26618             
26619             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26620                 placement = 'top';
26621             }
26622             
26623             align = this.alignment[placement];
26624         }
26625         
26626         this.el.alignTo(this.bindEl, align[0],align[1]);
26627         //var arrow = this.el.select('.arrow',true).first();
26628         //arrow.set(align[2], 
26629         
26630         this.el.addClass(placement);
26631         
26632         this.el.addClass('in fade');
26633         
26634         this.hoverState = null;
26635         
26636         if (this.el.hasClass('fade')) {
26637             // fade it?
26638         }
26639         
26640     },
26641     hide : function()
26642     {
26643          
26644         if (!this.el) {
26645             return;
26646         }
26647         //this.el.setXY([0,0]);
26648         this.el.removeClass('in');
26649         //this.el.hide();
26650         
26651     }
26652     
26653 });
26654  
26655
26656  /*
26657  * - LGPL
26658  *
26659  * Location Picker
26660  * 
26661  */
26662
26663 /**
26664  * @class Roo.bootstrap.LocationPicker
26665  * @extends Roo.bootstrap.Component
26666  * Bootstrap LocationPicker class
26667  * @cfg {Number} latitude Position when init default 0
26668  * @cfg {Number} longitude Position when init default 0
26669  * @cfg {Number} zoom default 15
26670  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26671  * @cfg {Boolean} mapTypeControl default false
26672  * @cfg {Boolean} disableDoubleClickZoom default false
26673  * @cfg {Boolean} scrollwheel default true
26674  * @cfg {Boolean} streetViewControl default false
26675  * @cfg {Number} radius default 0
26676  * @cfg {String} locationName
26677  * @cfg {Boolean} draggable default true
26678  * @cfg {Boolean} enableAutocomplete default false
26679  * @cfg {Boolean} enableReverseGeocode default true
26680  * @cfg {String} markerTitle
26681  * 
26682  * @constructor
26683  * Create a new LocationPicker
26684  * @param {Object} config The config object
26685  */
26686
26687
26688 Roo.bootstrap.LocationPicker = function(config){
26689     
26690     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26691     
26692     this.addEvents({
26693         /**
26694          * @event initial
26695          * Fires when the picker initialized.
26696          * @param {Roo.bootstrap.LocationPicker} this
26697          * @param {Google Location} location
26698          */
26699         initial : true,
26700         /**
26701          * @event positionchanged
26702          * Fires when the picker position changed.
26703          * @param {Roo.bootstrap.LocationPicker} this
26704          * @param {Google Location} location
26705          */
26706         positionchanged : true,
26707         /**
26708          * @event resize
26709          * Fires when the map resize.
26710          * @param {Roo.bootstrap.LocationPicker} this
26711          */
26712         resize : true,
26713         /**
26714          * @event show
26715          * Fires when the map show.
26716          * @param {Roo.bootstrap.LocationPicker} this
26717          */
26718         show : true,
26719         /**
26720          * @event hide
26721          * Fires when the map hide.
26722          * @param {Roo.bootstrap.LocationPicker} this
26723          */
26724         hide : true,
26725         /**
26726          * @event mapClick
26727          * Fires when click the map.
26728          * @param {Roo.bootstrap.LocationPicker} this
26729          * @param {Map event} e
26730          */
26731         mapClick : true,
26732         /**
26733          * @event mapRightClick
26734          * Fires when right click the map.
26735          * @param {Roo.bootstrap.LocationPicker} this
26736          * @param {Map event} e
26737          */
26738         mapRightClick : true,
26739         /**
26740          * @event markerClick
26741          * Fires when click the marker.
26742          * @param {Roo.bootstrap.LocationPicker} this
26743          * @param {Map event} e
26744          */
26745         markerClick : true,
26746         /**
26747          * @event markerRightClick
26748          * Fires when right click the marker.
26749          * @param {Roo.bootstrap.LocationPicker} this
26750          * @param {Map event} e
26751          */
26752         markerRightClick : true,
26753         /**
26754          * @event OverlayViewDraw
26755          * Fires when OverlayView Draw
26756          * @param {Roo.bootstrap.LocationPicker} this
26757          */
26758         OverlayViewDraw : true,
26759         /**
26760          * @event OverlayViewOnAdd
26761          * Fires when OverlayView Draw
26762          * @param {Roo.bootstrap.LocationPicker} this
26763          */
26764         OverlayViewOnAdd : true,
26765         /**
26766          * @event OverlayViewOnRemove
26767          * Fires when OverlayView Draw
26768          * @param {Roo.bootstrap.LocationPicker} this
26769          */
26770         OverlayViewOnRemove : true,
26771         /**
26772          * @event OverlayViewShow
26773          * Fires when OverlayView Draw
26774          * @param {Roo.bootstrap.LocationPicker} this
26775          * @param {Pixel} cpx
26776          */
26777         OverlayViewShow : true,
26778         /**
26779          * @event OverlayViewHide
26780          * Fires when OverlayView Draw
26781          * @param {Roo.bootstrap.LocationPicker} this
26782          */
26783         OverlayViewHide : true,
26784         /**
26785          * @event loadexception
26786          * Fires when load google lib failed.
26787          * @param {Roo.bootstrap.LocationPicker} this
26788          */
26789         loadexception : true
26790     });
26791         
26792 };
26793
26794 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26795     
26796     gMapContext: false,
26797     
26798     latitude: 0,
26799     longitude: 0,
26800     zoom: 15,
26801     mapTypeId: false,
26802     mapTypeControl: false,
26803     disableDoubleClickZoom: false,
26804     scrollwheel: true,
26805     streetViewControl: false,
26806     radius: 0,
26807     locationName: '',
26808     draggable: true,
26809     enableAutocomplete: false,
26810     enableReverseGeocode: true,
26811     markerTitle: '',
26812     
26813     getAutoCreate: function()
26814     {
26815
26816         var cfg = {
26817             tag: 'div',
26818             cls: 'roo-location-picker'
26819         };
26820         
26821         return cfg
26822     },
26823     
26824     initEvents: function(ct, position)
26825     {       
26826         if(!this.el.getWidth() || this.isApplied()){
26827             return;
26828         }
26829         
26830         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26831         
26832         this.initial();
26833     },
26834     
26835     initial: function()
26836     {
26837         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26838             this.fireEvent('loadexception', this);
26839             return;
26840         }
26841         
26842         if(!this.mapTypeId){
26843             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26844         }
26845         
26846         this.gMapContext = this.GMapContext();
26847         
26848         this.initOverlayView();
26849         
26850         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26851         
26852         var _this = this;
26853                 
26854         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26855             _this.setPosition(_this.gMapContext.marker.position);
26856         });
26857         
26858         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26859             _this.fireEvent('mapClick', this, event);
26860             
26861         });
26862
26863         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26864             _this.fireEvent('mapRightClick', this, event);
26865             
26866         });
26867         
26868         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26869             _this.fireEvent('markerClick', this, event);
26870             
26871         });
26872
26873         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26874             _this.fireEvent('markerRightClick', this, event);
26875             
26876         });
26877         
26878         this.setPosition(this.gMapContext.location);
26879         
26880         this.fireEvent('initial', this, this.gMapContext.location);
26881     },
26882     
26883     initOverlayView: function()
26884     {
26885         var _this = this;
26886         
26887         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26888             
26889             draw: function()
26890             {
26891                 _this.fireEvent('OverlayViewDraw', _this);
26892             },
26893             
26894             onAdd: function()
26895             {
26896                 _this.fireEvent('OverlayViewOnAdd', _this);
26897             },
26898             
26899             onRemove: function()
26900             {
26901                 _this.fireEvent('OverlayViewOnRemove', _this);
26902             },
26903             
26904             show: function(cpx)
26905             {
26906                 _this.fireEvent('OverlayViewShow', _this, cpx);
26907             },
26908             
26909             hide: function()
26910             {
26911                 _this.fireEvent('OverlayViewHide', _this);
26912             }
26913             
26914         });
26915     },
26916     
26917     fromLatLngToContainerPixel: function(event)
26918     {
26919         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26920     },
26921     
26922     isApplied: function() 
26923     {
26924         return this.getGmapContext() == false ? false : true;
26925     },
26926     
26927     getGmapContext: function() 
26928     {
26929         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26930     },
26931     
26932     GMapContext: function() 
26933     {
26934         var position = new google.maps.LatLng(this.latitude, this.longitude);
26935         
26936         var _map = new google.maps.Map(this.el.dom, {
26937             center: position,
26938             zoom: this.zoom,
26939             mapTypeId: this.mapTypeId,
26940             mapTypeControl: this.mapTypeControl,
26941             disableDoubleClickZoom: this.disableDoubleClickZoom,
26942             scrollwheel: this.scrollwheel,
26943             streetViewControl: this.streetViewControl,
26944             locationName: this.locationName,
26945             draggable: this.draggable,
26946             enableAutocomplete: this.enableAutocomplete,
26947             enableReverseGeocode: this.enableReverseGeocode
26948         });
26949         
26950         var _marker = new google.maps.Marker({
26951             position: position,
26952             map: _map,
26953             title: this.markerTitle,
26954             draggable: this.draggable
26955         });
26956         
26957         return {
26958             map: _map,
26959             marker: _marker,
26960             circle: null,
26961             location: position,
26962             radius: this.radius,
26963             locationName: this.locationName,
26964             addressComponents: {
26965                 formatted_address: null,
26966                 addressLine1: null,
26967                 addressLine2: null,
26968                 streetName: null,
26969                 streetNumber: null,
26970                 city: null,
26971                 district: null,
26972                 state: null,
26973                 stateOrProvince: null
26974             },
26975             settings: this,
26976             domContainer: this.el.dom,
26977             geodecoder: new google.maps.Geocoder()
26978         };
26979     },
26980     
26981     drawCircle: function(center, radius, options) 
26982     {
26983         if (this.gMapContext.circle != null) {
26984             this.gMapContext.circle.setMap(null);
26985         }
26986         if (radius > 0) {
26987             radius *= 1;
26988             options = Roo.apply({}, options, {
26989                 strokeColor: "#0000FF",
26990                 strokeOpacity: .35,
26991                 strokeWeight: 2,
26992                 fillColor: "#0000FF",
26993                 fillOpacity: .2
26994             });
26995             
26996             options.map = this.gMapContext.map;
26997             options.radius = radius;
26998             options.center = center;
26999             this.gMapContext.circle = new google.maps.Circle(options);
27000             return this.gMapContext.circle;
27001         }
27002         
27003         return null;
27004     },
27005     
27006     setPosition: function(location) 
27007     {
27008         this.gMapContext.location = location;
27009         this.gMapContext.marker.setPosition(location);
27010         this.gMapContext.map.panTo(location);
27011         this.drawCircle(location, this.gMapContext.radius, {});
27012         
27013         var _this = this;
27014         
27015         if (this.gMapContext.settings.enableReverseGeocode) {
27016             this.gMapContext.geodecoder.geocode({
27017                 latLng: this.gMapContext.location
27018             }, function(results, status) {
27019                 
27020                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27021                     _this.gMapContext.locationName = results[0].formatted_address;
27022                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27023                     
27024                     _this.fireEvent('positionchanged', this, location);
27025                 }
27026             });
27027             
27028             return;
27029         }
27030         
27031         this.fireEvent('positionchanged', this, location);
27032     },
27033     
27034     resize: function()
27035     {
27036         google.maps.event.trigger(this.gMapContext.map, "resize");
27037         
27038         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27039         
27040         this.fireEvent('resize', this);
27041     },
27042     
27043     setPositionByLatLng: function(latitude, longitude)
27044     {
27045         this.setPosition(new google.maps.LatLng(latitude, longitude));
27046     },
27047     
27048     getCurrentPosition: function() 
27049     {
27050         return {
27051             latitude: this.gMapContext.location.lat(),
27052             longitude: this.gMapContext.location.lng()
27053         };
27054     },
27055     
27056     getAddressName: function() 
27057     {
27058         return this.gMapContext.locationName;
27059     },
27060     
27061     getAddressComponents: function() 
27062     {
27063         return this.gMapContext.addressComponents;
27064     },
27065     
27066     address_component_from_google_geocode: function(address_components) 
27067     {
27068         var result = {};
27069         
27070         for (var i = 0; i < address_components.length; i++) {
27071             var component = address_components[i];
27072             if (component.types.indexOf("postal_code") >= 0) {
27073                 result.postalCode = component.short_name;
27074             } else if (component.types.indexOf("street_number") >= 0) {
27075                 result.streetNumber = component.short_name;
27076             } else if (component.types.indexOf("route") >= 0) {
27077                 result.streetName = component.short_name;
27078             } else if (component.types.indexOf("neighborhood") >= 0) {
27079                 result.city = component.short_name;
27080             } else if (component.types.indexOf("locality") >= 0) {
27081                 result.city = component.short_name;
27082             } else if (component.types.indexOf("sublocality") >= 0) {
27083                 result.district = component.short_name;
27084             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27085                 result.stateOrProvince = component.short_name;
27086             } else if (component.types.indexOf("country") >= 0) {
27087                 result.country = component.short_name;
27088             }
27089         }
27090         
27091         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27092         result.addressLine2 = "";
27093         return result;
27094     },
27095     
27096     setZoomLevel: function(zoom)
27097     {
27098         this.gMapContext.map.setZoom(zoom);
27099     },
27100     
27101     show: function()
27102     {
27103         if(!this.el){
27104             return;
27105         }
27106         
27107         this.el.show();
27108         
27109         this.resize();
27110         
27111         this.fireEvent('show', this);
27112     },
27113     
27114     hide: function()
27115     {
27116         if(!this.el){
27117             return;
27118         }
27119         
27120         this.el.hide();
27121         
27122         this.fireEvent('hide', this);
27123     }
27124     
27125 });
27126
27127 Roo.apply(Roo.bootstrap.LocationPicker, {
27128     
27129     OverlayView : function(map, options)
27130     {
27131         options = options || {};
27132         
27133         this.setMap(map);
27134     }
27135     
27136     
27137 });/*
27138  * - LGPL
27139  *
27140  * Alert
27141  * 
27142  */
27143
27144 /**
27145  * @class Roo.bootstrap.Alert
27146  * @extends Roo.bootstrap.Component
27147  * Bootstrap Alert class
27148  * @cfg {String} title The title of alert
27149  * @cfg {String} html The content of alert
27150  * @cfg {String} weight (  success | info | warning | danger )
27151  * @cfg {String} faicon font-awesomeicon
27152  * 
27153  * @constructor
27154  * Create a new alert
27155  * @param {Object} config The config object
27156  */
27157
27158
27159 Roo.bootstrap.Alert = function(config){
27160     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27161     
27162 };
27163
27164 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27165     
27166     title: '',
27167     html: '',
27168     weight: false,
27169     faicon: false,
27170     
27171     getAutoCreate : function()
27172     {
27173         
27174         var cfg = {
27175             tag : 'div',
27176             cls : 'alert',
27177             cn : [
27178                 {
27179                     tag : 'i',
27180                     cls : 'roo-alert-icon'
27181                     
27182                 },
27183                 {
27184                     tag : 'b',
27185                     cls : 'roo-alert-title',
27186                     html : this.title
27187                 },
27188                 {
27189                     tag : 'span',
27190                     cls : 'roo-alert-text',
27191                     html : this.html
27192                 }
27193             ]
27194         };
27195         
27196         if(this.faicon){
27197             cfg.cn[0].cls += ' fa ' + this.faicon;
27198         }
27199         
27200         if(this.weight){
27201             cfg.cls += ' alert-' + this.weight;
27202         }
27203         
27204         return cfg;
27205     },
27206     
27207     initEvents: function() 
27208     {
27209         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27210     },
27211     
27212     setTitle : function(str)
27213     {
27214         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27215     },
27216     
27217     setText : function(str)
27218     {
27219         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27220     },
27221     
27222     setWeight : function(weight)
27223     {
27224         if(this.weight){
27225             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27226         }
27227         
27228         this.weight = weight;
27229         
27230         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27231     },
27232     
27233     setIcon : function(icon)
27234     {
27235         if(this.faicon){
27236             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27237         }
27238         
27239         this.faicon = icon;
27240         
27241         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27242     },
27243     
27244     hide: function() 
27245     {
27246         this.el.hide();   
27247     },
27248     
27249     show: function() 
27250     {  
27251         this.el.show();   
27252     }
27253     
27254 });
27255
27256  
27257 /*
27258 * Licence: LGPL
27259 */
27260
27261 /**
27262  * @class Roo.bootstrap.UploadCropbox
27263  * @extends Roo.bootstrap.Component
27264  * Bootstrap UploadCropbox class
27265  * @cfg {String} emptyText show when image has been loaded
27266  * @cfg {String} rotateNotify show when image too small to rotate
27267  * @cfg {Number} errorTimeout default 3000
27268  * @cfg {Number} minWidth default 300
27269  * @cfg {Number} minHeight default 300
27270  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27271  * @cfg {Boolean} isDocument (true|false) default false
27272  * @cfg {String} url action url
27273  * @cfg {String} paramName default 'imageUpload'
27274  * @cfg {String} method default POST
27275  * @cfg {Boolean} loadMask (true|false) default true
27276  * @cfg {Boolean} loadingText default 'Loading...'
27277  * 
27278  * @constructor
27279  * Create a new UploadCropbox
27280  * @param {Object} config The config object
27281  */
27282
27283 Roo.bootstrap.UploadCropbox = function(config){
27284     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27285     
27286     this.addEvents({
27287         /**
27288          * @event beforeselectfile
27289          * Fire before select file
27290          * @param {Roo.bootstrap.UploadCropbox} this
27291          */
27292         "beforeselectfile" : true,
27293         /**
27294          * @event initial
27295          * Fire after initEvent
27296          * @param {Roo.bootstrap.UploadCropbox} this
27297          */
27298         "initial" : true,
27299         /**
27300          * @event crop
27301          * Fire after initEvent
27302          * @param {Roo.bootstrap.UploadCropbox} this
27303          * @param {String} data
27304          */
27305         "crop" : true,
27306         /**
27307          * @event prepare
27308          * Fire when preparing the file data
27309          * @param {Roo.bootstrap.UploadCropbox} this
27310          * @param {Object} file
27311          */
27312         "prepare" : true,
27313         /**
27314          * @event exception
27315          * Fire when get exception
27316          * @param {Roo.bootstrap.UploadCropbox} this
27317          * @param {XMLHttpRequest} xhr
27318          */
27319         "exception" : true,
27320         /**
27321          * @event beforeloadcanvas
27322          * Fire before load the canvas
27323          * @param {Roo.bootstrap.UploadCropbox} this
27324          * @param {String} src
27325          */
27326         "beforeloadcanvas" : true,
27327         /**
27328          * @event trash
27329          * Fire when trash image
27330          * @param {Roo.bootstrap.UploadCropbox} this
27331          */
27332         "trash" : true,
27333         /**
27334          * @event download
27335          * Fire when download the image
27336          * @param {Roo.bootstrap.UploadCropbox} this
27337          */
27338         "download" : true,
27339         /**
27340          * @event footerbuttonclick
27341          * Fire when footerbuttonclick
27342          * @param {Roo.bootstrap.UploadCropbox} this
27343          * @param {String} type
27344          */
27345         "footerbuttonclick" : true,
27346         /**
27347          * @event resize
27348          * Fire when resize
27349          * @param {Roo.bootstrap.UploadCropbox} this
27350          */
27351         "resize" : true,
27352         /**
27353          * @event rotate
27354          * Fire when rotate the image
27355          * @param {Roo.bootstrap.UploadCropbox} this
27356          * @param {String} pos
27357          */
27358         "rotate" : true,
27359         /**
27360          * @event inspect
27361          * Fire when inspect the file
27362          * @param {Roo.bootstrap.UploadCropbox} this
27363          * @param {Object} file
27364          */
27365         "inspect" : true,
27366         /**
27367          * @event upload
27368          * Fire when xhr upload the file
27369          * @param {Roo.bootstrap.UploadCropbox} this
27370          * @param {Object} data
27371          */
27372         "upload" : true,
27373         /**
27374          * @event arrange
27375          * Fire when arrange the file data
27376          * @param {Roo.bootstrap.UploadCropbox} this
27377          * @param {Object} formData
27378          */
27379         "arrange" : true
27380     });
27381     
27382     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27383 };
27384
27385 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27386     
27387     emptyText : 'Click to upload image',
27388     rotateNotify : 'Image is too small to rotate',
27389     errorTimeout : 3000,
27390     scale : 0,
27391     baseScale : 1,
27392     rotate : 0,
27393     dragable : false,
27394     pinching : false,
27395     mouseX : 0,
27396     mouseY : 0,
27397     cropData : false,
27398     minWidth : 300,
27399     minHeight : 300,
27400     file : false,
27401     exif : {},
27402     baseRotate : 1,
27403     cropType : 'image/jpeg',
27404     buttons : false,
27405     canvasLoaded : false,
27406     isDocument : false,
27407     method : 'POST',
27408     paramName : 'imageUpload',
27409     loadMask : true,
27410     loadingText : 'Loading...',
27411     maskEl : false,
27412     
27413     getAutoCreate : function()
27414     {
27415         var cfg = {
27416             tag : 'div',
27417             cls : 'roo-upload-cropbox',
27418             cn : [
27419                 {
27420                     tag : 'input',
27421                     cls : 'roo-upload-cropbox-selector',
27422                     type : 'file'
27423                 },
27424                 {
27425                     tag : 'div',
27426                     cls : 'roo-upload-cropbox-body',
27427                     style : 'cursor:pointer',
27428                     cn : [
27429                         {
27430                             tag : 'div',
27431                             cls : 'roo-upload-cropbox-preview'
27432                         },
27433                         {
27434                             tag : 'div',
27435                             cls : 'roo-upload-cropbox-thumb'
27436                         },
27437                         {
27438                             tag : 'div',
27439                             cls : 'roo-upload-cropbox-empty-notify',
27440                             html : this.emptyText
27441                         },
27442                         {
27443                             tag : 'div',
27444                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27445                             html : this.rotateNotify
27446                         }
27447                     ]
27448                 },
27449                 {
27450                     tag : 'div',
27451                     cls : 'roo-upload-cropbox-footer',
27452                     cn : {
27453                         tag : 'div',
27454                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27455                         cn : []
27456                     }
27457                 }
27458             ]
27459         };
27460         
27461         return cfg;
27462     },
27463     
27464     onRender : function(ct, position)
27465     {
27466         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27467         
27468         if (this.buttons.length) {
27469             
27470             Roo.each(this.buttons, function(bb) {
27471                 
27472                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27473                 
27474                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27475                 
27476             }, this);
27477         }
27478         
27479         if(this.loadMask){
27480             this.maskEl = this.el;
27481         }
27482     },
27483     
27484     initEvents : function()
27485     {
27486         this.urlAPI = (window.createObjectURL && window) || 
27487                                 (window.URL && URL.revokeObjectURL && URL) || 
27488                                 (window.webkitURL && webkitURL);
27489                         
27490         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27491         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27492         
27493         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27494         this.selectorEl.hide();
27495         
27496         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27497         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27498         
27499         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27500         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27501         this.thumbEl.hide();
27502         
27503         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27504         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27505         
27506         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27507         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27508         this.errorEl.hide();
27509         
27510         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27511         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27512         this.footerEl.hide();
27513         
27514         this.setThumbBoxSize();
27515         
27516         this.bind();
27517         
27518         this.resize();
27519         
27520         this.fireEvent('initial', this);
27521     },
27522
27523     bind : function()
27524     {
27525         var _this = this;
27526         
27527         window.addEventListener("resize", function() { _this.resize(); } );
27528         
27529         this.bodyEl.on('click', this.beforeSelectFile, this);
27530         
27531         if(Roo.isTouch){
27532             this.bodyEl.on('touchstart', this.onTouchStart, this);
27533             this.bodyEl.on('touchmove', this.onTouchMove, this);
27534             this.bodyEl.on('touchend', this.onTouchEnd, this);
27535         }
27536         
27537         if(!Roo.isTouch){
27538             this.bodyEl.on('mousedown', this.onMouseDown, this);
27539             this.bodyEl.on('mousemove', this.onMouseMove, this);
27540             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27541             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27542             Roo.get(document).on('mouseup', this.onMouseUp, this);
27543         }
27544         
27545         this.selectorEl.on('change', this.onFileSelected, this);
27546     },
27547     
27548     reset : function()
27549     {    
27550         this.scale = 0;
27551         this.baseScale = 1;
27552         this.rotate = 0;
27553         this.baseRotate = 1;
27554         this.dragable = false;
27555         this.pinching = false;
27556         this.mouseX = 0;
27557         this.mouseY = 0;
27558         this.cropData = false;
27559         this.notifyEl.dom.innerHTML = this.emptyText;
27560         
27561         this.selectorEl.dom.value = '';
27562         
27563     },
27564     
27565     resize : function()
27566     {
27567         if(this.fireEvent('resize', this) != false){
27568             this.setThumbBoxPosition();
27569             this.setCanvasPosition();
27570         }
27571     },
27572     
27573     onFooterButtonClick : function(e, el, o, type)
27574     {
27575         switch (type) {
27576             case 'rotate-left' :
27577                 this.onRotateLeft(e);
27578                 break;
27579             case 'rotate-right' :
27580                 this.onRotateRight(e);
27581                 break;
27582             case 'picture' :
27583                 this.beforeSelectFile(e);
27584                 break;
27585             case 'trash' :
27586                 this.trash(e);
27587                 break;
27588             case 'crop' :
27589                 this.crop(e);
27590                 break;
27591             case 'download' :
27592                 this.download(e);
27593                 break;
27594             default :
27595                 break;
27596         }
27597         
27598         this.fireEvent('footerbuttonclick', this, type);
27599     },
27600     
27601     beforeSelectFile : function(e)
27602     {
27603         e.preventDefault();
27604         
27605         if(this.fireEvent('beforeselectfile', this) != false){
27606             this.selectorEl.dom.click();
27607         }
27608     },
27609     
27610     onFileSelected : function(e)
27611     {
27612         e.preventDefault();
27613         
27614         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27615             return;
27616         }
27617         
27618         var file = this.selectorEl.dom.files[0];
27619         
27620         if(this.fireEvent('inspect', this, file) != false){
27621             this.prepare(file);
27622         }
27623         
27624     },
27625     
27626     trash : function(e)
27627     {
27628         this.fireEvent('trash', this);
27629     },
27630     
27631     download : function(e)
27632     {
27633         this.fireEvent('download', this);
27634     },
27635     
27636     loadCanvas : function(src)
27637     {   
27638         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27639             
27640             this.reset();
27641             
27642             this.imageEl = document.createElement('img');
27643             
27644             var _this = this;
27645             
27646             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27647             
27648             this.imageEl.src = src;
27649         }
27650     },
27651     
27652     onLoadCanvas : function()
27653     {   
27654         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27655         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27656         
27657         this.bodyEl.un('click', this.beforeSelectFile, this);
27658         
27659         this.notifyEl.hide();
27660         this.thumbEl.show();
27661         this.footerEl.show();
27662         
27663         this.baseRotateLevel();
27664         
27665         if(this.isDocument){
27666             this.setThumbBoxSize();
27667         }
27668         
27669         this.setThumbBoxPosition();
27670         
27671         this.baseScaleLevel();
27672         
27673         this.draw();
27674         
27675         this.resize();
27676         
27677         this.canvasLoaded = true;
27678         
27679         if(this.loadMask){
27680             this.maskEl.unmask();
27681         }
27682         
27683     },
27684     
27685     setCanvasPosition : function()
27686     {   
27687         if(!this.canvasEl){
27688             return;
27689         }
27690         
27691         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27692         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27693         
27694         this.previewEl.setLeft(pw);
27695         this.previewEl.setTop(ph);
27696         
27697     },
27698     
27699     onMouseDown : function(e)
27700     {   
27701         e.stopEvent();
27702         
27703         this.dragable = true;
27704         this.pinching = false;
27705         
27706         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27707             this.dragable = false;
27708             return;
27709         }
27710         
27711         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27712         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27713         
27714     },
27715     
27716     onMouseMove : function(e)
27717     {   
27718         e.stopEvent();
27719         
27720         if(!this.canvasLoaded){
27721             return;
27722         }
27723         
27724         if (!this.dragable){
27725             return;
27726         }
27727         
27728         var minX = Math.ceil(this.thumbEl.getLeft(true));
27729         var minY = Math.ceil(this.thumbEl.getTop(true));
27730         
27731         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27732         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27733         
27734         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27735         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27736         
27737         x = x - this.mouseX;
27738         y = y - this.mouseY;
27739         
27740         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27741         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27742         
27743         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27744         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27745         
27746         this.previewEl.setLeft(bgX);
27747         this.previewEl.setTop(bgY);
27748         
27749         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27750         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27751     },
27752     
27753     onMouseUp : function(e)
27754     {   
27755         e.stopEvent();
27756         
27757         this.dragable = false;
27758     },
27759     
27760     onMouseWheel : function(e)
27761     {   
27762         e.stopEvent();
27763         
27764         this.startScale = this.scale;
27765         
27766         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27767         
27768         if(!this.zoomable()){
27769             this.scale = this.startScale;
27770             return;
27771         }
27772         
27773         this.draw();
27774         
27775         return;
27776     },
27777     
27778     zoomable : function()
27779     {
27780         var minScale = this.thumbEl.getWidth() / this.minWidth;
27781         
27782         if(this.minWidth < this.minHeight){
27783             minScale = this.thumbEl.getHeight() / this.minHeight;
27784         }
27785         
27786         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27787         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27788         
27789         if(
27790                 this.isDocument &&
27791                 (this.rotate == 0 || this.rotate == 180) && 
27792                 (
27793                     width > this.imageEl.OriginWidth || 
27794                     height > this.imageEl.OriginHeight ||
27795                     (width < this.minWidth && height < this.minHeight)
27796                 )
27797         ){
27798             return false;
27799         }
27800         
27801         if(
27802                 this.isDocument &&
27803                 (this.rotate == 90 || this.rotate == 270) && 
27804                 (
27805                     width > this.imageEl.OriginWidth || 
27806                     height > this.imageEl.OriginHeight ||
27807                     (width < this.minHeight && height < this.minWidth)
27808                 )
27809         ){
27810             return false;
27811         }
27812         
27813         if(
27814                 !this.isDocument &&
27815                 (this.rotate == 0 || this.rotate == 180) && 
27816                 (
27817                     width < this.minWidth || 
27818                     width > this.imageEl.OriginWidth || 
27819                     height < this.minHeight || 
27820                     height > this.imageEl.OriginHeight
27821                 )
27822         ){
27823             return false;
27824         }
27825         
27826         if(
27827                 !this.isDocument &&
27828                 (this.rotate == 90 || this.rotate == 270) && 
27829                 (
27830                     width < this.minHeight || 
27831                     width > this.imageEl.OriginWidth || 
27832                     height < this.minWidth || 
27833                     height > this.imageEl.OriginHeight
27834                 )
27835         ){
27836             return false;
27837         }
27838         
27839         return true;
27840         
27841     },
27842     
27843     onRotateLeft : function(e)
27844     {   
27845         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27846             
27847             var minScale = this.thumbEl.getWidth() / this.minWidth;
27848             
27849             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27850             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27851             
27852             this.startScale = this.scale;
27853             
27854             while (this.getScaleLevel() < minScale){
27855             
27856                 this.scale = this.scale + 1;
27857                 
27858                 if(!this.zoomable()){
27859                     break;
27860                 }
27861                 
27862                 if(
27863                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27864                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27865                 ){
27866                     continue;
27867                 }
27868                 
27869                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27870
27871                 this.draw();
27872                 
27873                 return;
27874             }
27875             
27876             this.scale = this.startScale;
27877             
27878             this.onRotateFail();
27879             
27880             return false;
27881         }
27882         
27883         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27884
27885         if(this.isDocument){
27886             this.setThumbBoxSize();
27887             this.setThumbBoxPosition();
27888             this.setCanvasPosition();
27889         }
27890         
27891         this.draw();
27892         
27893         this.fireEvent('rotate', this, 'left');
27894         
27895     },
27896     
27897     onRotateRight : function(e)
27898     {
27899         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27900             
27901             var minScale = this.thumbEl.getWidth() / this.minWidth;
27902         
27903             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27904             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27905             
27906             this.startScale = this.scale;
27907             
27908             while (this.getScaleLevel() < minScale){
27909             
27910                 this.scale = this.scale + 1;
27911                 
27912                 if(!this.zoomable()){
27913                     break;
27914                 }
27915                 
27916                 if(
27917                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27918                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27919                 ){
27920                     continue;
27921                 }
27922                 
27923                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27924
27925                 this.draw();
27926                 
27927                 return;
27928             }
27929             
27930             this.scale = this.startScale;
27931             
27932             this.onRotateFail();
27933             
27934             return false;
27935         }
27936         
27937         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27938
27939         if(this.isDocument){
27940             this.setThumbBoxSize();
27941             this.setThumbBoxPosition();
27942             this.setCanvasPosition();
27943         }
27944         
27945         this.draw();
27946         
27947         this.fireEvent('rotate', this, 'right');
27948     },
27949     
27950     onRotateFail : function()
27951     {
27952         this.errorEl.show(true);
27953         
27954         var _this = this;
27955         
27956         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27957     },
27958     
27959     draw : function()
27960     {
27961         this.previewEl.dom.innerHTML = '';
27962         
27963         var canvasEl = document.createElement("canvas");
27964         
27965         var contextEl = canvasEl.getContext("2d");
27966         
27967         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27968         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27969         var center = this.imageEl.OriginWidth / 2;
27970         
27971         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27972             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27973             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27974             center = this.imageEl.OriginHeight / 2;
27975         }
27976         
27977         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27978         
27979         contextEl.translate(center, center);
27980         contextEl.rotate(this.rotate * Math.PI / 180);
27981
27982         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27983         
27984         this.canvasEl = document.createElement("canvas");
27985         
27986         this.contextEl = this.canvasEl.getContext("2d");
27987         
27988         switch (this.rotate) {
27989             case 0 :
27990                 
27991                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27992                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27993                 
27994                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27995                 
27996                 break;
27997             case 90 : 
27998                 
27999                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28000                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28001                 
28002                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28003                     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);
28004                     break;
28005                 }
28006                 
28007                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28008                 
28009                 break;
28010             case 180 :
28011                 
28012                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28013                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28014                 
28015                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28016                     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);
28017                     break;
28018                 }
28019                 
28020                 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);
28021                 
28022                 break;
28023             case 270 :
28024                 
28025                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28026                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28027         
28028                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28029                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28030                     break;
28031                 }
28032                 
28033                 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);
28034                 
28035                 break;
28036             default : 
28037                 break;
28038         }
28039         
28040         this.previewEl.appendChild(this.canvasEl);
28041         
28042         this.setCanvasPosition();
28043     },
28044     
28045     crop : function()
28046     {
28047         if(!this.canvasLoaded){
28048             return;
28049         }
28050         
28051         var imageCanvas = document.createElement("canvas");
28052         
28053         var imageContext = imageCanvas.getContext("2d");
28054         
28055         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28056         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28057         
28058         var center = imageCanvas.width / 2;
28059         
28060         imageContext.translate(center, center);
28061         
28062         imageContext.rotate(this.rotate * Math.PI / 180);
28063         
28064         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28065         
28066         var canvas = document.createElement("canvas");
28067         
28068         var context = canvas.getContext("2d");
28069                 
28070         canvas.width = this.minWidth;
28071         canvas.height = this.minHeight;
28072
28073         switch (this.rotate) {
28074             case 0 :
28075                 
28076                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28077                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28078                 
28079                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28080                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28081                 
28082                 var targetWidth = this.minWidth - 2 * x;
28083                 var targetHeight = this.minHeight - 2 * y;
28084                 
28085                 var scale = 1;
28086                 
28087                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28088                     scale = targetWidth / width;
28089                 }
28090                 
28091                 if(x > 0 && y == 0){
28092                     scale = targetHeight / height;
28093                 }
28094                 
28095                 if(x > 0 && y > 0){
28096                     scale = targetWidth / width;
28097                     
28098                     if(width < height){
28099                         scale = targetHeight / height;
28100                     }
28101                 }
28102                 
28103                 context.scale(scale, scale);
28104                 
28105                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28106                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28107
28108                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28109                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28110
28111                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28112                 
28113                 break;
28114             case 90 : 
28115                 
28116                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28117                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28118                 
28119                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28120                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28121                 
28122                 var targetWidth = this.minWidth - 2 * x;
28123                 var targetHeight = this.minHeight - 2 * y;
28124                 
28125                 var scale = 1;
28126                 
28127                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28128                     scale = targetWidth / width;
28129                 }
28130                 
28131                 if(x > 0 && y == 0){
28132                     scale = targetHeight / height;
28133                 }
28134                 
28135                 if(x > 0 && y > 0){
28136                     scale = targetWidth / width;
28137                     
28138                     if(width < height){
28139                         scale = targetHeight / height;
28140                     }
28141                 }
28142                 
28143                 context.scale(scale, scale);
28144                 
28145                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28146                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28147
28148                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28149                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28150                 
28151                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28152                 
28153                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28154                 
28155                 break;
28156             case 180 :
28157                 
28158                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28159                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28160                 
28161                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28162                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28163                 
28164                 var targetWidth = this.minWidth - 2 * x;
28165                 var targetHeight = this.minHeight - 2 * y;
28166                 
28167                 var scale = 1;
28168                 
28169                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28170                     scale = targetWidth / width;
28171                 }
28172                 
28173                 if(x > 0 && y == 0){
28174                     scale = targetHeight / height;
28175                 }
28176                 
28177                 if(x > 0 && y > 0){
28178                     scale = targetWidth / width;
28179                     
28180                     if(width < height){
28181                         scale = targetHeight / height;
28182                     }
28183                 }
28184                 
28185                 context.scale(scale, scale);
28186                 
28187                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28188                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28189
28190                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28191                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28192
28193                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28194                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28195                 
28196                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28197                 
28198                 break;
28199             case 270 :
28200                 
28201                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28202                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28203                 
28204                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28205                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28206                 
28207                 var targetWidth = this.minWidth - 2 * x;
28208                 var targetHeight = this.minHeight - 2 * y;
28209                 
28210                 var scale = 1;
28211                 
28212                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28213                     scale = targetWidth / width;
28214                 }
28215                 
28216                 if(x > 0 && y == 0){
28217                     scale = targetHeight / height;
28218                 }
28219                 
28220                 if(x > 0 && y > 0){
28221                     scale = targetWidth / width;
28222                     
28223                     if(width < height){
28224                         scale = targetHeight / height;
28225                     }
28226                 }
28227                 
28228                 context.scale(scale, scale);
28229                 
28230                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28231                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28232
28233                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28234                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28235                 
28236                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28237                 
28238                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28239                 
28240                 break;
28241             default : 
28242                 break;
28243         }
28244         
28245         this.cropData = canvas.toDataURL(this.cropType);
28246         
28247         if(this.fireEvent('crop', this, this.cropData) !== false){
28248             this.process(this.file, this.cropData);
28249         }
28250         
28251         return;
28252         
28253     },
28254     
28255     setThumbBoxSize : function()
28256     {
28257         var width, height;
28258         
28259         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28260             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28261             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28262             
28263             this.minWidth = width;
28264             this.minHeight = height;
28265             
28266             if(this.rotate == 90 || this.rotate == 270){
28267                 this.minWidth = height;
28268                 this.minHeight = width;
28269             }
28270         }
28271         
28272         height = 300;
28273         width = Math.ceil(this.minWidth * height / this.minHeight);
28274         
28275         if(this.minWidth > this.minHeight){
28276             width = 300;
28277             height = Math.ceil(this.minHeight * width / this.minWidth);
28278         }
28279         
28280         this.thumbEl.setStyle({
28281             width : width + 'px',
28282             height : height + 'px'
28283         });
28284
28285         return;
28286             
28287     },
28288     
28289     setThumbBoxPosition : function()
28290     {
28291         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28292         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28293         
28294         this.thumbEl.setLeft(x);
28295         this.thumbEl.setTop(y);
28296         
28297     },
28298     
28299     baseRotateLevel : function()
28300     {
28301         this.baseRotate = 1;
28302         
28303         if(
28304                 typeof(this.exif) != 'undefined' &&
28305                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28306                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28307         ){
28308             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28309         }
28310         
28311         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28312         
28313     },
28314     
28315     baseScaleLevel : function()
28316     {
28317         var width, height;
28318         
28319         if(this.isDocument){
28320             
28321             if(this.baseRotate == 6 || this.baseRotate == 8){
28322             
28323                 height = this.thumbEl.getHeight();
28324                 this.baseScale = height / this.imageEl.OriginWidth;
28325
28326                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28327                     width = this.thumbEl.getWidth();
28328                     this.baseScale = width / this.imageEl.OriginHeight;
28329                 }
28330
28331                 return;
28332             }
28333
28334             height = this.thumbEl.getHeight();
28335             this.baseScale = height / this.imageEl.OriginHeight;
28336
28337             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28338                 width = this.thumbEl.getWidth();
28339                 this.baseScale = width / this.imageEl.OriginWidth;
28340             }
28341
28342             return;
28343         }
28344         
28345         if(this.baseRotate == 6 || this.baseRotate == 8){
28346             
28347             width = this.thumbEl.getHeight();
28348             this.baseScale = width / this.imageEl.OriginHeight;
28349             
28350             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28351                 height = this.thumbEl.getWidth();
28352                 this.baseScale = height / this.imageEl.OriginHeight;
28353             }
28354             
28355             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28356                 height = this.thumbEl.getWidth();
28357                 this.baseScale = height / this.imageEl.OriginHeight;
28358                 
28359                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28360                     width = this.thumbEl.getHeight();
28361                     this.baseScale = width / this.imageEl.OriginWidth;
28362                 }
28363             }
28364             
28365             return;
28366         }
28367         
28368         width = this.thumbEl.getWidth();
28369         this.baseScale = width / this.imageEl.OriginWidth;
28370         
28371         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28372             height = this.thumbEl.getHeight();
28373             this.baseScale = height / this.imageEl.OriginHeight;
28374         }
28375         
28376         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28377             
28378             height = this.thumbEl.getHeight();
28379             this.baseScale = height / this.imageEl.OriginHeight;
28380             
28381             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28382                 width = this.thumbEl.getWidth();
28383                 this.baseScale = width / this.imageEl.OriginWidth;
28384             }
28385             
28386         }
28387         
28388         return;
28389     },
28390     
28391     getScaleLevel : function()
28392     {
28393         return this.baseScale * Math.pow(1.1, this.scale);
28394     },
28395     
28396     onTouchStart : function(e)
28397     {
28398         if(!this.canvasLoaded){
28399             this.beforeSelectFile(e);
28400             return;
28401         }
28402         
28403         var touches = e.browserEvent.touches;
28404         
28405         if(!touches){
28406             return;
28407         }
28408         
28409         if(touches.length == 1){
28410             this.onMouseDown(e);
28411             return;
28412         }
28413         
28414         if(touches.length != 2){
28415             return;
28416         }
28417         
28418         var coords = [];
28419         
28420         for(var i = 0, finger; finger = touches[i]; i++){
28421             coords.push(finger.pageX, finger.pageY);
28422         }
28423         
28424         var x = Math.pow(coords[0] - coords[2], 2);
28425         var y = Math.pow(coords[1] - coords[3], 2);
28426         
28427         this.startDistance = Math.sqrt(x + y);
28428         
28429         this.startScale = this.scale;
28430         
28431         this.pinching = true;
28432         this.dragable = false;
28433         
28434     },
28435     
28436     onTouchMove : function(e)
28437     {
28438         if(!this.pinching && !this.dragable){
28439             return;
28440         }
28441         
28442         var touches = e.browserEvent.touches;
28443         
28444         if(!touches){
28445             return;
28446         }
28447         
28448         if(this.dragable){
28449             this.onMouseMove(e);
28450             return;
28451         }
28452         
28453         var coords = [];
28454         
28455         for(var i = 0, finger; finger = touches[i]; i++){
28456             coords.push(finger.pageX, finger.pageY);
28457         }
28458         
28459         var x = Math.pow(coords[0] - coords[2], 2);
28460         var y = Math.pow(coords[1] - coords[3], 2);
28461         
28462         this.endDistance = Math.sqrt(x + y);
28463         
28464         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28465         
28466         if(!this.zoomable()){
28467             this.scale = this.startScale;
28468             return;
28469         }
28470         
28471         this.draw();
28472         
28473     },
28474     
28475     onTouchEnd : function(e)
28476     {
28477         this.pinching = false;
28478         this.dragable = false;
28479         
28480     },
28481     
28482     process : function(file, crop)
28483     {
28484         if(this.loadMask){
28485             this.maskEl.mask(this.loadingText);
28486         }
28487         
28488         this.xhr = new XMLHttpRequest();
28489         
28490         file.xhr = this.xhr;
28491
28492         this.xhr.open(this.method, this.url, true);
28493         
28494         var headers = {
28495             "Accept": "application/json",
28496             "Cache-Control": "no-cache",
28497             "X-Requested-With": "XMLHttpRequest"
28498         };
28499         
28500         for (var headerName in headers) {
28501             var headerValue = headers[headerName];
28502             if (headerValue) {
28503                 this.xhr.setRequestHeader(headerName, headerValue);
28504             }
28505         }
28506         
28507         var _this = this;
28508         
28509         this.xhr.onload = function()
28510         {
28511             _this.xhrOnLoad(_this.xhr);
28512         }
28513         
28514         this.xhr.onerror = function()
28515         {
28516             _this.xhrOnError(_this.xhr);
28517         }
28518         
28519         var formData = new FormData();
28520
28521         formData.append('returnHTML', 'NO');
28522         
28523         if(crop){
28524             formData.append('crop', crop);
28525         }
28526         
28527         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28528             formData.append(this.paramName, file, file.name);
28529         }
28530         
28531         if(typeof(file.filename) != 'undefined'){
28532             formData.append('filename', file.filename);
28533         }
28534         
28535         if(typeof(file.mimetype) != 'undefined'){
28536             formData.append('mimetype', file.mimetype);
28537         }
28538         
28539         if(this.fireEvent('arrange', this, formData) != false){
28540             this.xhr.send(formData);
28541         };
28542     },
28543     
28544     xhrOnLoad : function(xhr)
28545     {
28546         if(this.loadMask){
28547             this.maskEl.unmask();
28548         }
28549         
28550         if (xhr.readyState !== 4) {
28551             this.fireEvent('exception', this, xhr);
28552             return;
28553         }
28554
28555         var response = Roo.decode(xhr.responseText);
28556         
28557         if(!response.success){
28558             this.fireEvent('exception', this, xhr);
28559             return;
28560         }
28561         
28562         var response = Roo.decode(xhr.responseText);
28563         
28564         this.fireEvent('upload', this, response);
28565         
28566     },
28567     
28568     xhrOnError : function()
28569     {
28570         if(this.loadMask){
28571             this.maskEl.unmask();
28572         }
28573         
28574         Roo.log('xhr on error');
28575         
28576         var response = Roo.decode(xhr.responseText);
28577           
28578         Roo.log(response);
28579         
28580     },
28581     
28582     prepare : function(file)
28583     {   
28584         if(this.loadMask){
28585             this.maskEl.mask(this.loadingText);
28586         }
28587         
28588         this.file = false;
28589         this.exif = {};
28590         
28591         if(typeof(file) === 'string'){
28592             this.loadCanvas(file);
28593             return;
28594         }
28595         
28596         if(!file || !this.urlAPI){
28597             return;
28598         }
28599         
28600         this.file = file;
28601         this.cropType = file.type;
28602         
28603         var _this = this;
28604         
28605         if(this.fireEvent('prepare', this, this.file) != false){
28606             
28607             var reader = new FileReader();
28608             
28609             reader.onload = function (e) {
28610                 if (e.target.error) {
28611                     Roo.log(e.target.error);
28612                     return;
28613                 }
28614                 
28615                 var buffer = e.target.result,
28616                     dataView = new DataView(buffer),
28617                     offset = 2,
28618                     maxOffset = dataView.byteLength - 4,
28619                     markerBytes,
28620                     markerLength;
28621                 
28622                 if (dataView.getUint16(0) === 0xffd8) {
28623                     while (offset < maxOffset) {
28624                         markerBytes = dataView.getUint16(offset);
28625                         
28626                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28627                             markerLength = dataView.getUint16(offset + 2) + 2;
28628                             if (offset + markerLength > dataView.byteLength) {
28629                                 Roo.log('Invalid meta data: Invalid segment size.');
28630                                 break;
28631                             }
28632                             
28633                             if(markerBytes == 0xffe1){
28634                                 _this.parseExifData(
28635                                     dataView,
28636                                     offset,
28637                                     markerLength
28638                                 );
28639                             }
28640                             
28641                             offset += markerLength;
28642                             
28643                             continue;
28644                         }
28645                         
28646                         break;
28647                     }
28648                     
28649                 }
28650                 
28651                 var url = _this.urlAPI.createObjectURL(_this.file);
28652                 
28653                 _this.loadCanvas(url);
28654                 
28655                 return;
28656             }
28657             
28658             reader.readAsArrayBuffer(this.file);
28659             
28660         }
28661         
28662     },
28663     
28664     parseExifData : function(dataView, offset, length)
28665     {
28666         var tiffOffset = offset + 10,
28667             littleEndian,
28668             dirOffset;
28669     
28670         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28671             // No Exif data, might be XMP data instead
28672             return;
28673         }
28674         
28675         // Check for the ASCII code for "Exif" (0x45786966):
28676         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28677             // No Exif data, might be XMP data instead
28678             return;
28679         }
28680         if (tiffOffset + 8 > dataView.byteLength) {
28681             Roo.log('Invalid Exif data: Invalid segment size.');
28682             return;
28683         }
28684         // Check for the two null bytes:
28685         if (dataView.getUint16(offset + 8) !== 0x0000) {
28686             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28687             return;
28688         }
28689         // Check the byte alignment:
28690         switch (dataView.getUint16(tiffOffset)) {
28691         case 0x4949:
28692             littleEndian = true;
28693             break;
28694         case 0x4D4D:
28695             littleEndian = false;
28696             break;
28697         default:
28698             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28699             return;
28700         }
28701         // Check for the TIFF tag marker (0x002A):
28702         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28703             Roo.log('Invalid Exif data: Missing TIFF marker.');
28704             return;
28705         }
28706         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28707         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28708         
28709         this.parseExifTags(
28710             dataView,
28711             tiffOffset,
28712             tiffOffset + dirOffset,
28713             littleEndian
28714         );
28715     },
28716     
28717     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28718     {
28719         var tagsNumber,
28720             dirEndOffset,
28721             i;
28722         if (dirOffset + 6 > dataView.byteLength) {
28723             Roo.log('Invalid Exif data: Invalid directory offset.');
28724             return;
28725         }
28726         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28727         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28728         if (dirEndOffset + 4 > dataView.byteLength) {
28729             Roo.log('Invalid Exif data: Invalid directory size.');
28730             return;
28731         }
28732         for (i = 0; i < tagsNumber; i += 1) {
28733             this.parseExifTag(
28734                 dataView,
28735                 tiffOffset,
28736                 dirOffset + 2 + 12 * i, // tag offset
28737                 littleEndian
28738             );
28739         }
28740         // Return the offset to the next directory:
28741         return dataView.getUint32(dirEndOffset, littleEndian);
28742     },
28743     
28744     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28745     {
28746         var tag = dataView.getUint16(offset, littleEndian);
28747         
28748         this.exif[tag] = this.getExifValue(
28749             dataView,
28750             tiffOffset,
28751             offset,
28752             dataView.getUint16(offset + 2, littleEndian), // tag type
28753             dataView.getUint32(offset + 4, littleEndian), // tag length
28754             littleEndian
28755         );
28756     },
28757     
28758     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28759     {
28760         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28761             tagSize,
28762             dataOffset,
28763             values,
28764             i,
28765             str,
28766             c;
28767     
28768         if (!tagType) {
28769             Roo.log('Invalid Exif data: Invalid tag type.');
28770             return;
28771         }
28772         
28773         tagSize = tagType.size * length;
28774         // Determine if the value is contained in the dataOffset bytes,
28775         // or if the value at the dataOffset is a pointer to the actual data:
28776         dataOffset = tagSize > 4 ?
28777                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28778         if (dataOffset + tagSize > dataView.byteLength) {
28779             Roo.log('Invalid Exif data: Invalid data offset.');
28780             return;
28781         }
28782         if (length === 1) {
28783             return tagType.getValue(dataView, dataOffset, littleEndian);
28784         }
28785         values = [];
28786         for (i = 0; i < length; i += 1) {
28787             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28788         }
28789         
28790         if (tagType.ascii) {
28791             str = '';
28792             // Concatenate the chars:
28793             for (i = 0; i < values.length; i += 1) {
28794                 c = values[i];
28795                 // Ignore the terminating NULL byte(s):
28796                 if (c === '\u0000') {
28797                     break;
28798                 }
28799                 str += c;
28800             }
28801             return str;
28802         }
28803         return values;
28804     }
28805     
28806 });
28807
28808 Roo.apply(Roo.bootstrap.UploadCropbox, {
28809     tags : {
28810         'Orientation': 0x0112
28811     },
28812     
28813     Orientation: {
28814             1: 0, //'top-left',
28815 //            2: 'top-right',
28816             3: 180, //'bottom-right',
28817 //            4: 'bottom-left',
28818 //            5: 'left-top',
28819             6: 90, //'right-top',
28820 //            7: 'right-bottom',
28821             8: 270 //'left-bottom'
28822     },
28823     
28824     exifTagTypes : {
28825         // byte, 8-bit unsigned int:
28826         1: {
28827             getValue: function (dataView, dataOffset) {
28828                 return dataView.getUint8(dataOffset);
28829             },
28830             size: 1
28831         },
28832         // ascii, 8-bit byte:
28833         2: {
28834             getValue: function (dataView, dataOffset) {
28835                 return String.fromCharCode(dataView.getUint8(dataOffset));
28836             },
28837             size: 1,
28838             ascii: true
28839         },
28840         // short, 16 bit int:
28841         3: {
28842             getValue: function (dataView, dataOffset, littleEndian) {
28843                 return dataView.getUint16(dataOffset, littleEndian);
28844             },
28845             size: 2
28846         },
28847         // long, 32 bit int:
28848         4: {
28849             getValue: function (dataView, dataOffset, littleEndian) {
28850                 return dataView.getUint32(dataOffset, littleEndian);
28851             },
28852             size: 4
28853         },
28854         // rational = two long values, first is numerator, second is denominator:
28855         5: {
28856             getValue: function (dataView, dataOffset, littleEndian) {
28857                 return dataView.getUint32(dataOffset, littleEndian) /
28858                     dataView.getUint32(dataOffset + 4, littleEndian);
28859             },
28860             size: 8
28861         },
28862         // slong, 32 bit signed int:
28863         9: {
28864             getValue: function (dataView, dataOffset, littleEndian) {
28865                 return dataView.getInt32(dataOffset, littleEndian);
28866             },
28867             size: 4
28868         },
28869         // srational, two slongs, first is numerator, second is denominator:
28870         10: {
28871             getValue: function (dataView, dataOffset, littleEndian) {
28872                 return dataView.getInt32(dataOffset, littleEndian) /
28873                     dataView.getInt32(dataOffset + 4, littleEndian);
28874             },
28875             size: 8
28876         }
28877     },
28878     
28879     footer : {
28880         STANDARD : [
28881             {
28882                 tag : 'div',
28883                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28884                 action : 'rotate-left',
28885                 cn : [
28886                     {
28887                         tag : 'button',
28888                         cls : 'btn btn-default',
28889                         html : '<i class="fa fa-undo"></i>'
28890                     }
28891                 ]
28892             },
28893             {
28894                 tag : 'div',
28895                 cls : 'btn-group roo-upload-cropbox-picture',
28896                 action : 'picture',
28897                 cn : [
28898                     {
28899                         tag : 'button',
28900                         cls : 'btn btn-default',
28901                         html : '<i class="fa fa-picture-o"></i>'
28902                     }
28903                 ]
28904             },
28905             {
28906                 tag : 'div',
28907                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28908                 action : 'rotate-right',
28909                 cn : [
28910                     {
28911                         tag : 'button',
28912                         cls : 'btn btn-default',
28913                         html : '<i class="fa fa-repeat"></i>'
28914                     }
28915                 ]
28916             }
28917         ],
28918         DOCUMENT : [
28919             {
28920                 tag : 'div',
28921                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28922                 action : 'rotate-left',
28923                 cn : [
28924                     {
28925                         tag : 'button',
28926                         cls : 'btn btn-default',
28927                         html : '<i class="fa fa-undo"></i>'
28928                     }
28929                 ]
28930             },
28931             {
28932                 tag : 'div',
28933                 cls : 'btn-group roo-upload-cropbox-download',
28934                 action : 'download',
28935                 cn : [
28936                     {
28937                         tag : 'button',
28938                         cls : 'btn btn-default',
28939                         html : '<i class="fa fa-download"></i>'
28940                     }
28941                 ]
28942             },
28943             {
28944                 tag : 'div',
28945                 cls : 'btn-group roo-upload-cropbox-crop',
28946                 action : 'crop',
28947                 cn : [
28948                     {
28949                         tag : 'button',
28950                         cls : 'btn btn-default',
28951                         html : '<i class="fa fa-crop"></i>'
28952                     }
28953                 ]
28954             },
28955             {
28956                 tag : 'div',
28957                 cls : 'btn-group roo-upload-cropbox-trash',
28958                 action : 'trash',
28959                 cn : [
28960                     {
28961                         tag : 'button',
28962                         cls : 'btn btn-default',
28963                         html : '<i class="fa fa-trash"></i>'
28964                     }
28965                 ]
28966             },
28967             {
28968                 tag : 'div',
28969                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28970                 action : 'rotate-right',
28971                 cn : [
28972                     {
28973                         tag : 'button',
28974                         cls : 'btn btn-default',
28975                         html : '<i class="fa fa-repeat"></i>'
28976                     }
28977                 ]
28978             }
28979         ],
28980         ROTATOR : [
28981             {
28982                 tag : 'div',
28983                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28984                 action : 'rotate-left',
28985                 cn : [
28986                     {
28987                         tag : 'button',
28988                         cls : 'btn btn-default',
28989                         html : '<i class="fa fa-undo"></i>'
28990                     }
28991                 ]
28992             },
28993             {
28994                 tag : 'div',
28995                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28996                 action : 'rotate-right',
28997                 cn : [
28998                     {
28999                         tag : 'button',
29000                         cls : 'btn btn-default',
29001                         html : '<i class="fa fa-repeat"></i>'
29002                     }
29003                 ]
29004             }
29005         ]
29006     }
29007 });
29008
29009 /*
29010 * Licence: LGPL
29011 */
29012
29013 /**
29014  * @class Roo.bootstrap.DocumentManager
29015  * @extends Roo.bootstrap.Component
29016  * Bootstrap DocumentManager class
29017  * @cfg {String} paramName default 'imageUpload'
29018  * @cfg {String} toolTipName default 'filename'
29019  * @cfg {String} method default POST
29020  * @cfg {String} url action url
29021  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29022  * @cfg {Boolean} multiple multiple upload default true
29023  * @cfg {Number} thumbSize default 300
29024  * @cfg {String} fieldLabel
29025  * @cfg {Number} labelWidth default 4
29026  * @cfg {String} labelAlign (left|top) default left
29027  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29028 * @cfg {Number} labellg set the width of label (1-12)
29029  * @cfg {Number} labelmd set the width of label (1-12)
29030  * @cfg {Number} labelsm set the width of label (1-12)
29031  * @cfg {Number} labelxs set the width of label (1-12)
29032  * 
29033  * @constructor
29034  * Create a new DocumentManager
29035  * @param {Object} config The config object
29036  */
29037
29038 Roo.bootstrap.DocumentManager = function(config){
29039     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29040     
29041     this.files = [];
29042     this.delegates = [];
29043     
29044     this.addEvents({
29045         /**
29046          * @event initial
29047          * Fire when initial the DocumentManager
29048          * @param {Roo.bootstrap.DocumentManager} this
29049          */
29050         "initial" : true,
29051         /**
29052          * @event inspect
29053          * inspect selected file
29054          * @param {Roo.bootstrap.DocumentManager} this
29055          * @param {File} file
29056          */
29057         "inspect" : true,
29058         /**
29059          * @event exception
29060          * Fire when xhr load exception
29061          * @param {Roo.bootstrap.DocumentManager} this
29062          * @param {XMLHttpRequest} xhr
29063          */
29064         "exception" : true,
29065         /**
29066          * @event afterupload
29067          * Fire when xhr load exception
29068          * @param {Roo.bootstrap.DocumentManager} this
29069          * @param {XMLHttpRequest} xhr
29070          */
29071         "afterupload" : true,
29072         /**
29073          * @event prepare
29074          * prepare the form data
29075          * @param {Roo.bootstrap.DocumentManager} this
29076          * @param {Object} formData
29077          */
29078         "prepare" : true,
29079         /**
29080          * @event remove
29081          * Fire when remove the file
29082          * @param {Roo.bootstrap.DocumentManager} this
29083          * @param {Object} file
29084          */
29085         "remove" : true,
29086         /**
29087          * @event refresh
29088          * Fire after refresh the file
29089          * @param {Roo.bootstrap.DocumentManager} this
29090          */
29091         "refresh" : true,
29092         /**
29093          * @event click
29094          * Fire after click the image
29095          * @param {Roo.bootstrap.DocumentManager} this
29096          * @param {Object} file
29097          */
29098         "click" : true,
29099         /**
29100          * @event edit
29101          * Fire when upload a image and editable set to true
29102          * @param {Roo.bootstrap.DocumentManager} this
29103          * @param {Object} file
29104          */
29105         "edit" : true,
29106         /**
29107          * @event beforeselectfile
29108          * Fire before select file
29109          * @param {Roo.bootstrap.DocumentManager} this
29110          */
29111         "beforeselectfile" : true,
29112         /**
29113          * @event process
29114          * Fire before process file
29115          * @param {Roo.bootstrap.DocumentManager} this
29116          * @param {Object} file
29117          */
29118         "process" : true,
29119         /**
29120          * @event previewrendered
29121          * Fire when preview rendered
29122          * @param {Roo.bootstrap.DocumentManager} this
29123          * @param {Object} file
29124          */
29125         "previewrendered" : true,
29126         /**
29127          */
29128         "previewResize" : true
29129         
29130     });
29131 };
29132
29133 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29134     
29135     boxes : 0,
29136     inputName : '',
29137     thumbSize : 300,
29138     multiple : true,
29139     files : false,
29140     method : 'POST',
29141     url : '',
29142     paramName : 'imageUpload',
29143     toolTipName : 'filename',
29144     fieldLabel : '',
29145     labelWidth : 4,
29146     labelAlign : 'left',
29147     editable : true,
29148     delegates : false,
29149     xhr : false, 
29150     
29151     labellg : 0,
29152     labelmd : 0,
29153     labelsm : 0,
29154     labelxs : 0,
29155     
29156     getAutoCreate : function()
29157     {   
29158         var managerWidget = {
29159             tag : 'div',
29160             cls : 'roo-document-manager',
29161             cn : [
29162                 {
29163                     tag : 'input',
29164                     cls : 'roo-document-manager-selector',
29165                     type : 'file'
29166                 },
29167                 {
29168                     tag : 'div',
29169                     cls : 'roo-document-manager-uploader',
29170                     cn : [
29171                         {
29172                             tag : 'div',
29173                             cls : 'roo-document-manager-upload-btn',
29174                             html : '<i class="fa fa-plus"></i>'
29175                         }
29176                     ]
29177                     
29178                 }
29179             ]
29180         };
29181         
29182         var content = [
29183             {
29184                 tag : 'div',
29185                 cls : 'column col-md-12',
29186                 cn : managerWidget
29187             }
29188         ];
29189         
29190         if(this.fieldLabel.length){
29191             
29192             content = [
29193                 {
29194                     tag : 'div',
29195                     cls : 'column col-md-12',
29196                     html : this.fieldLabel
29197                 },
29198                 {
29199                     tag : 'div',
29200                     cls : 'column col-md-12',
29201                     cn : managerWidget
29202                 }
29203             ];
29204
29205             if(this.labelAlign == 'left'){
29206                 content = [
29207                     {
29208                         tag : 'div',
29209                         cls : 'column',
29210                         html : this.fieldLabel
29211                     },
29212                     {
29213                         tag : 'div',
29214                         cls : 'column',
29215                         cn : managerWidget
29216                     }
29217                 ];
29218                 
29219                 if(this.labelWidth > 12){
29220                     content[0].style = "width: " + this.labelWidth + 'px';
29221                 }
29222
29223                 if(this.labelWidth < 13 && this.labelmd == 0){
29224                     this.labelmd = this.labelWidth;
29225                 }
29226
29227                 if(this.labellg > 0){
29228                     content[0].cls += ' col-lg-' + this.labellg;
29229                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29230                 }
29231
29232                 if(this.labelmd > 0){
29233                     content[0].cls += ' col-md-' + this.labelmd;
29234                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29235                 }
29236
29237                 if(this.labelsm > 0){
29238                     content[0].cls += ' col-sm-' + this.labelsm;
29239                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29240                 }
29241
29242                 if(this.labelxs > 0){
29243                     content[0].cls += ' col-xs-' + this.labelxs;
29244                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29245                 }
29246                 
29247             }
29248         }
29249         
29250         var cfg = {
29251             tag : 'div',
29252             cls : 'row clearfix',
29253             cn : content
29254         };
29255         
29256         return cfg;
29257         
29258     },
29259     
29260     initEvents : function()
29261     {
29262         this.managerEl = this.el.select('.roo-document-manager', true).first();
29263         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29264         
29265         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29266         this.selectorEl.hide();
29267         
29268         if(this.multiple){
29269             this.selectorEl.attr('multiple', 'multiple');
29270         }
29271         
29272         this.selectorEl.on('change', this.onFileSelected, this);
29273         
29274         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29275         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29276         
29277         this.uploader.on('click', this.onUploaderClick, this);
29278         
29279         this.renderProgressDialog();
29280         
29281         var _this = this;
29282         
29283         window.addEventListener("resize", function() { _this.refresh(); } );
29284         
29285         this.fireEvent('initial', this);
29286     },
29287     
29288     renderProgressDialog : function()
29289     {
29290         var _this = this;
29291         
29292         this.progressDialog = new Roo.bootstrap.Modal({
29293             cls : 'roo-document-manager-progress-dialog',
29294             allow_close : false,
29295             animate : false,
29296             title : '',
29297             buttons : [
29298                 {
29299                     name  :'cancel',
29300                     weight : 'danger',
29301                     html : 'Cancel'
29302                 }
29303             ], 
29304             listeners : { 
29305                 btnclick : function() {
29306                     _this.uploadCancel();
29307                     this.hide();
29308                 }
29309             }
29310         });
29311          
29312         this.progressDialog.render(Roo.get(document.body));
29313          
29314         this.progress = new Roo.bootstrap.Progress({
29315             cls : 'roo-document-manager-progress',
29316             active : true,
29317             striped : true
29318         });
29319         
29320         this.progress.render(this.progressDialog.getChildContainer());
29321         
29322         this.progressBar = new Roo.bootstrap.ProgressBar({
29323             cls : 'roo-document-manager-progress-bar',
29324             aria_valuenow : 0,
29325             aria_valuemin : 0,
29326             aria_valuemax : 12,
29327             panel : 'success'
29328         });
29329         
29330         this.progressBar.render(this.progress.getChildContainer());
29331     },
29332     
29333     onUploaderClick : function(e)
29334     {
29335         e.preventDefault();
29336      
29337         if(this.fireEvent('beforeselectfile', this) != false){
29338             this.selectorEl.dom.click();
29339         }
29340         
29341     },
29342     
29343     onFileSelected : function(e)
29344     {
29345         e.preventDefault();
29346         
29347         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29348             return;
29349         }
29350         
29351         Roo.each(this.selectorEl.dom.files, function(file){
29352             if(this.fireEvent('inspect', this, file) != false){
29353                 this.files.push(file);
29354             }
29355         }, this);
29356         
29357         this.queue();
29358         
29359     },
29360     
29361     queue : function()
29362     {
29363         this.selectorEl.dom.value = '';
29364         
29365         if(!this.files || !this.files.length){
29366             return;
29367         }
29368         
29369         if(this.boxes > 0 && this.files.length > this.boxes){
29370             this.files = this.files.slice(0, this.boxes);
29371         }
29372         
29373         this.uploader.show();
29374         
29375         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29376             this.uploader.hide();
29377         }
29378         
29379         var _this = this;
29380         
29381         var files = [];
29382         
29383         var docs = [];
29384         
29385         Roo.each(this.files, function(file){
29386             
29387             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29388                 var f = this.renderPreview(file);
29389                 files.push(f);
29390                 return;
29391             }
29392             
29393             if(file.type.indexOf('image') != -1){
29394                 this.delegates.push(
29395                     (function(){
29396                         _this.process(file);
29397                     }).createDelegate(this)
29398                 );
29399         
29400                 return;
29401             }
29402             
29403             docs.push(
29404                 (function(){
29405                     _this.process(file);
29406                 }).createDelegate(this)
29407             );
29408             
29409         }, this);
29410         
29411         this.files = files;
29412         
29413         this.delegates = this.delegates.concat(docs);
29414         
29415         if(!this.delegates.length){
29416             this.refresh();
29417             return;
29418         }
29419         
29420         this.progressBar.aria_valuemax = this.delegates.length;
29421         
29422         this.arrange();
29423         
29424         return;
29425     },
29426     
29427     arrange : function()
29428     {
29429         if(!this.delegates.length){
29430             this.progressDialog.hide();
29431             this.refresh();
29432             return;
29433         }
29434         
29435         var delegate = this.delegates.shift();
29436         
29437         this.progressDialog.show();
29438         
29439         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29440         
29441         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29442         
29443         delegate();
29444     },
29445     
29446     refresh : function()
29447     {
29448         this.uploader.show();
29449         
29450         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29451             this.uploader.hide();
29452         }
29453         
29454         Roo.isTouch ? this.closable(false) : this.closable(true);
29455         
29456         this.fireEvent('refresh', this);
29457     },
29458     
29459     onRemove : function(e, el, o)
29460     {
29461         e.preventDefault();
29462         
29463         this.fireEvent('remove', this, o);
29464         
29465     },
29466     
29467     remove : function(o)
29468     {
29469         var files = [];
29470         
29471         Roo.each(this.files, function(file){
29472             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29473                 files.push(file);
29474                 return;
29475             }
29476
29477             o.target.remove();
29478
29479         }, this);
29480         
29481         this.files = files;
29482         
29483         this.refresh();
29484     },
29485     
29486     clear : function()
29487     {
29488         Roo.each(this.files, function(file){
29489             if(!file.target){
29490                 return;
29491             }
29492             
29493             file.target.remove();
29494
29495         }, this);
29496         
29497         this.files = [];
29498         
29499         this.refresh();
29500     },
29501     
29502     onClick : function(e, el, o)
29503     {
29504         e.preventDefault();
29505         
29506         this.fireEvent('click', this, o);
29507         
29508     },
29509     
29510     closable : function(closable)
29511     {
29512         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29513             
29514             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29515             
29516             if(closable){
29517                 el.show();
29518                 return;
29519             }
29520             
29521             el.hide();
29522             
29523         }, this);
29524     },
29525     
29526     xhrOnLoad : function(xhr)
29527     {
29528         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29529             el.remove();
29530         }, this);
29531         
29532         if (xhr.readyState !== 4) {
29533             this.arrange();
29534             this.fireEvent('exception', this, xhr);
29535             return;
29536         }
29537
29538         var response = Roo.decode(xhr.responseText);
29539         
29540         if(!response.success){
29541             this.arrange();
29542             this.fireEvent('exception', this, xhr);
29543             return;
29544         }
29545         
29546         var file = this.renderPreview(response.data);
29547         
29548         this.files.push(file);
29549         
29550         this.arrange();
29551         
29552         this.fireEvent('afterupload', this, xhr);
29553         
29554     },
29555     
29556     xhrOnError : function(xhr)
29557     {
29558         Roo.log('xhr on error');
29559         
29560         var response = Roo.decode(xhr.responseText);
29561           
29562         Roo.log(response);
29563         
29564         this.arrange();
29565     },
29566     
29567     process : function(file)
29568     {
29569         if(this.fireEvent('process', this, file) !== false){
29570             if(this.editable && file.type.indexOf('image') != -1){
29571                 this.fireEvent('edit', this, file);
29572                 return;
29573             }
29574
29575             this.uploadStart(file, false);
29576
29577             return;
29578         }
29579         
29580     },
29581     
29582     uploadStart : function(file, crop)
29583     {
29584         this.xhr = new XMLHttpRequest();
29585         
29586         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29587             this.arrange();
29588             return;
29589         }
29590         
29591         file.xhr = this.xhr;
29592             
29593         this.managerEl.createChild({
29594             tag : 'div',
29595             cls : 'roo-document-manager-loading',
29596             cn : [
29597                 {
29598                     tag : 'div',
29599                     tooltip : file.name,
29600                     cls : 'roo-document-manager-thumb',
29601                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29602                 }
29603             ]
29604
29605         });
29606
29607         this.xhr.open(this.method, this.url, true);
29608         
29609         var headers = {
29610             "Accept": "application/json",
29611             "Cache-Control": "no-cache",
29612             "X-Requested-With": "XMLHttpRequest"
29613         };
29614         
29615         for (var headerName in headers) {
29616             var headerValue = headers[headerName];
29617             if (headerValue) {
29618                 this.xhr.setRequestHeader(headerName, headerValue);
29619             }
29620         }
29621         
29622         var _this = this;
29623         
29624         this.xhr.onload = function()
29625         {
29626             _this.xhrOnLoad(_this.xhr);
29627         }
29628         
29629         this.xhr.onerror = function()
29630         {
29631             _this.xhrOnError(_this.xhr);
29632         }
29633         
29634         var formData = new FormData();
29635
29636         formData.append('returnHTML', 'NO');
29637         
29638         if(crop){
29639             formData.append('crop', crop);
29640         }
29641         
29642         formData.append(this.paramName, file, file.name);
29643         
29644         var options = {
29645             file : file, 
29646             manually : false
29647         };
29648         
29649         if(this.fireEvent('prepare', this, formData, options) != false){
29650             
29651             if(options.manually){
29652                 return;
29653             }
29654             
29655             this.xhr.send(formData);
29656             return;
29657         };
29658         
29659         this.uploadCancel();
29660     },
29661     
29662     uploadCancel : function()
29663     {
29664         if (this.xhr) {
29665             this.xhr.abort();
29666         }
29667         
29668         this.delegates = [];
29669         
29670         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29671             el.remove();
29672         }, this);
29673         
29674         this.arrange();
29675     },
29676     
29677     renderPreview : function(file)
29678     {
29679         if(typeof(file.target) != 'undefined' && file.target){
29680             return file;
29681         }
29682         
29683         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29684         
29685         var previewEl = this.managerEl.createChild({
29686             tag : 'div',
29687             cls : 'roo-document-manager-preview',
29688             cn : [
29689                 {
29690                     tag : 'div',
29691                     tooltip : file[this.toolTipName],
29692                     cls : 'roo-document-manager-thumb',
29693                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29694                 },
29695                 {
29696                     tag : 'button',
29697                     cls : 'close',
29698                     html : '<i class="fa fa-times-circle"></i>'
29699                 }
29700             ]
29701         });
29702
29703         var close = previewEl.select('button.close', true).first();
29704
29705         close.on('click', this.onRemove, this, file);
29706
29707         file.target = previewEl;
29708
29709         var image = previewEl.select('img', true).first();
29710         
29711         var _this = this;
29712         
29713         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29714         
29715         image.on('click', this.onClick, this, file);
29716         
29717         this.fireEvent('previewrendered', this, file);
29718         
29719         return file;
29720         
29721     },
29722     
29723     onPreviewLoad : function(file, image)
29724     {
29725         if(typeof(file.target) == 'undefined' || !file.target){
29726             return;
29727         }
29728         
29729         var width = image.dom.naturalWidth || image.dom.width;
29730         var height = image.dom.naturalHeight || image.dom.height;
29731         
29732         if(!this.previewResize) {
29733             return;
29734         }
29735         
29736         if(width > height){
29737             file.target.addClass('wide');
29738             return;
29739         }
29740         
29741         file.target.addClass('tall');
29742         return;
29743         
29744     },
29745     
29746     uploadFromSource : function(file, crop)
29747     {
29748         this.xhr = new XMLHttpRequest();
29749         
29750         this.managerEl.createChild({
29751             tag : 'div',
29752             cls : 'roo-document-manager-loading',
29753             cn : [
29754                 {
29755                     tag : 'div',
29756                     tooltip : file.name,
29757                     cls : 'roo-document-manager-thumb',
29758                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29759                 }
29760             ]
29761
29762         });
29763
29764         this.xhr.open(this.method, this.url, true);
29765         
29766         var headers = {
29767             "Accept": "application/json",
29768             "Cache-Control": "no-cache",
29769             "X-Requested-With": "XMLHttpRequest"
29770         };
29771         
29772         for (var headerName in headers) {
29773             var headerValue = headers[headerName];
29774             if (headerValue) {
29775                 this.xhr.setRequestHeader(headerName, headerValue);
29776             }
29777         }
29778         
29779         var _this = this;
29780         
29781         this.xhr.onload = function()
29782         {
29783             _this.xhrOnLoad(_this.xhr);
29784         }
29785         
29786         this.xhr.onerror = function()
29787         {
29788             _this.xhrOnError(_this.xhr);
29789         }
29790         
29791         var formData = new FormData();
29792
29793         formData.append('returnHTML', 'NO');
29794         
29795         formData.append('crop', crop);
29796         
29797         if(typeof(file.filename) != 'undefined'){
29798             formData.append('filename', file.filename);
29799         }
29800         
29801         if(typeof(file.mimetype) != 'undefined'){
29802             formData.append('mimetype', file.mimetype);
29803         }
29804         
29805         Roo.log(formData);
29806         
29807         if(this.fireEvent('prepare', this, formData) != false){
29808             this.xhr.send(formData);
29809         };
29810     }
29811 });
29812
29813 /*
29814 * Licence: LGPL
29815 */
29816
29817 /**
29818  * @class Roo.bootstrap.DocumentViewer
29819  * @extends Roo.bootstrap.Component
29820  * Bootstrap DocumentViewer class
29821  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29822  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29823  * 
29824  * @constructor
29825  * Create a new DocumentViewer
29826  * @param {Object} config The config object
29827  */
29828
29829 Roo.bootstrap.DocumentViewer = function(config){
29830     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29831     
29832     this.addEvents({
29833         /**
29834          * @event initial
29835          * Fire after initEvent
29836          * @param {Roo.bootstrap.DocumentViewer} this
29837          */
29838         "initial" : true,
29839         /**
29840          * @event click
29841          * Fire after click
29842          * @param {Roo.bootstrap.DocumentViewer} this
29843          */
29844         "click" : true,
29845         /**
29846          * @event download
29847          * Fire after download button
29848          * @param {Roo.bootstrap.DocumentViewer} this
29849          */
29850         "download" : true,
29851         /**
29852          * @event trash
29853          * Fire after trash button
29854          * @param {Roo.bootstrap.DocumentViewer} this
29855          */
29856         "trash" : true
29857         
29858     });
29859 };
29860
29861 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29862     
29863     showDownload : true,
29864     
29865     showTrash : true,
29866     
29867     getAutoCreate : function()
29868     {
29869         var cfg = {
29870             tag : 'div',
29871             cls : 'roo-document-viewer',
29872             cn : [
29873                 {
29874                     tag : 'div',
29875                     cls : 'roo-document-viewer-body',
29876                     cn : [
29877                         {
29878                             tag : 'div',
29879                             cls : 'roo-document-viewer-thumb',
29880                             cn : [
29881                                 {
29882                                     tag : 'img',
29883                                     cls : 'roo-document-viewer-image'
29884                                 }
29885                             ]
29886                         }
29887                     ]
29888                 },
29889                 {
29890                     tag : 'div',
29891                     cls : 'roo-document-viewer-footer',
29892                     cn : {
29893                         tag : 'div',
29894                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29895                         cn : [
29896                             {
29897                                 tag : 'div',
29898                                 cls : 'btn-group roo-document-viewer-download',
29899                                 cn : [
29900                                     {
29901                                         tag : 'button',
29902                                         cls : 'btn btn-default',
29903                                         html : '<i class="fa fa-download"></i>'
29904                                     }
29905                                 ]
29906                             },
29907                             {
29908                                 tag : 'div',
29909                                 cls : 'btn-group roo-document-viewer-trash',
29910                                 cn : [
29911                                     {
29912                                         tag : 'button',
29913                                         cls : 'btn btn-default',
29914                                         html : '<i class="fa fa-trash"></i>'
29915                                     }
29916                                 ]
29917                             }
29918                         ]
29919                     }
29920                 }
29921             ]
29922         };
29923         
29924         return cfg;
29925     },
29926     
29927     initEvents : function()
29928     {
29929         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29930         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29931         
29932         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29933         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29934         
29935         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29936         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29937         
29938         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29939         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29940         
29941         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29942         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29943         
29944         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29945         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29946         
29947         this.bodyEl.on('click', this.onClick, this);
29948         this.downloadBtn.on('click', this.onDownload, this);
29949         this.trashBtn.on('click', this.onTrash, this);
29950         
29951         this.downloadBtn.hide();
29952         this.trashBtn.hide();
29953         
29954         if(this.showDownload){
29955             this.downloadBtn.show();
29956         }
29957         
29958         if(this.showTrash){
29959             this.trashBtn.show();
29960         }
29961         
29962         if(!this.showDownload && !this.showTrash) {
29963             this.footerEl.hide();
29964         }
29965         
29966     },
29967     
29968     initial : function()
29969     {
29970         this.fireEvent('initial', this);
29971         
29972     },
29973     
29974     onClick : function(e)
29975     {
29976         e.preventDefault();
29977         
29978         this.fireEvent('click', this);
29979     },
29980     
29981     onDownload : function(e)
29982     {
29983         e.preventDefault();
29984         
29985         this.fireEvent('download', this);
29986     },
29987     
29988     onTrash : function(e)
29989     {
29990         e.preventDefault();
29991         
29992         this.fireEvent('trash', this);
29993     }
29994     
29995 });
29996 /*
29997  * - LGPL
29998  *
29999  * nav progress bar
30000  * 
30001  */
30002
30003 /**
30004  * @class Roo.bootstrap.NavProgressBar
30005  * @extends Roo.bootstrap.Component
30006  * Bootstrap NavProgressBar class
30007  * 
30008  * @constructor
30009  * Create a new nav progress bar
30010  * @param {Object} config The config object
30011  */
30012
30013 Roo.bootstrap.NavProgressBar = function(config){
30014     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30015
30016     this.bullets = this.bullets || [];
30017    
30018 //    Roo.bootstrap.NavProgressBar.register(this);
30019      this.addEvents({
30020         /**
30021              * @event changed
30022              * Fires when the active item changes
30023              * @param {Roo.bootstrap.NavProgressBar} this
30024              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30025              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
30026          */
30027         'changed': true
30028      });
30029     
30030 };
30031
30032 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
30033     
30034     bullets : [],
30035     barItems : [],
30036     
30037     getAutoCreate : function()
30038     {
30039         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30040         
30041         cfg = {
30042             tag : 'div',
30043             cls : 'roo-navigation-bar-group',
30044             cn : [
30045                 {
30046                     tag : 'div',
30047                     cls : 'roo-navigation-top-bar'
30048                 },
30049                 {
30050                     tag : 'div',
30051                     cls : 'roo-navigation-bullets-bar',
30052                     cn : [
30053                         {
30054                             tag : 'ul',
30055                             cls : 'roo-navigation-bar'
30056                         }
30057                     ]
30058                 },
30059                 
30060                 {
30061                     tag : 'div',
30062                     cls : 'roo-navigation-bottom-bar'
30063                 }
30064             ]
30065             
30066         };
30067         
30068         return cfg;
30069         
30070     },
30071     
30072     initEvents: function() 
30073     {
30074         
30075     },
30076     
30077     onRender : function(ct, position) 
30078     {
30079         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30080         
30081         if(this.bullets.length){
30082             Roo.each(this.bullets, function(b){
30083                this.addItem(b);
30084             }, this);
30085         }
30086         
30087         this.format();
30088         
30089     },
30090     
30091     addItem : function(cfg)
30092     {
30093         var item = new Roo.bootstrap.NavProgressItem(cfg);
30094         
30095         item.parentId = this.id;
30096         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30097         
30098         if(cfg.html){
30099             var top = new Roo.bootstrap.Element({
30100                 tag : 'div',
30101                 cls : 'roo-navigation-bar-text'
30102             });
30103             
30104             var bottom = new Roo.bootstrap.Element({
30105                 tag : 'div',
30106                 cls : 'roo-navigation-bar-text'
30107             });
30108             
30109             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30110             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30111             
30112             var topText = new Roo.bootstrap.Element({
30113                 tag : 'span',
30114                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30115             });
30116             
30117             var bottomText = new Roo.bootstrap.Element({
30118                 tag : 'span',
30119                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30120             });
30121             
30122             topText.onRender(top.el, null);
30123             bottomText.onRender(bottom.el, null);
30124             
30125             item.topEl = top;
30126             item.bottomEl = bottom;
30127         }
30128         
30129         this.barItems.push(item);
30130         
30131         return item;
30132     },
30133     
30134     getActive : function()
30135     {
30136         var active = false;
30137         
30138         Roo.each(this.barItems, function(v){
30139             
30140             if (!v.isActive()) {
30141                 return;
30142             }
30143             
30144             active = v;
30145             return false;
30146             
30147         });
30148         
30149         return active;
30150     },
30151     
30152     setActiveItem : function(item)
30153     {
30154         var prev = false;
30155         
30156         Roo.each(this.barItems, function(v){
30157             if (v.rid == item.rid) {
30158                 return ;
30159             }
30160             
30161             if (v.isActive()) {
30162                 v.setActive(false);
30163                 prev = v;
30164             }
30165         });
30166
30167         item.setActive(true);
30168         
30169         this.fireEvent('changed', this, item, prev);
30170     },
30171     
30172     getBarItem: function(rid)
30173     {
30174         var ret = false;
30175         
30176         Roo.each(this.barItems, function(e) {
30177             if (e.rid != rid) {
30178                 return;
30179             }
30180             
30181             ret =  e;
30182             return false;
30183         });
30184         
30185         return ret;
30186     },
30187     
30188     indexOfItem : function(item)
30189     {
30190         var index = false;
30191         
30192         Roo.each(this.barItems, function(v, i){
30193             
30194             if (v.rid != item.rid) {
30195                 return;
30196             }
30197             
30198             index = i;
30199             return false
30200         });
30201         
30202         return index;
30203     },
30204     
30205     setActiveNext : function()
30206     {
30207         var i = this.indexOfItem(this.getActive());
30208         
30209         if (i > this.barItems.length) {
30210             return;
30211         }
30212         
30213         this.setActiveItem(this.barItems[i+1]);
30214     },
30215     
30216     setActivePrev : function()
30217     {
30218         var i = this.indexOfItem(this.getActive());
30219         
30220         if (i  < 1) {
30221             return;
30222         }
30223         
30224         this.setActiveItem(this.barItems[i-1]);
30225     },
30226     
30227     format : function()
30228     {
30229         if(!this.barItems.length){
30230             return;
30231         }
30232      
30233         var width = 100 / this.barItems.length;
30234         
30235         Roo.each(this.barItems, function(i){
30236             i.el.setStyle('width', width + '%');
30237             i.topEl.el.setStyle('width', width + '%');
30238             i.bottomEl.el.setStyle('width', width + '%');
30239         }, this);
30240         
30241     }
30242     
30243 });
30244 /*
30245  * - LGPL
30246  *
30247  * Nav Progress Item
30248  * 
30249  */
30250
30251 /**
30252  * @class Roo.bootstrap.NavProgressItem
30253  * @extends Roo.bootstrap.Component
30254  * Bootstrap NavProgressItem class
30255  * @cfg {String} rid the reference id
30256  * @cfg {Boolean} active (true|false) Is item active default false
30257  * @cfg {Boolean} disabled (true|false) Is item active default false
30258  * @cfg {String} html
30259  * @cfg {String} position (top|bottom) text position default bottom
30260  * @cfg {String} icon show icon instead of number
30261  * 
30262  * @constructor
30263  * Create a new NavProgressItem
30264  * @param {Object} config The config object
30265  */
30266 Roo.bootstrap.NavProgressItem = function(config){
30267     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30268     this.addEvents({
30269         // raw events
30270         /**
30271          * @event click
30272          * The raw click event for the entire grid.
30273          * @param {Roo.bootstrap.NavProgressItem} this
30274          * @param {Roo.EventObject} e
30275          */
30276         "click" : true
30277     });
30278    
30279 };
30280
30281 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30282     
30283     rid : '',
30284     active : false,
30285     disabled : false,
30286     html : '',
30287     position : 'bottom',
30288     icon : false,
30289     
30290     getAutoCreate : function()
30291     {
30292         var iconCls = 'roo-navigation-bar-item-icon';
30293         
30294         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30295         
30296         var cfg = {
30297             tag: 'li',
30298             cls: 'roo-navigation-bar-item',
30299             cn : [
30300                 {
30301                     tag : 'i',
30302                     cls : iconCls
30303                 }
30304             ]
30305         };
30306         
30307         if(this.active){
30308             cfg.cls += ' active';
30309         }
30310         if(this.disabled){
30311             cfg.cls += ' disabled';
30312         }
30313         
30314         return cfg;
30315     },
30316     
30317     disable : function()
30318     {
30319         this.setDisabled(true);
30320     },
30321     
30322     enable : function()
30323     {
30324         this.setDisabled(false);
30325     },
30326     
30327     initEvents: function() 
30328     {
30329         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30330         
30331         this.iconEl.on('click', this.onClick, this);
30332     },
30333     
30334     onClick : function(e)
30335     {
30336         e.preventDefault();
30337         
30338         if(this.disabled){
30339             return;
30340         }
30341         
30342         if(this.fireEvent('click', this, e) === false){
30343             return;
30344         };
30345         
30346         this.parent().setActiveItem(this);
30347     },
30348     
30349     isActive: function () 
30350     {
30351         return this.active;
30352     },
30353     
30354     setActive : function(state)
30355     {
30356         if(this.active == state){
30357             return;
30358         }
30359         
30360         this.active = state;
30361         
30362         if (state) {
30363             this.el.addClass('active');
30364             return;
30365         }
30366         
30367         this.el.removeClass('active');
30368         
30369         return;
30370     },
30371     
30372     setDisabled : function(state)
30373     {
30374         if(this.disabled == state){
30375             return;
30376         }
30377         
30378         this.disabled = state;
30379         
30380         if (state) {
30381             this.el.addClass('disabled');
30382             return;
30383         }
30384         
30385         this.el.removeClass('disabled');
30386     },
30387     
30388     tooltipEl : function()
30389     {
30390         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30391     }
30392 });
30393  
30394
30395  /*
30396  * - LGPL
30397  *
30398  * FieldLabel
30399  * 
30400  */
30401
30402 /**
30403  * @class Roo.bootstrap.FieldLabel
30404  * @extends Roo.bootstrap.Component
30405  * Bootstrap FieldLabel class
30406  * @cfg {String} html contents of the element
30407  * @cfg {String} tag tag of the element default label
30408  * @cfg {String} cls class of the element
30409  * @cfg {String} target label target 
30410  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30411  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30412  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30413  * @cfg {String} iconTooltip default "This field is required"
30414  * @cfg {String} indicatorpos (left|right) default left
30415  * 
30416  * @constructor
30417  * Create a new FieldLabel
30418  * @param {Object} config The config object
30419  */
30420
30421 Roo.bootstrap.FieldLabel = function(config){
30422     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30423     
30424     this.addEvents({
30425             /**
30426              * @event invalid
30427              * Fires after the field has been marked as invalid.
30428              * @param {Roo.form.FieldLabel} this
30429              * @param {String} msg The validation message
30430              */
30431             invalid : true,
30432             /**
30433              * @event valid
30434              * Fires after the field has been validated with no errors.
30435              * @param {Roo.form.FieldLabel} this
30436              */
30437             valid : true
30438         });
30439 };
30440
30441 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30442     
30443     tag: 'label',
30444     cls: '',
30445     html: '',
30446     target: '',
30447     allowBlank : true,
30448     invalidClass : 'has-warning',
30449     validClass : 'has-success',
30450     iconTooltip : 'This field is required',
30451     indicatorpos : 'left',
30452     
30453     getAutoCreate : function(){
30454         
30455         var cls = "";
30456         if (!this.allowBlank) {
30457             cls  = "visible";
30458         }
30459         
30460         var cfg = {
30461             tag : this.tag,
30462             cls : 'roo-bootstrap-field-label ' + this.cls,
30463             for : this.target,
30464             cn : [
30465                 {
30466                     tag : 'i',
30467                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30468                     tooltip : this.iconTooltip
30469                 },
30470                 {
30471                     tag : 'span',
30472                     html : this.html
30473                 }
30474             ] 
30475         };
30476         
30477         if(this.indicatorpos == 'right'){
30478             var cfg = {
30479                 tag : this.tag,
30480                 cls : 'roo-bootstrap-field-label ' + this.cls,
30481                 for : this.target,
30482                 cn : [
30483                     {
30484                         tag : 'span',
30485                         html : this.html
30486                     },
30487                     {
30488                         tag : 'i',
30489                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30490                         tooltip : this.iconTooltip
30491                     }
30492                 ] 
30493             };
30494         }
30495         
30496         return cfg;
30497     },
30498     
30499     initEvents: function() 
30500     {
30501         Roo.bootstrap.Element.superclass.initEvents.call(this);
30502         
30503         this.indicator = this.indicatorEl();
30504         
30505         if(this.indicator){
30506             this.indicator.removeClass('visible');
30507             this.indicator.addClass('invisible');
30508         }
30509         
30510         Roo.bootstrap.FieldLabel.register(this);
30511     },
30512     
30513     indicatorEl : function()
30514     {
30515         var indicator = this.el.select('i.roo-required-indicator',true).first();
30516         
30517         if(!indicator){
30518             return false;
30519         }
30520         
30521         return indicator;
30522         
30523     },
30524     
30525     /**
30526      * Mark this field as valid
30527      */
30528     markValid : function()
30529     {
30530         if(this.indicator){
30531             this.indicator.removeClass('visible');
30532             this.indicator.addClass('invisible');
30533         }
30534         if (Roo.bootstrap.version == 3) {
30535             this.el.removeClass(this.invalidClass);
30536             this.el.addClass(this.validClass);
30537         } else {
30538             this.el.removeClass('is-invalid');
30539             this.el.addClass('is-valid');
30540         }
30541         
30542         
30543         this.fireEvent('valid', this);
30544     },
30545     
30546     /**
30547      * Mark this field as invalid
30548      * @param {String} msg The validation message
30549      */
30550     markInvalid : function(msg)
30551     {
30552         if(this.indicator){
30553             this.indicator.removeClass('invisible');
30554             this.indicator.addClass('visible');
30555         }
30556           if (Roo.bootstrap.version == 3) {
30557             this.el.removeClass(this.validClass);
30558             this.el.addClass(this.invalidClass);
30559         } else {
30560             this.el.removeClass('is-valid');
30561             this.el.addClass('is-invalid');
30562         }
30563         
30564         
30565         this.fireEvent('invalid', this, msg);
30566     }
30567     
30568    
30569 });
30570
30571 Roo.apply(Roo.bootstrap.FieldLabel, {
30572     
30573     groups: {},
30574     
30575      /**
30576     * register a FieldLabel Group
30577     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30578     */
30579     register : function(label)
30580     {
30581         if(this.groups.hasOwnProperty(label.target)){
30582             return;
30583         }
30584      
30585         this.groups[label.target] = label;
30586         
30587     },
30588     /**
30589     * fetch a FieldLabel Group based on the target
30590     * @param {string} target
30591     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30592     */
30593     get: function(target) {
30594         if (typeof(this.groups[target]) == 'undefined') {
30595             return false;
30596         }
30597         
30598         return this.groups[target] ;
30599     }
30600 });
30601
30602  
30603
30604  /*
30605  * - LGPL
30606  *
30607  * page DateSplitField.
30608  * 
30609  */
30610
30611
30612 /**
30613  * @class Roo.bootstrap.DateSplitField
30614  * @extends Roo.bootstrap.Component
30615  * Bootstrap DateSplitField class
30616  * @cfg {string} fieldLabel - the label associated
30617  * @cfg {Number} labelWidth set the width of label (0-12)
30618  * @cfg {String} labelAlign (top|left)
30619  * @cfg {Boolean} dayAllowBlank (true|false) default false
30620  * @cfg {Boolean} monthAllowBlank (true|false) default false
30621  * @cfg {Boolean} yearAllowBlank (true|false) default false
30622  * @cfg {string} dayPlaceholder 
30623  * @cfg {string} monthPlaceholder
30624  * @cfg {string} yearPlaceholder
30625  * @cfg {string} dayFormat default 'd'
30626  * @cfg {string} monthFormat default 'm'
30627  * @cfg {string} yearFormat default 'Y'
30628  * @cfg {Number} labellg set the width of label (1-12)
30629  * @cfg {Number} labelmd set the width of label (1-12)
30630  * @cfg {Number} labelsm set the width of label (1-12)
30631  * @cfg {Number} labelxs set the width of label (1-12)
30632
30633  *     
30634  * @constructor
30635  * Create a new DateSplitField
30636  * @param {Object} config The config object
30637  */
30638
30639 Roo.bootstrap.DateSplitField = function(config){
30640     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30641     
30642     this.addEvents({
30643         // raw events
30644          /**
30645          * @event years
30646          * getting the data of years
30647          * @param {Roo.bootstrap.DateSplitField} this
30648          * @param {Object} years
30649          */
30650         "years" : true,
30651         /**
30652          * @event days
30653          * getting the data of days
30654          * @param {Roo.bootstrap.DateSplitField} this
30655          * @param {Object} days
30656          */
30657         "days" : true,
30658         /**
30659          * @event invalid
30660          * Fires after the field has been marked as invalid.
30661          * @param {Roo.form.Field} this
30662          * @param {String} msg The validation message
30663          */
30664         invalid : true,
30665        /**
30666          * @event valid
30667          * Fires after the field has been validated with no errors.
30668          * @param {Roo.form.Field} this
30669          */
30670         valid : true
30671     });
30672 };
30673
30674 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30675     
30676     fieldLabel : '',
30677     labelAlign : 'top',
30678     labelWidth : 3,
30679     dayAllowBlank : false,
30680     monthAllowBlank : false,
30681     yearAllowBlank : false,
30682     dayPlaceholder : '',
30683     monthPlaceholder : '',
30684     yearPlaceholder : '',
30685     dayFormat : 'd',
30686     monthFormat : 'm',
30687     yearFormat : 'Y',
30688     isFormField : true,
30689     labellg : 0,
30690     labelmd : 0,
30691     labelsm : 0,
30692     labelxs : 0,
30693     
30694     getAutoCreate : function()
30695     {
30696         var cfg = {
30697             tag : 'div',
30698             cls : 'row roo-date-split-field-group',
30699             cn : [
30700                 {
30701                     tag : 'input',
30702                     type : 'hidden',
30703                     cls : 'form-hidden-field roo-date-split-field-group-value',
30704                     name : this.name
30705                 }
30706             ]
30707         };
30708         
30709         var labelCls = 'col-md-12';
30710         var contentCls = 'col-md-4';
30711         
30712         if(this.fieldLabel){
30713             
30714             var label = {
30715                 tag : 'div',
30716                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30717                 cn : [
30718                     {
30719                         tag : 'label',
30720                         html : this.fieldLabel
30721                     }
30722                 ]
30723             };
30724             
30725             if(this.labelAlign == 'left'){
30726             
30727                 if(this.labelWidth > 12){
30728                     label.style = "width: " + this.labelWidth + 'px';
30729                 }
30730
30731                 if(this.labelWidth < 13 && this.labelmd == 0){
30732                     this.labelmd = this.labelWidth;
30733                 }
30734
30735                 if(this.labellg > 0){
30736                     labelCls = ' col-lg-' + this.labellg;
30737                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30738                 }
30739
30740                 if(this.labelmd > 0){
30741                     labelCls = ' col-md-' + this.labelmd;
30742                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30743                 }
30744
30745                 if(this.labelsm > 0){
30746                     labelCls = ' col-sm-' + this.labelsm;
30747                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30748                 }
30749
30750                 if(this.labelxs > 0){
30751                     labelCls = ' col-xs-' + this.labelxs;
30752                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30753                 }
30754             }
30755             
30756             label.cls += ' ' + labelCls;
30757             
30758             cfg.cn.push(label);
30759         }
30760         
30761         Roo.each(['day', 'month', 'year'], function(t){
30762             cfg.cn.push({
30763                 tag : 'div',
30764                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30765             });
30766         }, this);
30767         
30768         return cfg;
30769     },
30770     
30771     inputEl: function ()
30772     {
30773         return this.el.select('.roo-date-split-field-group-value', true).first();
30774     },
30775     
30776     onRender : function(ct, position) 
30777     {
30778         var _this = this;
30779         
30780         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30781         
30782         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30783         
30784         this.dayField = new Roo.bootstrap.ComboBox({
30785             allowBlank : this.dayAllowBlank,
30786             alwaysQuery : true,
30787             displayField : 'value',
30788             editable : false,
30789             fieldLabel : '',
30790             forceSelection : true,
30791             mode : 'local',
30792             placeholder : this.dayPlaceholder,
30793             selectOnFocus : true,
30794             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30795             triggerAction : 'all',
30796             typeAhead : true,
30797             valueField : 'value',
30798             store : new Roo.data.SimpleStore({
30799                 data : (function() {    
30800                     var days = [];
30801                     _this.fireEvent('days', _this, days);
30802                     return days;
30803                 })(),
30804                 fields : [ 'value' ]
30805             }),
30806             listeners : {
30807                 select : function (_self, record, index)
30808                 {
30809                     _this.setValue(_this.getValue());
30810                 }
30811             }
30812         });
30813
30814         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30815         
30816         this.monthField = new Roo.bootstrap.MonthField({
30817             after : '<i class=\"fa fa-calendar\"></i>',
30818             allowBlank : this.monthAllowBlank,
30819             placeholder : this.monthPlaceholder,
30820             readOnly : true,
30821             listeners : {
30822                 render : function (_self)
30823                 {
30824                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30825                         e.preventDefault();
30826                         _self.focus();
30827                     });
30828                 },
30829                 select : function (_self, oldvalue, newvalue)
30830                 {
30831                     _this.setValue(_this.getValue());
30832                 }
30833             }
30834         });
30835         
30836         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30837         
30838         this.yearField = new Roo.bootstrap.ComboBox({
30839             allowBlank : this.yearAllowBlank,
30840             alwaysQuery : true,
30841             displayField : 'value',
30842             editable : false,
30843             fieldLabel : '',
30844             forceSelection : true,
30845             mode : 'local',
30846             placeholder : this.yearPlaceholder,
30847             selectOnFocus : true,
30848             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30849             triggerAction : 'all',
30850             typeAhead : true,
30851             valueField : 'value',
30852             store : new Roo.data.SimpleStore({
30853                 data : (function() {
30854                     var years = [];
30855                     _this.fireEvent('years', _this, years);
30856                     return years;
30857                 })(),
30858                 fields : [ 'value' ]
30859             }),
30860             listeners : {
30861                 select : function (_self, record, index)
30862                 {
30863                     _this.setValue(_this.getValue());
30864                 }
30865             }
30866         });
30867
30868         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30869     },
30870     
30871     setValue : function(v, format)
30872     {
30873         this.inputEl.dom.value = v;
30874         
30875         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30876         
30877         var d = Date.parseDate(v, f);
30878         
30879         if(!d){
30880             this.validate();
30881             return;
30882         }
30883         
30884         this.setDay(d.format(this.dayFormat));
30885         this.setMonth(d.format(this.monthFormat));
30886         this.setYear(d.format(this.yearFormat));
30887         
30888         this.validate();
30889         
30890         return;
30891     },
30892     
30893     setDay : function(v)
30894     {
30895         this.dayField.setValue(v);
30896         this.inputEl.dom.value = this.getValue();
30897         this.validate();
30898         return;
30899     },
30900     
30901     setMonth : function(v)
30902     {
30903         this.monthField.setValue(v, true);
30904         this.inputEl.dom.value = this.getValue();
30905         this.validate();
30906         return;
30907     },
30908     
30909     setYear : function(v)
30910     {
30911         this.yearField.setValue(v);
30912         this.inputEl.dom.value = this.getValue();
30913         this.validate();
30914         return;
30915     },
30916     
30917     getDay : function()
30918     {
30919         return this.dayField.getValue();
30920     },
30921     
30922     getMonth : function()
30923     {
30924         return this.monthField.getValue();
30925     },
30926     
30927     getYear : function()
30928     {
30929         return this.yearField.getValue();
30930     },
30931     
30932     getValue : function()
30933     {
30934         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30935         
30936         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30937         
30938         return date;
30939     },
30940     
30941     reset : function()
30942     {
30943         this.setDay('');
30944         this.setMonth('');
30945         this.setYear('');
30946         this.inputEl.dom.value = '';
30947         this.validate();
30948         return;
30949     },
30950     
30951     validate : function()
30952     {
30953         var d = this.dayField.validate();
30954         var m = this.monthField.validate();
30955         var y = this.yearField.validate();
30956         
30957         var valid = true;
30958         
30959         if(
30960                 (!this.dayAllowBlank && !d) ||
30961                 (!this.monthAllowBlank && !m) ||
30962                 (!this.yearAllowBlank && !y)
30963         ){
30964             valid = false;
30965         }
30966         
30967         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30968             return valid;
30969         }
30970         
30971         if(valid){
30972             this.markValid();
30973             return valid;
30974         }
30975         
30976         this.markInvalid();
30977         
30978         return valid;
30979     },
30980     
30981     markValid : function()
30982     {
30983         
30984         var label = this.el.select('label', true).first();
30985         var icon = this.el.select('i.fa-star', true).first();
30986
30987         if(label && icon){
30988             icon.remove();
30989         }
30990         
30991         this.fireEvent('valid', this);
30992     },
30993     
30994      /**
30995      * Mark this field as invalid
30996      * @param {String} msg The validation message
30997      */
30998     markInvalid : function(msg)
30999     {
31000         
31001         var label = this.el.select('label', true).first();
31002         var icon = this.el.select('i.fa-star', true).first();
31003
31004         if(label && !icon){
31005             this.el.select('.roo-date-split-field-label', true).createChild({
31006                 tag : 'i',
31007                 cls : 'text-danger fa fa-lg fa-star',
31008                 tooltip : 'This field is required',
31009                 style : 'margin-right:5px;'
31010             }, label, true);
31011         }
31012         
31013         this.fireEvent('invalid', this, msg);
31014     },
31015     
31016     clearInvalid : function()
31017     {
31018         var label = this.el.select('label', true).first();
31019         var icon = this.el.select('i.fa-star', true).first();
31020
31021         if(label && icon){
31022             icon.remove();
31023         }
31024         
31025         this.fireEvent('valid', this);
31026     },
31027     
31028     getName: function()
31029     {
31030         return this.name;
31031     }
31032     
31033 });
31034
31035  /**
31036  *
31037  * This is based on 
31038  * http://masonry.desandro.com
31039  *
31040  * The idea is to render all the bricks based on vertical width...
31041  *
31042  * The original code extends 'outlayer' - we might need to use that....
31043  * 
31044  */
31045
31046
31047 /**
31048  * @class Roo.bootstrap.LayoutMasonry
31049  * @extends Roo.bootstrap.Component
31050  * Bootstrap Layout Masonry class
31051  * 
31052  * @constructor
31053  * Create a new Element
31054  * @param {Object} config The config object
31055  */
31056
31057 Roo.bootstrap.LayoutMasonry = function(config){
31058     
31059     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31060     
31061     this.bricks = [];
31062     
31063     Roo.bootstrap.LayoutMasonry.register(this);
31064     
31065     this.addEvents({
31066         // raw events
31067         /**
31068          * @event layout
31069          * Fire after layout the items
31070          * @param {Roo.bootstrap.LayoutMasonry} this
31071          * @param {Roo.EventObject} e
31072          */
31073         "layout" : true
31074     });
31075     
31076 };
31077
31078 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31079     
31080     /**
31081      * @cfg {Boolean} isLayoutInstant = no animation?
31082      */   
31083     isLayoutInstant : false, // needed?
31084    
31085     /**
31086      * @cfg {Number} boxWidth  width of the columns
31087      */   
31088     boxWidth : 450,
31089     
31090       /**
31091      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31092      */   
31093     boxHeight : 0,
31094     
31095     /**
31096      * @cfg {Number} padWidth padding below box..
31097      */   
31098     padWidth : 10, 
31099     
31100     /**
31101      * @cfg {Number} gutter gutter width..
31102      */   
31103     gutter : 10,
31104     
31105      /**
31106      * @cfg {Number} maxCols maximum number of columns
31107      */   
31108     
31109     maxCols: 0,
31110     
31111     /**
31112      * @cfg {Boolean} isAutoInitial defalut true
31113      */   
31114     isAutoInitial : true, 
31115     
31116     containerWidth: 0,
31117     
31118     /**
31119      * @cfg {Boolean} isHorizontal defalut false
31120      */   
31121     isHorizontal : false, 
31122
31123     currentSize : null,
31124     
31125     tag: 'div',
31126     
31127     cls: '',
31128     
31129     bricks: null, //CompositeElement
31130     
31131     cols : 1,
31132     
31133     _isLayoutInited : false,
31134     
31135 //    isAlternative : false, // only use for vertical layout...
31136     
31137     /**
31138      * @cfg {Number} alternativePadWidth padding below box..
31139      */   
31140     alternativePadWidth : 50,
31141     
31142     selectedBrick : [],
31143     
31144     getAutoCreate : function(){
31145         
31146         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31147         
31148         var cfg = {
31149             tag: this.tag,
31150             cls: 'blog-masonary-wrapper ' + this.cls,
31151             cn : {
31152                 cls : 'mas-boxes masonary'
31153             }
31154         };
31155         
31156         return cfg;
31157     },
31158     
31159     getChildContainer: function( )
31160     {
31161         if (this.boxesEl) {
31162             return this.boxesEl;
31163         }
31164         
31165         this.boxesEl = this.el.select('.mas-boxes').first();
31166         
31167         return this.boxesEl;
31168     },
31169     
31170     
31171     initEvents : function()
31172     {
31173         var _this = this;
31174         
31175         if(this.isAutoInitial){
31176             Roo.log('hook children rendered');
31177             this.on('childrenrendered', function() {
31178                 Roo.log('children rendered');
31179                 _this.initial();
31180             } ,this);
31181         }
31182     },
31183     
31184     initial : function()
31185     {
31186         this.selectedBrick = [];
31187         
31188         this.currentSize = this.el.getBox(true);
31189         
31190         Roo.EventManager.onWindowResize(this.resize, this); 
31191
31192         if(!this.isAutoInitial){
31193             this.layout();
31194             return;
31195         }
31196         
31197         this.layout();
31198         
31199         return;
31200         //this.layout.defer(500,this);
31201         
31202     },
31203     
31204     resize : function()
31205     {
31206         var cs = this.el.getBox(true);
31207         
31208         if (
31209                 this.currentSize.width == cs.width && 
31210                 this.currentSize.x == cs.x && 
31211                 this.currentSize.height == cs.height && 
31212                 this.currentSize.y == cs.y 
31213         ) {
31214             Roo.log("no change in with or X or Y");
31215             return;
31216         }
31217         
31218         this.currentSize = cs;
31219         
31220         this.layout();
31221         
31222     },
31223     
31224     layout : function()
31225     {   
31226         this._resetLayout();
31227         
31228         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31229         
31230         this.layoutItems( isInstant );
31231       
31232         this._isLayoutInited = true;
31233         
31234         this.fireEvent('layout', this);
31235         
31236     },
31237     
31238     _resetLayout : function()
31239     {
31240         if(this.isHorizontal){
31241             this.horizontalMeasureColumns();
31242             return;
31243         }
31244         
31245         this.verticalMeasureColumns();
31246         
31247     },
31248     
31249     verticalMeasureColumns : function()
31250     {
31251         this.getContainerWidth();
31252         
31253 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31254 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31255 //            return;
31256 //        }
31257         
31258         var boxWidth = this.boxWidth + this.padWidth;
31259         
31260         if(this.containerWidth < this.boxWidth){
31261             boxWidth = this.containerWidth
31262         }
31263         
31264         var containerWidth = this.containerWidth;
31265         
31266         var cols = Math.floor(containerWidth / boxWidth);
31267         
31268         this.cols = Math.max( cols, 1 );
31269         
31270         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31271         
31272         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31273         
31274         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31275         
31276         this.colWidth = boxWidth + avail - this.padWidth;
31277         
31278         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31279         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31280     },
31281     
31282     horizontalMeasureColumns : function()
31283     {
31284         this.getContainerWidth();
31285         
31286         var boxWidth = this.boxWidth;
31287         
31288         if(this.containerWidth < boxWidth){
31289             boxWidth = this.containerWidth;
31290         }
31291         
31292         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31293         
31294         this.el.setHeight(boxWidth);
31295         
31296     },
31297     
31298     getContainerWidth : function()
31299     {
31300         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31301     },
31302     
31303     layoutItems : function( isInstant )
31304     {
31305         Roo.log(this.bricks);
31306         
31307         var items = Roo.apply([], this.bricks);
31308         
31309         if(this.isHorizontal){
31310             this._horizontalLayoutItems( items , isInstant );
31311             return;
31312         }
31313         
31314 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31315 //            this._verticalAlternativeLayoutItems( items , isInstant );
31316 //            return;
31317 //        }
31318         
31319         this._verticalLayoutItems( items , isInstant );
31320         
31321     },
31322     
31323     _verticalLayoutItems : function ( items , isInstant)
31324     {
31325         if ( !items || !items.length ) {
31326             return;
31327         }
31328         
31329         var standard = [
31330             ['xs', 'xs', 'xs', 'tall'],
31331             ['xs', 'xs', 'tall'],
31332             ['xs', 'xs', 'sm'],
31333             ['xs', 'xs', 'xs'],
31334             ['xs', 'tall'],
31335             ['xs', 'sm'],
31336             ['xs', 'xs'],
31337             ['xs'],
31338             
31339             ['sm', 'xs', 'xs'],
31340             ['sm', 'xs'],
31341             ['sm'],
31342             
31343             ['tall', 'xs', 'xs', 'xs'],
31344             ['tall', 'xs', 'xs'],
31345             ['tall', 'xs'],
31346             ['tall']
31347             
31348         ];
31349         
31350         var queue = [];
31351         
31352         var boxes = [];
31353         
31354         var box = [];
31355         
31356         Roo.each(items, function(item, k){
31357             
31358             switch (item.size) {
31359                 // these layouts take up a full box,
31360                 case 'md' :
31361                 case 'md-left' :
31362                 case 'md-right' :
31363                 case 'wide' :
31364                     
31365                     if(box.length){
31366                         boxes.push(box);
31367                         box = [];
31368                     }
31369                     
31370                     boxes.push([item]);
31371                     
31372                     break;
31373                     
31374                 case 'xs' :
31375                 case 'sm' :
31376                 case 'tall' :
31377                     
31378                     box.push(item);
31379                     
31380                     break;
31381                 default :
31382                     break;
31383                     
31384             }
31385             
31386         }, this);
31387         
31388         if(box.length){
31389             boxes.push(box);
31390             box = [];
31391         }
31392         
31393         var filterPattern = function(box, length)
31394         {
31395             if(!box.length){
31396                 return;
31397             }
31398             
31399             var match = false;
31400             
31401             var pattern = box.slice(0, length);
31402             
31403             var format = [];
31404             
31405             Roo.each(pattern, function(i){
31406                 format.push(i.size);
31407             }, this);
31408             
31409             Roo.each(standard, function(s){
31410                 
31411                 if(String(s) != String(format)){
31412                     return;
31413                 }
31414                 
31415                 match = true;
31416                 return false;
31417                 
31418             }, this);
31419             
31420             if(!match && length == 1){
31421                 return;
31422             }
31423             
31424             if(!match){
31425                 filterPattern(box, length - 1);
31426                 return;
31427             }
31428                 
31429             queue.push(pattern);
31430
31431             box = box.slice(length, box.length);
31432
31433             filterPattern(box, 4);
31434
31435             return;
31436             
31437         }
31438         
31439         Roo.each(boxes, function(box, k){
31440             
31441             if(!box.length){
31442                 return;
31443             }
31444             
31445             if(box.length == 1){
31446                 queue.push(box);
31447                 return;
31448             }
31449             
31450             filterPattern(box, 4);
31451             
31452         }, this);
31453         
31454         this._processVerticalLayoutQueue( queue, isInstant );
31455         
31456     },
31457     
31458 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31459 //    {
31460 //        if ( !items || !items.length ) {
31461 //            return;
31462 //        }
31463 //
31464 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31465 //        
31466 //    },
31467     
31468     _horizontalLayoutItems : function ( items , isInstant)
31469     {
31470         if ( !items || !items.length || items.length < 3) {
31471             return;
31472         }
31473         
31474         items.reverse();
31475         
31476         var eItems = items.slice(0, 3);
31477         
31478         items = items.slice(3, items.length);
31479         
31480         var standard = [
31481             ['xs', 'xs', 'xs', 'wide'],
31482             ['xs', 'xs', 'wide'],
31483             ['xs', 'xs', 'sm'],
31484             ['xs', 'xs', 'xs'],
31485             ['xs', 'wide'],
31486             ['xs', 'sm'],
31487             ['xs', 'xs'],
31488             ['xs'],
31489             
31490             ['sm', 'xs', 'xs'],
31491             ['sm', 'xs'],
31492             ['sm'],
31493             
31494             ['wide', 'xs', 'xs', 'xs'],
31495             ['wide', 'xs', 'xs'],
31496             ['wide', 'xs'],
31497             ['wide'],
31498             
31499             ['wide-thin']
31500         ];
31501         
31502         var queue = [];
31503         
31504         var boxes = [];
31505         
31506         var box = [];
31507         
31508         Roo.each(items, function(item, k){
31509             
31510             switch (item.size) {
31511                 case 'md' :
31512                 case 'md-left' :
31513                 case 'md-right' :
31514                 case 'tall' :
31515                     
31516                     if(box.length){
31517                         boxes.push(box);
31518                         box = [];
31519                     }
31520                     
31521                     boxes.push([item]);
31522                     
31523                     break;
31524                     
31525                 case 'xs' :
31526                 case 'sm' :
31527                 case 'wide' :
31528                 case 'wide-thin' :
31529                     
31530                     box.push(item);
31531                     
31532                     break;
31533                 default :
31534                     break;
31535                     
31536             }
31537             
31538         }, this);
31539         
31540         if(box.length){
31541             boxes.push(box);
31542             box = [];
31543         }
31544         
31545         var filterPattern = function(box, length)
31546         {
31547             if(!box.length){
31548                 return;
31549             }
31550             
31551             var match = false;
31552             
31553             var pattern = box.slice(0, length);
31554             
31555             var format = [];
31556             
31557             Roo.each(pattern, function(i){
31558                 format.push(i.size);
31559             }, this);
31560             
31561             Roo.each(standard, function(s){
31562                 
31563                 if(String(s) != String(format)){
31564                     return;
31565                 }
31566                 
31567                 match = true;
31568                 return false;
31569                 
31570             }, this);
31571             
31572             if(!match && length == 1){
31573                 return;
31574             }
31575             
31576             if(!match){
31577                 filterPattern(box, length - 1);
31578                 return;
31579             }
31580                 
31581             queue.push(pattern);
31582
31583             box = box.slice(length, box.length);
31584
31585             filterPattern(box, 4);
31586
31587             return;
31588             
31589         }
31590         
31591         Roo.each(boxes, function(box, k){
31592             
31593             if(!box.length){
31594                 return;
31595             }
31596             
31597             if(box.length == 1){
31598                 queue.push(box);
31599                 return;
31600             }
31601             
31602             filterPattern(box, 4);
31603             
31604         }, this);
31605         
31606         
31607         var prune = [];
31608         
31609         var pos = this.el.getBox(true);
31610         
31611         var minX = pos.x;
31612         
31613         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31614         
31615         var hit_end = false;
31616         
31617         Roo.each(queue, function(box){
31618             
31619             if(hit_end){
31620                 
31621                 Roo.each(box, function(b){
31622                 
31623                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31624                     b.el.hide();
31625
31626                 }, this);
31627
31628                 return;
31629             }
31630             
31631             var mx = 0;
31632             
31633             Roo.each(box, function(b){
31634                 
31635                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31636                 b.el.show();
31637
31638                 mx = Math.max(mx, b.x);
31639                 
31640             }, this);
31641             
31642             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31643             
31644             if(maxX < minX){
31645                 
31646                 Roo.each(box, function(b){
31647                 
31648                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31649                     b.el.hide();
31650                     
31651                 }, this);
31652                 
31653                 hit_end = true;
31654                 
31655                 return;
31656             }
31657             
31658             prune.push(box);
31659             
31660         }, this);
31661         
31662         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31663     },
31664     
31665     /** Sets position of item in DOM
31666     * @param {Element} item
31667     * @param {Number} x - horizontal position
31668     * @param {Number} y - vertical position
31669     * @param {Boolean} isInstant - disables transitions
31670     */
31671     _processVerticalLayoutQueue : function( queue, isInstant )
31672     {
31673         var pos = this.el.getBox(true);
31674         var x = pos.x;
31675         var y = pos.y;
31676         var maxY = [];
31677         
31678         for (var i = 0; i < this.cols; i++){
31679             maxY[i] = pos.y;
31680         }
31681         
31682         Roo.each(queue, function(box, k){
31683             
31684             var col = k % this.cols;
31685             
31686             Roo.each(box, function(b,kk){
31687                 
31688                 b.el.position('absolute');
31689                 
31690                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31691                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31692                 
31693                 if(b.size == 'md-left' || b.size == 'md-right'){
31694                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31695                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31696                 }
31697                 
31698                 b.el.setWidth(width);
31699                 b.el.setHeight(height);
31700                 // iframe?
31701                 b.el.select('iframe',true).setSize(width,height);
31702                 
31703             }, this);
31704             
31705             for (var i = 0; i < this.cols; i++){
31706                 
31707                 if(maxY[i] < maxY[col]){
31708                     col = i;
31709                     continue;
31710                 }
31711                 
31712                 col = Math.min(col, i);
31713                 
31714             }
31715             
31716             x = pos.x + col * (this.colWidth + this.padWidth);
31717             
31718             y = maxY[col];
31719             
31720             var positions = [];
31721             
31722             switch (box.length){
31723                 case 1 :
31724                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31725                     break;
31726                 case 2 :
31727                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31728                     break;
31729                 case 3 :
31730                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31731                     break;
31732                 case 4 :
31733                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31734                     break;
31735                 default :
31736                     break;
31737             }
31738             
31739             Roo.each(box, function(b,kk){
31740                 
31741                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31742                 
31743                 var sz = b.el.getSize();
31744                 
31745                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31746                 
31747             }, this);
31748             
31749         }, this);
31750         
31751         var mY = 0;
31752         
31753         for (var i = 0; i < this.cols; i++){
31754             mY = Math.max(mY, maxY[i]);
31755         }
31756         
31757         this.el.setHeight(mY - pos.y);
31758         
31759     },
31760     
31761 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31762 //    {
31763 //        var pos = this.el.getBox(true);
31764 //        var x = pos.x;
31765 //        var y = pos.y;
31766 //        var maxX = pos.right;
31767 //        
31768 //        var maxHeight = 0;
31769 //        
31770 //        Roo.each(items, function(item, k){
31771 //            
31772 //            var c = k % 2;
31773 //            
31774 //            item.el.position('absolute');
31775 //                
31776 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31777 //
31778 //            item.el.setWidth(width);
31779 //
31780 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31781 //
31782 //            item.el.setHeight(height);
31783 //            
31784 //            if(c == 0){
31785 //                item.el.setXY([x, y], isInstant ? false : true);
31786 //            } else {
31787 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31788 //            }
31789 //            
31790 //            y = y + height + this.alternativePadWidth;
31791 //            
31792 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31793 //            
31794 //        }, this);
31795 //        
31796 //        this.el.setHeight(maxHeight);
31797 //        
31798 //    },
31799     
31800     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31801     {
31802         var pos = this.el.getBox(true);
31803         
31804         var minX = pos.x;
31805         var minY = pos.y;
31806         
31807         var maxX = pos.right;
31808         
31809         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31810         
31811         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31812         
31813         Roo.each(queue, function(box, k){
31814             
31815             Roo.each(box, function(b, kk){
31816                 
31817                 b.el.position('absolute');
31818                 
31819                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31820                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31821                 
31822                 if(b.size == 'md-left' || b.size == 'md-right'){
31823                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31824                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31825                 }
31826                 
31827                 b.el.setWidth(width);
31828                 b.el.setHeight(height);
31829                 
31830             }, this);
31831             
31832             if(!box.length){
31833                 return;
31834             }
31835             
31836             var positions = [];
31837             
31838             switch (box.length){
31839                 case 1 :
31840                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31841                     break;
31842                 case 2 :
31843                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31844                     break;
31845                 case 3 :
31846                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31847                     break;
31848                 case 4 :
31849                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31850                     break;
31851                 default :
31852                     break;
31853             }
31854             
31855             Roo.each(box, function(b,kk){
31856                 
31857                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31858                 
31859                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31860                 
31861             }, this);
31862             
31863         }, this);
31864         
31865     },
31866     
31867     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31868     {
31869         Roo.each(eItems, function(b,k){
31870             
31871             b.size = (k == 0) ? 'sm' : 'xs';
31872             b.x = (k == 0) ? 2 : 1;
31873             b.y = (k == 0) ? 2 : 1;
31874             
31875             b.el.position('absolute');
31876             
31877             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31878                 
31879             b.el.setWidth(width);
31880             
31881             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31882             
31883             b.el.setHeight(height);
31884             
31885         }, this);
31886
31887         var positions = [];
31888         
31889         positions.push({
31890             x : maxX - this.unitWidth * 2 - this.gutter,
31891             y : minY
31892         });
31893         
31894         positions.push({
31895             x : maxX - this.unitWidth,
31896             y : minY + (this.unitWidth + this.gutter) * 2
31897         });
31898         
31899         positions.push({
31900             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31901             y : minY
31902         });
31903         
31904         Roo.each(eItems, function(b,k){
31905             
31906             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31907
31908         }, this);
31909         
31910     },
31911     
31912     getVerticalOneBoxColPositions : function(x, y, box)
31913     {
31914         var pos = [];
31915         
31916         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31917         
31918         if(box[0].size == 'md-left'){
31919             rand = 0;
31920         }
31921         
31922         if(box[0].size == 'md-right'){
31923             rand = 1;
31924         }
31925         
31926         pos.push({
31927             x : x + (this.unitWidth + this.gutter) * rand,
31928             y : y
31929         });
31930         
31931         return pos;
31932     },
31933     
31934     getVerticalTwoBoxColPositions : function(x, y, box)
31935     {
31936         var pos = [];
31937         
31938         if(box[0].size == 'xs'){
31939             
31940             pos.push({
31941                 x : x,
31942                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31943             });
31944
31945             pos.push({
31946                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31947                 y : y
31948             });
31949             
31950             return pos;
31951             
31952         }
31953         
31954         pos.push({
31955             x : x,
31956             y : y
31957         });
31958
31959         pos.push({
31960             x : x + (this.unitWidth + this.gutter) * 2,
31961             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31962         });
31963         
31964         return pos;
31965         
31966     },
31967     
31968     getVerticalThreeBoxColPositions : function(x, y, box)
31969     {
31970         var pos = [];
31971         
31972         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31973             
31974             pos.push({
31975                 x : x,
31976                 y : y
31977             });
31978
31979             pos.push({
31980                 x : x + (this.unitWidth + this.gutter) * 1,
31981                 y : y
31982             });
31983             
31984             pos.push({
31985                 x : x + (this.unitWidth + this.gutter) * 2,
31986                 y : y
31987             });
31988             
31989             return pos;
31990             
31991         }
31992         
31993         if(box[0].size == 'xs' && box[1].size == 'xs'){
31994             
31995             pos.push({
31996                 x : x,
31997                 y : y
31998             });
31999
32000             pos.push({
32001                 x : x,
32002                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32003             });
32004             
32005             pos.push({
32006                 x : x + (this.unitWidth + this.gutter) * 1,
32007                 y : y
32008             });
32009             
32010             return pos;
32011             
32012         }
32013         
32014         pos.push({
32015             x : x,
32016             y : y
32017         });
32018
32019         pos.push({
32020             x : x + (this.unitWidth + this.gutter) * 2,
32021             y : y
32022         });
32023
32024         pos.push({
32025             x : x + (this.unitWidth + this.gutter) * 2,
32026             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32027         });
32028             
32029         return pos;
32030         
32031     },
32032     
32033     getVerticalFourBoxColPositions : function(x, y, box)
32034     {
32035         var pos = [];
32036         
32037         if(box[0].size == 'xs'){
32038             
32039             pos.push({
32040                 x : x,
32041                 y : y
32042             });
32043
32044             pos.push({
32045                 x : x,
32046                 y : y + (this.unitHeight + this.gutter) * 1
32047             });
32048             
32049             pos.push({
32050                 x : x,
32051                 y : y + (this.unitHeight + this.gutter) * 2
32052             });
32053             
32054             pos.push({
32055                 x : x + (this.unitWidth + this.gutter) * 1,
32056                 y : y
32057             });
32058             
32059             return pos;
32060             
32061         }
32062         
32063         pos.push({
32064             x : x,
32065             y : y
32066         });
32067
32068         pos.push({
32069             x : x + (this.unitWidth + this.gutter) * 2,
32070             y : y
32071         });
32072
32073         pos.push({
32074             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32075             y : y + (this.unitHeight + this.gutter) * 1
32076         });
32077
32078         pos.push({
32079             x : x + (this.unitWidth + this.gutter) * 2,
32080             y : y + (this.unitWidth + this.gutter) * 2
32081         });
32082
32083         return pos;
32084         
32085     },
32086     
32087     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32088     {
32089         var pos = [];
32090         
32091         if(box[0].size == 'md-left'){
32092             pos.push({
32093                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32094                 y : minY
32095             });
32096             
32097             return pos;
32098         }
32099         
32100         if(box[0].size == 'md-right'){
32101             pos.push({
32102                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32103                 y : minY + (this.unitWidth + this.gutter) * 1
32104             });
32105             
32106             return pos;
32107         }
32108         
32109         var rand = Math.floor(Math.random() * (4 - box[0].y));
32110         
32111         pos.push({
32112             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32113             y : minY + (this.unitWidth + this.gutter) * rand
32114         });
32115         
32116         return pos;
32117         
32118     },
32119     
32120     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32121     {
32122         var pos = [];
32123         
32124         if(box[0].size == 'xs'){
32125             
32126             pos.push({
32127                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32128                 y : minY
32129             });
32130
32131             pos.push({
32132                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32133                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32134             });
32135             
32136             return pos;
32137             
32138         }
32139         
32140         pos.push({
32141             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32142             y : minY
32143         });
32144
32145         pos.push({
32146             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32147             y : minY + (this.unitWidth + this.gutter) * 2
32148         });
32149         
32150         return pos;
32151         
32152     },
32153     
32154     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32155     {
32156         var pos = [];
32157         
32158         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32159             
32160             pos.push({
32161                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32162                 y : minY
32163             });
32164
32165             pos.push({
32166                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32167                 y : minY + (this.unitWidth + this.gutter) * 1
32168             });
32169             
32170             pos.push({
32171                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32172                 y : minY + (this.unitWidth + this.gutter) * 2
32173             });
32174             
32175             return pos;
32176             
32177         }
32178         
32179         if(box[0].size == 'xs' && box[1].size == 'xs'){
32180             
32181             pos.push({
32182                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32183                 y : minY
32184             });
32185
32186             pos.push({
32187                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32188                 y : minY
32189             });
32190             
32191             pos.push({
32192                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32193                 y : minY + (this.unitWidth + this.gutter) * 1
32194             });
32195             
32196             return pos;
32197             
32198         }
32199         
32200         pos.push({
32201             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32202             y : minY
32203         });
32204
32205         pos.push({
32206             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32207             y : minY + (this.unitWidth + this.gutter) * 2
32208         });
32209
32210         pos.push({
32211             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32212             y : minY + (this.unitWidth + this.gutter) * 2
32213         });
32214             
32215         return pos;
32216         
32217     },
32218     
32219     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32220     {
32221         var pos = [];
32222         
32223         if(box[0].size == 'xs'){
32224             
32225             pos.push({
32226                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32227                 y : minY
32228             });
32229
32230             pos.push({
32231                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32232                 y : minY
32233             });
32234             
32235             pos.push({
32236                 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),
32237                 y : minY
32238             });
32239             
32240             pos.push({
32241                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32242                 y : minY + (this.unitWidth + this.gutter) * 1
32243             });
32244             
32245             return pos;
32246             
32247         }
32248         
32249         pos.push({
32250             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32251             y : minY
32252         });
32253         
32254         pos.push({
32255             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32256             y : minY + (this.unitWidth + this.gutter) * 2
32257         });
32258         
32259         pos.push({
32260             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32261             y : minY + (this.unitWidth + this.gutter) * 2
32262         });
32263         
32264         pos.push({
32265             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),
32266             y : minY + (this.unitWidth + this.gutter) * 2
32267         });
32268
32269         return pos;
32270         
32271     },
32272     
32273     /**
32274     * remove a Masonry Brick
32275     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32276     */
32277     removeBrick : function(brick_id)
32278     {
32279         if (!brick_id) {
32280             return;
32281         }
32282         
32283         for (var i = 0; i<this.bricks.length; i++) {
32284             if (this.bricks[i].id == brick_id) {
32285                 this.bricks.splice(i,1);
32286                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32287                 this.initial();
32288             }
32289         }
32290     },
32291     
32292     /**
32293     * adds a Masonry Brick
32294     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32295     */
32296     addBrick : function(cfg)
32297     {
32298         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32299         //this.register(cn);
32300         cn.parentId = this.id;
32301         cn.render(this.el);
32302         return cn;
32303     },
32304     
32305     /**
32306     * register a Masonry Brick
32307     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32308     */
32309     
32310     register : function(brick)
32311     {
32312         this.bricks.push(brick);
32313         brick.masonryId = this.id;
32314     },
32315     
32316     /**
32317     * clear all the Masonry Brick
32318     */
32319     clearAll : function()
32320     {
32321         this.bricks = [];
32322         //this.getChildContainer().dom.innerHTML = "";
32323         this.el.dom.innerHTML = '';
32324     },
32325     
32326     getSelected : function()
32327     {
32328         if (!this.selectedBrick) {
32329             return false;
32330         }
32331         
32332         return this.selectedBrick;
32333     }
32334 });
32335
32336 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32337     
32338     groups: {},
32339      /**
32340     * register a Masonry Layout
32341     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32342     */
32343     
32344     register : function(layout)
32345     {
32346         this.groups[layout.id] = layout;
32347     },
32348     /**
32349     * fetch a  Masonry Layout based on the masonry layout ID
32350     * @param {string} the masonry layout to add
32351     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32352     */
32353     
32354     get: function(layout_id) {
32355         if (typeof(this.groups[layout_id]) == 'undefined') {
32356             return false;
32357         }
32358         return this.groups[layout_id] ;
32359     }
32360     
32361     
32362     
32363 });
32364
32365  
32366
32367  /**
32368  *
32369  * This is based on 
32370  * http://masonry.desandro.com
32371  *
32372  * The idea is to render all the bricks based on vertical width...
32373  *
32374  * The original code extends 'outlayer' - we might need to use that....
32375  * 
32376  */
32377
32378
32379 /**
32380  * @class Roo.bootstrap.LayoutMasonryAuto
32381  * @extends Roo.bootstrap.Component
32382  * Bootstrap Layout Masonry class
32383  * 
32384  * @constructor
32385  * Create a new Element
32386  * @param {Object} config The config object
32387  */
32388
32389 Roo.bootstrap.LayoutMasonryAuto = function(config){
32390     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32391 };
32392
32393 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32394     
32395       /**
32396      * @cfg {Boolean} isFitWidth  - resize the width..
32397      */   
32398     isFitWidth : false,  // options..
32399     /**
32400      * @cfg {Boolean} isOriginLeft = left align?
32401      */   
32402     isOriginLeft : true,
32403     /**
32404      * @cfg {Boolean} isOriginTop = top align?
32405      */   
32406     isOriginTop : false,
32407     /**
32408      * @cfg {Boolean} isLayoutInstant = no animation?
32409      */   
32410     isLayoutInstant : false, // needed?
32411     /**
32412      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32413      */   
32414     isResizingContainer : true,
32415     /**
32416      * @cfg {Number} columnWidth  width of the columns 
32417      */   
32418     
32419     columnWidth : 0,
32420     
32421     /**
32422      * @cfg {Number} maxCols maximum number of columns
32423      */   
32424     
32425     maxCols: 0,
32426     /**
32427      * @cfg {Number} padHeight padding below box..
32428      */   
32429     
32430     padHeight : 10, 
32431     
32432     /**
32433      * @cfg {Boolean} isAutoInitial defalut true
32434      */   
32435     
32436     isAutoInitial : true, 
32437     
32438     // private?
32439     gutter : 0,
32440     
32441     containerWidth: 0,
32442     initialColumnWidth : 0,
32443     currentSize : null,
32444     
32445     colYs : null, // array.
32446     maxY : 0,
32447     padWidth: 10,
32448     
32449     
32450     tag: 'div',
32451     cls: '',
32452     bricks: null, //CompositeElement
32453     cols : 0, // array?
32454     // element : null, // wrapped now this.el
32455     _isLayoutInited : null, 
32456     
32457     
32458     getAutoCreate : function(){
32459         
32460         var cfg = {
32461             tag: this.tag,
32462             cls: 'blog-masonary-wrapper ' + this.cls,
32463             cn : {
32464                 cls : 'mas-boxes masonary'
32465             }
32466         };
32467         
32468         return cfg;
32469     },
32470     
32471     getChildContainer: function( )
32472     {
32473         if (this.boxesEl) {
32474             return this.boxesEl;
32475         }
32476         
32477         this.boxesEl = this.el.select('.mas-boxes').first();
32478         
32479         return this.boxesEl;
32480     },
32481     
32482     
32483     initEvents : function()
32484     {
32485         var _this = this;
32486         
32487         if(this.isAutoInitial){
32488             Roo.log('hook children rendered');
32489             this.on('childrenrendered', function() {
32490                 Roo.log('children rendered');
32491                 _this.initial();
32492             } ,this);
32493         }
32494         
32495     },
32496     
32497     initial : function()
32498     {
32499         this.reloadItems();
32500
32501         this.currentSize = this.el.getBox(true);
32502
32503         /// was window resize... - let's see if this works..
32504         Roo.EventManager.onWindowResize(this.resize, this); 
32505
32506         if(!this.isAutoInitial){
32507             this.layout();
32508             return;
32509         }
32510         
32511         this.layout.defer(500,this);
32512     },
32513     
32514     reloadItems: function()
32515     {
32516         this.bricks = this.el.select('.masonry-brick', true);
32517         
32518         this.bricks.each(function(b) {
32519             //Roo.log(b.getSize());
32520             if (!b.attr('originalwidth')) {
32521                 b.attr('originalwidth',  b.getSize().width);
32522             }
32523             
32524         });
32525         
32526         Roo.log(this.bricks.elements.length);
32527     },
32528     
32529     resize : function()
32530     {
32531         Roo.log('resize');
32532         var cs = this.el.getBox(true);
32533         
32534         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32535             Roo.log("no change in with or X");
32536             return;
32537         }
32538         this.currentSize = cs;
32539         this.layout();
32540     },
32541     
32542     layout : function()
32543     {
32544          Roo.log('layout');
32545         this._resetLayout();
32546         //this._manageStamps();
32547       
32548         // don't animate first layout
32549         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32550         this.layoutItems( isInstant );
32551       
32552         // flag for initalized
32553         this._isLayoutInited = true;
32554     },
32555     
32556     layoutItems : function( isInstant )
32557     {
32558         //var items = this._getItemsForLayout( this.items );
32559         // original code supports filtering layout items.. we just ignore it..
32560         
32561         this._layoutItems( this.bricks , isInstant );
32562       
32563         this._postLayout();
32564     },
32565     _layoutItems : function ( items , isInstant)
32566     {
32567        //this.fireEvent( 'layout', this, items );
32568     
32569
32570         if ( !items || !items.elements.length ) {
32571           // no items, emit event with empty array
32572             return;
32573         }
32574
32575         var queue = [];
32576         items.each(function(item) {
32577             Roo.log("layout item");
32578             Roo.log(item);
32579             // get x/y object from method
32580             var position = this._getItemLayoutPosition( item );
32581             // enqueue
32582             position.item = item;
32583             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32584             queue.push( position );
32585         }, this);
32586       
32587         this._processLayoutQueue( queue );
32588     },
32589     /** Sets position of item in DOM
32590     * @param {Element} item
32591     * @param {Number} x - horizontal position
32592     * @param {Number} y - vertical position
32593     * @param {Boolean} isInstant - disables transitions
32594     */
32595     _processLayoutQueue : function( queue )
32596     {
32597         for ( var i=0, len = queue.length; i < len; i++ ) {
32598             var obj = queue[i];
32599             obj.item.position('absolute');
32600             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32601         }
32602     },
32603       
32604     
32605     /**
32606     * Any logic you want to do after each layout,
32607     * i.e. size the container
32608     */
32609     _postLayout : function()
32610     {
32611         this.resizeContainer();
32612     },
32613     
32614     resizeContainer : function()
32615     {
32616         if ( !this.isResizingContainer ) {
32617             return;
32618         }
32619         var size = this._getContainerSize();
32620         if ( size ) {
32621             this.el.setSize(size.width,size.height);
32622             this.boxesEl.setSize(size.width,size.height);
32623         }
32624     },
32625     
32626     
32627     
32628     _resetLayout : function()
32629     {
32630         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32631         this.colWidth = this.el.getWidth();
32632         //this.gutter = this.el.getWidth(); 
32633         
32634         this.measureColumns();
32635
32636         // reset column Y
32637         var i = this.cols;
32638         this.colYs = [];
32639         while (i--) {
32640             this.colYs.push( 0 );
32641         }
32642     
32643         this.maxY = 0;
32644     },
32645
32646     measureColumns : function()
32647     {
32648         this.getContainerWidth();
32649       // if columnWidth is 0, default to outerWidth of first item
32650         if ( !this.columnWidth ) {
32651             var firstItem = this.bricks.first();
32652             Roo.log(firstItem);
32653             this.columnWidth  = this.containerWidth;
32654             if (firstItem && firstItem.attr('originalwidth') ) {
32655                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32656             }
32657             // columnWidth fall back to item of first element
32658             Roo.log("set column width?");
32659                         this.initialColumnWidth = this.columnWidth  ;
32660
32661             // if first elem has no width, default to size of container
32662             
32663         }
32664         
32665         
32666         if (this.initialColumnWidth) {
32667             this.columnWidth = this.initialColumnWidth;
32668         }
32669         
32670         
32671             
32672         // column width is fixed at the top - however if container width get's smaller we should
32673         // reduce it...
32674         
32675         // this bit calcs how man columns..
32676             
32677         var columnWidth = this.columnWidth += this.gutter;
32678       
32679         // calculate columns
32680         var containerWidth = this.containerWidth + this.gutter;
32681         
32682         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32683         // fix rounding errors, typically with gutters
32684         var excess = columnWidth - containerWidth % columnWidth;
32685         
32686         
32687         // if overshoot is less than a pixel, round up, otherwise floor it
32688         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32689         cols = Math[ mathMethod ]( cols );
32690         this.cols = Math.max( cols, 1 );
32691         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32692         
32693          // padding positioning..
32694         var totalColWidth = this.cols * this.columnWidth;
32695         var padavail = this.containerWidth - totalColWidth;
32696         // so for 2 columns - we need 3 'pads'
32697         
32698         var padNeeded = (1+this.cols) * this.padWidth;
32699         
32700         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32701         
32702         this.columnWidth += padExtra
32703         //this.padWidth = Math.floor(padavail /  ( this.cols));
32704         
32705         // adjust colum width so that padding is fixed??
32706         
32707         // we have 3 columns ... total = width * 3
32708         // we have X left over... that should be used by 
32709         
32710         //if (this.expandC) {
32711             
32712         //}
32713         
32714         
32715         
32716     },
32717     
32718     getContainerWidth : function()
32719     {
32720        /* // container is parent if fit width
32721         var container = this.isFitWidth ? this.element.parentNode : this.element;
32722         // check that this.size and size are there
32723         // IE8 triggers resize on body size change, so they might not be
32724         
32725         var size = getSize( container );  //FIXME
32726         this.containerWidth = size && size.innerWidth; //FIXME
32727         */
32728          
32729         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32730         
32731     },
32732     
32733     _getItemLayoutPosition : function( item )  // what is item?
32734     {
32735         // we resize the item to our columnWidth..
32736       
32737         item.setWidth(this.columnWidth);
32738         item.autoBoxAdjust  = false;
32739         
32740         var sz = item.getSize();
32741  
32742         // how many columns does this brick span
32743         var remainder = this.containerWidth % this.columnWidth;
32744         
32745         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32746         // round if off by 1 pixel, otherwise use ceil
32747         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32748         colSpan = Math.min( colSpan, this.cols );
32749         
32750         // normally this should be '1' as we dont' currently allow multi width columns..
32751         
32752         var colGroup = this._getColGroup( colSpan );
32753         // get the minimum Y value from the columns
32754         var minimumY = Math.min.apply( Math, colGroup );
32755         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32756         
32757         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32758          
32759         // position the brick
32760         var position = {
32761             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32762             y: this.currentSize.y + minimumY + this.padHeight
32763         };
32764         
32765         Roo.log(position);
32766         // apply setHeight to necessary columns
32767         var setHeight = minimumY + sz.height + this.padHeight;
32768         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32769         
32770         var setSpan = this.cols + 1 - colGroup.length;
32771         for ( var i = 0; i < setSpan; i++ ) {
32772           this.colYs[ shortColIndex + i ] = setHeight ;
32773         }
32774       
32775         return position;
32776     },
32777     
32778     /**
32779      * @param {Number} colSpan - number of columns the element spans
32780      * @returns {Array} colGroup
32781      */
32782     _getColGroup : function( colSpan )
32783     {
32784         if ( colSpan < 2 ) {
32785           // if brick spans only one column, use all the column Ys
32786           return this.colYs;
32787         }
32788       
32789         var colGroup = [];
32790         // how many different places could this brick fit horizontally
32791         var groupCount = this.cols + 1 - colSpan;
32792         // for each group potential horizontal position
32793         for ( var i = 0; i < groupCount; i++ ) {
32794           // make an array of colY values for that one group
32795           var groupColYs = this.colYs.slice( i, i + colSpan );
32796           // and get the max value of the array
32797           colGroup[i] = Math.max.apply( Math, groupColYs );
32798         }
32799         return colGroup;
32800     },
32801     /*
32802     _manageStamp : function( stamp )
32803     {
32804         var stampSize =  stamp.getSize();
32805         var offset = stamp.getBox();
32806         // get the columns that this stamp affects
32807         var firstX = this.isOriginLeft ? offset.x : offset.right;
32808         var lastX = firstX + stampSize.width;
32809         var firstCol = Math.floor( firstX / this.columnWidth );
32810         firstCol = Math.max( 0, firstCol );
32811         
32812         var lastCol = Math.floor( lastX / this.columnWidth );
32813         // lastCol should not go over if multiple of columnWidth #425
32814         lastCol -= lastX % this.columnWidth ? 0 : 1;
32815         lastCol = Math.min( this.cols - 1, lastCol );
32816         
32817         // set colYs to bottom of the stamp
32818         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32819             stampSize.height;
32820             
32821         for ( var i = firstCol; i <= lastCol; i++ ) {
32822           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32823         }
32824     },
32825     */
32826     
32827     _getContainerSize : function()
32828     {
32829         this.maxY = Math.max.apply( Math, this.colYs );
32830         var size = {
32831             height: this.maxY
32832         };
32833       
32834         if ( this.isFitWidth ) {
32835             size.width = this._getContainerFitWidth();
32836         }
32837       
32838         return size;
32839     },
32840     
32841     _getContainerFitWidth : function()
32842     {
32843         var unusedCols = 0;
32844         // count unused columns
32845         var i = this.cols;
32846         while ( --i ) {
32847           if ( this.colYs[i] !== 0 ) {
32848             break;
32849           }
32850           unusedCols++;
32851         }
32852         // fit container to columns that have been used
32853         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32854     },
32855     
32856     needsResizeLayout : function()
32857     {
32858         var previousWidth = this.containerWidth;
32859         this.getContainerWidth();
32860         return previousWidth !== this.containerWidth;
32861     }
32862  
32863 });
32864
32865  
32866
32867  /*
32868  * - LGPL
32869  *
32870  * element
32871  * 
32872  */
32873
32874 /**
32875  * @class Roo.bootstrap.MasonryBrick
32876  * @extends Roo.bootstrap.Component
32877  * Bootstrap MasonryBrick class
32878  * 
32879  * @constructor
32880  * Create a new MasonryBrick
32881  * @param {Object} config The config object
32882  */
32883
32884 Roo.bootstrap.MasonryBrick = function(config){
32885     
32886     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32887     
32888     Roo.bootstrap.MasonryBrick.register(this);
32889     
32890     this.addEvents({
32891         // raw events
32892         /**
32893          * @event click
32894          * When a MasonryBrick is clcik
32895          * @param {Roo.bootstrap.MasonryBrick} this
32896          * @param {Roo.EventObject} e
32897          */
32898         "click" : true
32899     });
32900 };
32901
32902 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32903     
32904     /**
32905      * @cfg {String} title
32906      */   
32907     title : '',
32908     /**
32909      * @cfg {String} html
32910      */   
32911     html : '',
32912     /**
32913      * @cfg {String} bgimage
32914      */   
32915     bgimage : '',
32916     /**
32917      * @cfg {String} videourl
32918      */   
32919     videourl : '',
32920     /**
32921      * @cfg {String} cls
32922      */   
32923     cls : '',
32924     /**
32925      * @cfg {String} href
32926      */   
32927     href : '',
32928     /**
32929      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32930      */   
32931     size : 'xs',
32932     
32933     /**
32934      * @cfg {String} placetitle (center|bottom)
32935      */   
32936     placetitle : '',
32937     
32938     /**
32939      * @cfg {Boolean} isFitContainer defalut true
32940      */   
32941     isFitContainer : true, 
32942     
32943     /**
32944      * @cfg {Boolean} preventDefault defalut false
32945      */   
32946     preventDefault : false, 
32947     
32948     /**
32949      * @cfg {Boolean} inverse defalut false
32950      */   
32951     maskInverse : false, 
32952     
32953     getAutoCreate : function()
32954     {
32955         if(!this.isFitContainer){
32956             return this.getSplitAutoCreate();
32957         }
32958         
32959         var cls = 'masonry-brick masonry-brick-full';
32960         
32961         if(this.href.length){
32962             cls += ' masonry-brick-link';
32963         }
32964         
32965         if(this.bgimage.length){
32966             cls += ' masonry-brick-image';
32967         }
32968         
32969         if(this.maskInverse){
32970             cls += ' mask-inverse';
32971         }
32972         
32973         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32974             cls += ' enable-mask';
32975         }
32976         
32977         if(this.size){
32978             cls += ' masonry-' + this.size + '-brick';
32979         }
32980         
32981         if(this.placetitle.length){
32982             
32983             switch (this.placetitle) {
32984                 case 'center' :
32985                     cls += ' masonry-center-title';
32986                     break;
32987                 case 'bottom' :
32988                     cls += ' masonry-bottom-title';
32989                     break;
32990                 default:
32991                     break;
32992             }
32993             
32994         } else {
32995             if(!this.html.length && !this.bgimage.length){
32996                 cls += ' masonry-center-title';
32997             }
32998
32999             if(!this.html.length && this.bgimage.length){
33000                 cls += ' masonry-bottom-title';
33001             }
33002         }
33003         
33004         if(this.cls){
33005             cls += ' ' + this.cls;
33006         }
33007         
33008         var cfg = {
33009             tag: (this.href.length) ? 'a' : 'div',
33010             cls: cls,
33011             cn: [
33012                 {
33013                     tag: 'div',
33014                     cls: 'masonry-brick-mask'
33015                 },
33016                 {
33017                     tag: 'div',
33018                     cls: 'masonry-brick-paragraph',
33019                     cn: []
33020                 }
33021             ]
33022         };
33023         
33024         if(this.href.length){
33025             cfg.href = this.href;
33026         }
33027         
33028         var cn = cfg.cn[1].cn;
33029         
33030         if(this.title.length){
33031             cn.push({
33032                 tag: 'h4',
33033                 cls: 'masonry-brick-title',
33034                 html: this.title
33035             });
33036         }
33037         
33038         if(this.html.length){
33039             cn.push({
33040                 tag: 'p',
33041                 cls: 'masonry-brick-text',
33042                 html: this.html
33043             });
33044         }
33045         
33046         if (!this.title.length && !this.html.length) {
33047             cfg.cn[1].cls += ' hide';
33048         }
33049         
33050         if(this.bgimage.length){
33051             cfg.cn.push({
33052                 tag: 'img',
33053                 cls: 'masonry-brick-image-view',
33054                 src: this.bgimage
33055             });
33056         }
33057         
33058         if(this.videourl.length){
33059             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33060             // youtube support only?
33061             cfg.cn.push({
33062                 tag: 'iframe',
33063                 cls: 'masonry-brick-image-view',
33064                 src: vurl,
33065                 frameborder : 0,
33066                 allowfullscreen : true
33067             });
33068         }
33069         
33070         return cfg;
33071         
33072     },
33073     
33074     getSplitAutoCreate : function()
33075     {
33076         var cls = 'masonry-brick masonry-brick-split';
33077         
33078         if(this.href.length){
33079             cls += ' masonry-brick-link';
33080         }
33081         
33082         if(this.bgimage.length){
33083             cls += ' masonry-brick-image';
33084         }
33085         
33086         if(this.size){
33087             cls += ' masonry-' + this.size + '-brick';
33088         }
33089         
33090         switch (this.placetitle) {
33091             case 'center' :
33092                 cls += ' masonry-center-title';
33093                 break;
33094             case 'bottom' :
33095                 cls += ' masonry-bottom-title';
33096                 break;
33097             default:
33098                 if(!this.bgimage.length){
33099                     cls += ' masonry-center-title';
33100                 }
33101
33102                 if(this.bgimage.length){
33103                     cls += ' masonry-bottom-title';
33104                 }
33105                 break;
33106         }
33107         
33108         if(this.cls){
33109             cls += ' ' + this.cls;
33110         }
33111         
33112         var cfg = {
33113             tag: (this.href.length) ? 'a' : 'div',
33114             cls: cls,
33115             cn: [
33116                 {
33117                     tag: 'div',
33118                     cls: 'masonry-brick-split-head',
33119                     cn: [
33120                         {
33121                             tag: 'div',
33122                             cls: 'masonry-brick-paragraph',
33123                             cn: []
33124                         }
33125                     ]
33126                 },
33127                 {
33128                     tag: 'div',
33129                     cls: 'masonry-brick-split-body',
33130                     cn: []
33131                 }
33132             ]
33133         };
33134         
33135         if(this.href.length){
33136             cfg.href = this.href;
33137         }
33138         
33139         if(this.title.length){
33140             cfg.cn[0].cn[0].cn.push({
33141                 tag: 'h4',
33142                 cls: 'masonry-brick-title',
33143                 html: this.title
33144             });
33145         }
33146         
33147         if(this.html.length){
33148             cfg.cn[1].cn.push({
33149                 tag: 'p',
33150                 cls: 'masonry-brick-text',
33151                 html: this.html
33152             });
33153         }
33154
33155         if(this.bgimage.length){
33156             cfg.cn[0].cn.push({
33157                 tag: 'img',
33158                 cls: 'masonry-brick-image-view',
33159                 src: this.bgimage
33160             });
33161         }
33162         
33163         if(this.videourl.length){
33164             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33165             // youtube support only?
33166             cfg.cn[0].cn.cn.push({
33167                 tag: 'iframe',
33168                 cls: 'masonry-brick-image-view',
33169                 src: vurl,
33170                 frameborder : 0,
33171                 allowfullscreen : true
33172             });
33173         }
33174         
33175         return cfg;
33176     },
33177     
33178     initEvents: function() 
33179     {
33180         switch (this.size) {
33181             case 'xs' :
33182                 this.x = 1;
33183                 this.y = 1;
33184                 break;
33185             case 'sm' :
33186                 this.x = 2;
33187                 this.y = 2;
33188                 break;
33189             case 'md' :
33190             case 'md-left' :
33191             case 'md-right' :
33192                 this.x = 3;
33193                 this.y = 3;
33194                 break;
33195             case 'tall' :
33196                 this.x = 2;
33197                 this.y = 3;
33198                 break;
33199             case 'wide' :
33200                 this.x = 3;
33201                 this.y = 2;
33202                 break;
33203             case 'wide-thin' :
33204                 this.x = 3;
33205                 this.y = 1;
33206                 break;
33207                         
33208             default :
33209                 break;
33210         }
33211         
33212         if(Roo.isTouch){
33213             this.el.on('touchstart', this.onTouchStart, this);
33214             this.el.on('touchmove', this.onTouchMove, this);
33215             this.el.on('touchend', this.onTouchEnd, this);
33216             this.el.on('contextmenu', this.onContextMenu, this);
33217         } else {
33218             this.el.on('mouseenter'  ,this.enter, this);
33219             this.el.on('mouseleave', this.leave, this);
33220             this.el.on('click', this.onClick, this);
33221         }
33222         
33223         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33224             this.parent().bricks.push(this);   
33225         }
33226         
33227     },
33228     
33229     onClick: function(e, el)
33230     {
33231         var time = this.endTimer - this.startTimer;
33232         // Roo.log(e.preventDefault());
33233         if(Roo.isTouch){
33234             if(time > 1000){
33235                 e.preventDefault();
33236                 return;
33237             }
33238         }
33239         
33240         if(!this.preventDefault){
33241             return;
33242         }
33243         
33244         e.preventDefault();
33245         
33246         if (this.activeClass != '') {
33247             this.selectBrick();
33248         }
33249         
33250         this.fireEvent('click', this, e);
33251     },
33252     
33253     enter: function(e, el)
33254     {
33255         e.preventDefault();
33256         
33257         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33258             return;
33259         }
33260         
33261         if(this.bgimage.length && this.html.length){
33262             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33263         }
33264     },
33265     
33266     leave: function(e, el)
33267     {
33268         e.preventDefault();
33269         
33270         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33271             return;
33272         }
33273         
33274         if(this.bgimage.length && this.html.length){
33275             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33276         }
33277     },
33278     
33279     onTouchStart: function(e, el)
33280     {
33281 //        e.preventDefault();
33282         
33283         this.touchmoved = false;
33284         
33285         if(!this.isFitContainer){
33286             return;
33287         }
33288         
33289         if(!this.bgimage.length || !this.html.length){
33290             return;
33291         }
33292         
33293         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33294         
33295         this.timer = new Date().getTime();
33296         
33297     },
33298     
33299     onTouchMove: function(e, el)
33300     {
33301         this.touchmoved = true;
33302     },
33303     
33304     onContextMenu : function(e,el)
33305     {
33306         e.preventDefault();
33307         e.stopPropagation();
33308         return false;
33309     },
33310     
33311     onTouchEnd: function(e, el)
33312     {
33313 //        e.preventDefault();
33314         
33315         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33316         
33317             this.leave(e,el);
33318             
33319             return;
33320         }
33321         
33322         if(!this.bgimage.length || !this.html.length){
33323             
33324             if(this.href.length){
33325                 window.location.href = this.href;
33326             }
33327             
33328             return;
33329         }
33330         
33331         if(!this.isFitContainer){
33332             return;
33333         }
33334         
33335         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33336         
33337         window.location.href = this.href;
33338     },
33339     
33340     //selection on single brick only
33341     selectBrick : function() {
33342         
33343         if (!this.parentId) {
33344             return;
33345         }
33346         
33347         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33348         var index = m.selectedBrick.indexOf(this.id);
33349         
33350         if ( index > -1) {
33351             m.selectedBrick.splice(index,1);
33352             this.el.removeClass(this.activeClass);
33353             return;
33354         }
33355         
33356         for(var i = 0; i < m.selectedBrick.length; i++) {
33357             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33358             b.el.removeClass(b.activeClass);
33359         }
33360         
33361         m.selectedBrick = [];
33362         
33363         m.selectedBrick.push(this.id);
33364         this.el.addClass(this.activeClass);
33365         return;
33366     },
33367     
33368     isSelected : function(){
33369         return this.el.hasClass(this.activeClass);
33370         
33371     }
33372 });
33373
33374 Roo.apply(Roo.bootstrap.MasonryBrick, {
33375     
33376     //groups: {},
33377     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33378      /**
33379     * register a Masonry Brick
33380     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33381     */
33382     
33383     register : function(brick)
33384     {
33385         //this.groups[brick.id] = brick;
33386         this.groups.add(brick.id, brick);
33387     },
33388     /**
33389     * fetch a  masonry brick based on the masonry brick ID
33390     * @param {string} the masonry brick to add
33391     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33392     */
33393     
33394     get: function(brick_id) 
33395     {
33396         // if (typeof(this.groups[brick_id]) == 'undefined') {
33397         //     return false;
33398         // }
33399         // return this.groups[brick_id] ;
33400         
33401         if(this.groups.key(brick_id)) {
33402             return this.groups.key(brick_id);
33403         }
33404         
33405         return false;
33406     }
33407     
33408     
33409     
33410 });
33411
33412  /*
33413  * - LGPL
33414  *
33415  * element
33416  * 
33417  */
33418
33419 /**
33420  * @class Roo.bootstrap.Brick
33421  * @extends Roo.bootstrap.Component
33422  * Bootstrap Brick class
33423  * 
33424  * @constructor
33425  * Create a new Brick
33426  * @param {Object} config The config object
33427  */
33428
33429 Roo.bootstrap.Brick = function(config){
33430     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33431     
33432     this.addEvents({
33433         // raw events
33434         /**
33435          * @event click
33436          * When a Brick is click
33437          * @param {Roo.bootstrap.Brick} this
33438          * @param {Roo.EventObject} e
33439          */
33440         "click" : true
33441     });
33442 };
33443
33444 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33445     
33446     /**
33447      * @cfg {String} title
33448      */   
33449     title : '',
33450     /**
33451      * @cfg {String} html
33452      */   
33453     html : '',
33454     /**
33455      * @cfg {String} bgimage
33456      */   
33457     bgimage : '',
33458     /**
33459      * @cfg {String} cls
33460      */   
33461     cls : '',
33462     /**
33463      * @cfg {String} href
33464      */   
33465     href : '',
33466     /**
33467      * @cfg {String} video
33468      */   
33469     video : '',
33470     /**
33471      * @cfg {Boolean} square
33472      */   
33473     square : true,
33474     
33475     getAutoCreate : function()
33476     {
33477         var cls = 'roo-brick';
33478         
33479         if(this.href.length){
33480             cls += ' roo-brick-link';
33481         }
33482         
33483         if(this.bgimage.length){
33484             cls += ' roo-brick-image';
33485         }
33486         
33487         if(!this.html.length && !this.bgimage.length){
33488             cls += ' roo-brick-center-title';
33489         }
33490         
33491         if(!this.html.length && this.bgimage.length){
33492             cls += ' roo-brick-bottom-title';
33493         }
33494         
33495         if(this.cls){
33496             cls += ' ' + this.cls;
33497         }
33498         
33499         var cfg = {
33500             tag: (this.href.length) ? 'a' : 'div',
33501             cls: cls,
33502             cn: [
33503                 {
33504                     tag: 'div',
33505                     cls: 'roo-brick-paragraph',
33506                     cn: []
33507                 }
33508             ]
33509         };
33510         
33511         if(this.href.length){
33512             cfg.href = this.href;
33513         }
33514         
33515         var cn = cfg.cn[0].cn;
33516         
33517         if(this.title.length){
33518             cn.push({
33519                 tag: 'h4',
33520                 cls: 'roo-brick-title',
33521                 html: this.title
33522             });
33523         }
33524         
33525         if(this.html.length){
33526             cn.push({
33527                 tag: 'p',
33528                 cls: 'roo-brick-text',
33529                 html: this.html
33530             });
33531         } else {
33532             cn.cls += ' hide';
33533         }
33534         
33535         if(this.bgimage.length){
33536             cfg.cn.push({
33537                 tag: 'img',
33538                 cls: 'roo-brick-image-view',
33539                 src: this.bgimage
33540             });
33541         }
33542         
33543         return cfg;
33544     },
33545     
33546     initEvents: function() 
33547     {
33548         if(this.title.length || this.html.length){
33549             this.el.on('mouseenter'  ,this.enter, this);
33550             this.el.on('mouseleave', this.leave, this);
33551         }
33552         
33553         Roo.EventManager.onWindowResize(this.resize, this); 
33554         
33555         if(this.bgimage.length){
33556             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33557             this.imageEl.on('load', this.onImageLoad, this);
33558             return;
33559         }
33560         
33561         this.resize();
33562     },
33563     
33564     onImageLoad : function()
33565     {
33566         this.resize();
33567     },
33568     
33569     resize : function()
33570     {
33571         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33572         
33573         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33574         
33575         if(this.bgimage.length){
33576             var image = this.el.select('.roo-brick-image-view', true).first();
33577             
33578             image.setWidth(paragraph.getWidth());
33579             
33580             if(this.square){
33581                 image.setHeight(paragraph.getWidth());
33582             }
33583             
33584             this.el.setHeight(image.getHeight());
33585             paragraph.setHeight(image.getHeight());
33586             
33587         }
33588         
33589     },
33590     
33591     enter: function(e, el)
33592     {
33593         e.preventDefault();
33594         
33595         if(this.bgimage.length){
33596             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33597             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33598         }
33599     },
33600     
33601     leave: function(e, el)
33602     {
33603         e.preventDefault();
33604         
33605         if(this.bgimage.length){
33606             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33607             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33608         }
33609     }
33610     
33611 });
33612
33613  
33614
33615  /*
33616  * - LGPL
33617  *
33618  * Number field 
33619  */
33620
33621 /**
33622  * @class Roo.bootstrap.NumberField
33623  * @extends Roo.bootstrap.Input
33624  * Bootstrap NumberField class
33625  * 
33626  * 
33627  * 
33628  * 
33629  * @constructor
33630  * Create a new NumberField
33631  * @param {Object} config The config object
33632  */
33633
33634 Roo.bootstrap.NumberField = function(config){
33635     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33636 };
33637
33638 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33639     
33640     /**
33641      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33642      */
33643     allowDecimals : true,
33644     /**
33645      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33646      */
33647     decimalSeparator : ".",
33648     /**
33649      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33650      */
33651     decimalPrecision : 2,
33652     /**
33653      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33654      */
33655     allowNegative : true,
33656     
33657     /**
33658      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33659      */
33660     allowZero: true,
33661     /**
33662      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33663      */
33664     minValue : Number.NEGATIVE_INFINITY,
33665     /**
33666      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33667      */
33668     maxValue : Number.MAX_VALUE,
33669     /**
33670      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33671      */
33672     minText : "The minimum value for this field is {0}",
33673     /**
33674      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33675      */
33676     maxText : "The maximum value for this field is {0}",
33677     /**
33678      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33679      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33680      */
33681     nanText : "{0} is not a valid number",
33682     /**
33683      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33684      */
33685     thousandsDelimiter : false,
33686     /**
33687      * @cfg {String} valueAlign alignment of value
33688      */
33689     valueAlign : "left",
33690
33691     getAutoCreate : function()
33692     {
33693         var hiddenInput = {
33694             tag: 'input',
33695             type: 'hidden',
33696             id: Roo.id(),
33697             cls: 'hidden-number-input'
33698         };
33699         
33700         if (this.name) {
33701             hiddenInput.name = this.name;
33702         }
33703         
33704         this.name = '';
33705         
33706         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33707         
33708         this.name = hiddenInput.name;
33709         
33710         if(cfg.cn.length > 0) {
33711             cfg.cn.push(hiddenInput);
33712         }
33713         
33714         return cfg;
33715     },
33716
33717     // private
33718     initEvents : function()
33719     {   
33720         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33721         
33722         var allowed = "0123456789";
33723         
33724         if(this.allowDecimals){
33725             allowed += this.decimalSeparator;
33726         }
33727         
33728         if(this.allowNegative){
33729             allowed += "-";
33730         }
33731         
33732         if(this.thousandsDelimiter) {
33733             allowed += ",";
33734         }
33735         
33736         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33737         
33738         var keyPress = function(e){
33739             
33740             var k = e.getKey();
33741             
33742             var c = e.getCharCode();
33743             
33744             if(
33745                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33746                     allowed.indexOf(String.fromCharCode(c)) === -1
33747             ){
33748                 e.stopEvent();
33749                 return;
33750             }
33751             
33752             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33753                 return;
33754             }
33755             
33756             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33757                 e.stopEvent();
33758             }
33759         };
33760         
33761         this.el.on("keypress", keyPress, this);
33762     },
33763     
33764     validateValue : function(value)
33765     {
33766         
33767         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33768             return false;
33769         }
33770         
33771         var num = this.parseValue(value);
33772         
33773         if(isNaN(num)){
33774             this.markInvalid(String.format(this.nanText, value));
33775             return false;
33776         }
33777         
33778         if(num < this.minValue){
33779             this.markInvalid(String.format(this.minText, this.minValue));
33780             return false;
33781         }
33782         
33783         if(num > this.maxValue){
33784             this.markInvalid(String.format(this.maxText, this.maxValue));
33785             return false;
33786         }
33787         
33788         return true;
33789     },
33790
33791     getValue : function()
33792     {
33793         var v = this.hiddenEl().getValue();
33794         
33795         return this.fixPrecision(this.parseValue(v));
33796     },
33797
33798     parseValue : function(value)
33799     {
33800         if(this.thousandsDelimiter) {
33801             value += "";
33802             r = new RegExp(",", "g");
33803             value = value.replace(r, "");
33804         }
33805         
33806         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33807         return isNaN(value) ? '' : value;
33808     },
33809
33810     fixPrecision : function(value)
33811     {
33812         if(this.thousandsDelimiter) {
33813             value += "";
33814             r = new RegExp(",", "g");
33815             value = value.replace(r, "");
33816         }
33817         
33818         var nan = isNaN(value);
33819         
33820         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33821             return nan ? '' : value;
33822         }
33823         return parseFloat(value).toFixed(this.decimalPrecision);
33824     },
33825
33826     setValue : function(v)
33827     {
33828         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33829         
33830         this.value = v;
33831         
33832         if(this.rendered){
33833             
33834             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33835             
33836             this.inputEl().dom.value = (v == '') ? '' :
33837                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33838             
33839             if(!this.allowZero && v === '0') {
33840                 this.hiddenEl().dom.value = '';
33841                 this.inputEl().dom.value = '';
33842             }
33843             
33844             this.validate();
33845         }
33846     },
33847
33848     decimalPrecisionFcn : function(v)
33849     {
33850         return Math.floor(v);
33851     },
33852
33853     beforeBlur : function()
33854     {
33855         var v = this.parseValue(this.getRawValue());
33856         
33857         if(v || v === 0 || v === ''){
33858             this.setValue(v);
33859         }
33860     },
33861     
33862     hiddenEl : function()
33863     {
33864         return this.el.select('input.hidden-number-input',true).first();
33865     }
33866     
33867 });
33868
33869  
33870
33871 /*
33872 * Licence: LGPL
33873 */
33874
33875 /**
33876  * @class Roo.bootstrap.DocumentSlider
33877  * @extends Roo.bootstrap.Component
33878  * Bootstrap DocumentSlider class
33879  * 
33880  * @constructor
33881  * Create a new DocumentViewer
33882  * @param {Object} config The config object
33883  */
33884
33885 Roo.bootstrap.DocumentSlider = function(config){
33886     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33887     
33888     this.files = [];
33889     
33890     this.addEvents({
33891         /**
33892          * @event initial
33893          * Fire after initEvent
33894          * @param {Roo.bootstrap.DocumentSlider} this
33895          */
33896         "initial" : true,
33897         /**
33898          * @event update
33899          * Fire after update
33900          * @param {Roo.bootstrap.DocumentSlider} this
33901          */
33902         "update" : true,
33903         /**
33904          * @event click
33905          * Fire after click
33906          * @param {Roo.bootstrap.DocumentSlider} this
33907          */
33908         "click" : true
33909     });
33910 };
33911
33912 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33913     
33914     files : false,
33915     
33916     indicator : 0,
33917     
33918     getAutoCreate : function()
33919     {
33920         var cfg = {
33921             tag : 'div',
33922             cls : 'roo-document-slider',
33923             cn : [
33924                 {
33925                     tag : 'div',
33926                     cls : 'roo-document-slider-header',
33927                     cn : [
33928                         {
33929                             tag : 'div',
33930                             cls : 'roo-document-slider-header-title'
33931                         }
33932                     ]
33933                 },
33934                 {
33935                     tag : 'div',
33936                     cls : 'roo-document-slider-body',
33937                     cn : [
33938                         {
33939                             tag : 'div',
33940                             cls : 'roo-document-slider-prev',
33941                             cn : [
33942                                 {
33943                                     tag : 'i',
33944                                     cls : 'fa fa-chevron-left'
33945                                 }
33946                             ]
33947                         },
33948                         {
33949                             tag : 'div',
33950                             cls : 'roo-document-slider-thumb',
33951                             cn : [
33952                                 {
33953                                     tag : 'img',
33954                                     cls : 'roo-document-slider-image'
33955                                 }
33956                             ]
33957                         },
33958                         {
33959                             tag : 'div',
33960                             cls : 'roo-document-slider-next',
33961                             cn : [
33962                                 {
33963                                     tag : 'i',
33964                                     cls : 'fa fa-chevron-right'
33965                                 }
33966                             ]
33967                         }
33968                     ]
33969                 }
33970             ]
33971         };
33972         
33973         return cfg;
33974     },
33975     
33976     initEvents : function()
33977     {
33978         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33979         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33980         
33981         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33982         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33983         
33984         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33985         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33986         
33987         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33988         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33989         
33990         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33991         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33992         
33993         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33994         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33995         
33996         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33997         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33998         
33999         this.thumbEl.on('click', this.onClick, this);
34000         
34001         this.prevIndicator.on('click', this.prev, this);
34002         
34003         this.nextIndicator.on('click', this.next, this);
34004         
34005     },
34006     
34007     initial : function()
34008     {
34009         if(this.files.length){
34010             this.indicator = 1;
34011             this.update()
34012         }
34013         
34014         this.fireEvent('initial', this);
34015     },
34016     
34017     update : function()
34018     {
34019         this.imageEl.attr('src', this.files[this.indicator - 1]);
34020         
34021         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34022         
34023         this.prevIndicator.show();
34024         
34025         if(this.indicator == 1){
34026             this.prevIndicator.hide();
34027         }
34028         
34029         this.nextIndicator.show();
34030         
34031         if(this.indicator == this.files.length){
34032             this.nextIndicator.hide();
34033         }
34034         
34035         this.thumbEl.scrollTo('top');
34036         
34037         this.fireEvent('update', this);
34038     },
34039     
34040     onClick : function(e)
34041     {
34042         e.preventDefault();
34043         
34044         this.fireEvent('click', this);
34045     },
34046     
34047     prev : function(e)
34048     {
34049         e.preventDefault();
34050         
34051         this.indicator = Math.max(1, this.indicator - 1);
34052         
34053         this.update();
34054     },
34055     
34056     next : function(e)
34057     {
34058         e.preventDefault();
34059         
34060         this.indicator = Math.min(this.files.length, this.indicator + 1);
34061         
34062         this.update();
34063     }
34064 });
34065 /*
34066  * - LGPL
34067  *
34068  * RadioSet
34069  *
34070  *
34071  */
34072
34073 /**
34074  * @class Roo.bootstrap.RadioSet
34075  * @extends Roo.bootstrap.Input
34076  * Bootstrap RadioSet class
34077  * @cfg {String} indicatorpos (left|right) default left
34078  * @cfg {Boolean} inline (true|false) inline the element (default true)
34079  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34080  * @constructor
34081  * Create a new RadioSet
34082  * @param {Object} config The config object
34083  */
34084
34085 Roo.bootstrap.RadioSet = function(config){
34086     
34087     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34088     
34089     this.radioes = [];
34090     
34091     Roo.bootstrap.RadioSet.register(this);
34092     
34093     this.addEvents({
34094         /**
34095         * @event check
34096         * Fires when the element is checked or unchecked.
34097         * @param {Roo.bootstrap.RadioSet} this This radio
34098         * @param {Roo.bootstrap.Radio} item The checked item
34099         */
34100        check : true,
34101        /**
34102         * @event click
34103         * Fires when the element is click.
34104         * @param {Roo.bootstrap.RadioSet} this This radio set
34105         * @param {Roo.bootstrap.Radio} item The checked item
34106         * @param {Roo.EventObject} e The event object
34107         */
34108        click : true
34109     });
34110     
34111 };
34112
34113 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34114
34115     radioes : false,
34116     
34117     inline : true,
34118     
34119     weight : '',
34120     
34121     indicatorpos : 'left',
34122     
34123     getAutoCreate : function()
34124     {
34125         var label = {
34126             tag : 'label',
34127             cls : 'roo-radio-set-label',
34128             cn : [
34129                 {
34130                     tag : 'span',
34131                     html : this.fieldLabel
34132                 }
34133             ]
34134         };
34135         if (Roo.bootstrap.version == 3) {
34136             
34137             
34138             if(this.indicatorpos == 'left'){
34139                 label.cn.unshift({
34140                     tag : 'i',
34141                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34142                     tooltip : 'This field is required'
34143                 });
34144             } else {
34145                 label.cn.push({
34146                     tag : 'i',
34147                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34148                     tooltip : 'This field is required'
34149                 });
34150             }
34151         }
34152         var items = {
34153             tag : 'div',
34154             cls : 'roo-radio-set-items'
34155         };
34156         
34157         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34158         
34159         if (align === 'left' && this.fieldLabel.length) {
34160             
34161             items = {
34162                 cls : "roo-radio-set-right", 
34163                 cn: [
34164                     items
34165                 ]
34166             };
34167             
34168             if(this.labelWidth > 12){
34169                 label.style = "width: " + this.labelWidth + 'px';
34170             }
34171             
34172             if(this.labelWidth < 13 && this.labelmd == 0){
34173                 this.labelmd = this.labelWidth;
34174             }
34175             
34176             if(this.labellg > 0){
34177                 label.cls += ' col-lg-' + this.labellg;
34178                 items.cls += ' col-lg-' + (12 - this.labellg);
34179             }
34180             
34181             if(this.labelmd > 0){
34182                 label.cls += ' col-md-' + this.labelmd;
34183                 items.cls += ' col-md-' + (12 - this.labelmd);
34184             }
34185             
34186             if(this.labelsm > 0){
34187                 label.cls += ' col-sm-' + this.labelsm;
34188                 items.cls += ' col-sm-' + (12 - this.labelsm);
34189             }
34190             
34191             if(this.labelxs > 0){
34192                 label.cls += ' col-xs-' + this.labelxs;
34193                 items.cls += ' col-xs-' + (12 - this.labelxs);
34194             }
34195         }
34196         
34197         var cfg = {
34198             tag : 'div',
34199             cls : 'roo-radio-set',
34200             cn : [
34201                 {
34202                     tag : 'input',
34203                     cls : 'roo-radio-set-input',
34204                     type : 'hidden',
34205                     name : this.name,
34206                     value : this.value ? this.value :  ''
34207                 },
34208                 label,
34209                 items
34210             ]
34211         };
34212         
34213         if(this.weight.length){
34214             cfg.cls += ' roo-radio-' + this.weight;
34215         }
34216         
34217         if(this.inline) {
34218             cfg.cls += ' roo-radio-set-inline';
34219         }
34220         
34221         var settings=this;
34222         ['xs','sm','md','lg'].map(function(size){
34223             if (settings[size]) {
34224                 cfg.cls += ' col-' + size + '-' + settings[size];
34225             }
34226         });
34227         
34228         return cfg;
34229         
34230     },
34231
34232     initEvents : function()
34233     {
34234         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34235         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34236         
34237         if(!this.fieldLabel.length){
34238             this.labelEl.hide();
34239         }
34240         
34241         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34242         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34243         
34244         this.indicator = this.indicatorEl();
34245         
34246         if(this.indicator){
34247             this.indicator.addClass('invisible');
34248         }
34249         
34250         this.originalValue = this.getValue();
34251         
34252     },
34253     
34254     inputEl: function ()
34255     {
34256         return this.el.select('.roo-radio-set-input', true).first();
34257     },
34258     
34259     getChildContainer : function()
34260     {
34261         return this.itemsEl;
34262     },
34263     
34264     register : function(item)
34265     {
34266         this.radioes.push(item);
34267         
34268     },
34269     
34270     validate : function()
34271     {   
34272         if(this.getVisibilityEl().hasClass('hidden')){
34273             return true;
34274         }
34275         
34276         var valid = false;
34277         
34278         Roo.each(this.radioes, function(i){
34279             if(!i.checked){
34280                 return;
34281             }
34282             
34283             valid = true;
34284             return false;
34285         });
34286         
34287         if(this.allowBlank) {
34288             return true;
34289         }
34290         
34291         if(this.disabled || valid){
34292             this.markValid();
34293             return true;
34294         }
34295         
34296         this.markInvalid();
34297         return false;
34298         
34299     },
34300     
34301     markValid : function()
34302     {
34303         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34304             this.indicatorEl().removeClass('visible');
34305             this.indicatorEl().addClass('invisible');
34306         }
34307         
34308         
34309         if (Roo.bootstrap.version == 3) {
34310             this.el.removeClass([this.invalidClass, this.validClass]);
34311             this.el.addClass(this.validClass);
34312         } else {
34313             this.el.removeClass(['is-invalid','is-valid']);
34314             this.el.addClass(['is-valid']);
34315         }
34316         this.fireEvent('valid', this);
34317     },
34318     
34319     markInvalid : function(msg)
34320     {
34321         if(this.allowBlank || this.disabled){
34322             return;
34323         }
34324         
34325         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34326             this.indicatorEl().removeClass('invisible');
34327             this.indicatorEl().addClass('visible');
34328         }
34329         if (Roo.bootstrap.version == 3) {
34330             this.el.removeClass([this.invalidClass, this.validClass]);
34331             this.el.addClass(this.invalidClass);
34332         } else {
34333             this.el.removeClass(['is-invalid','is-valid']);
34334             this.el.addClass(['is-invalid']);
34335         }
34336         
34337         this.fireEvent('invalid', this, msg);
34338         
34339     },
34340     
34341     setValue : function(v, suppressEvent)
34342     {   
34343         if(this.value === v){
34344             return;
34345         }
34346         
34347         this.value = v;
34348         
34349         if(this.rendered){
34350             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34351         }
34352         
34353         Roo.each(this.radioes, function(i){
34354             i.checked = false;
34355             i.el.removeClass('checked');
34356         });
34357         
34358         Roo.each(this.radioes, function(i){
34359             
34360             if(i.value === v || i.value.toString() === v.toString()){
34361                 i.checked = true;
34362                 i.el.addClass('checked');
34363                 
34364                 if(suppressEvent !== true){
34365                     this.fireEvent('check', this, i);
34366                 }
34367                 
34368                 return false;
34369             }
34370             
34371         }, this);
34372         
34373         this.validate();
34374     },
34375     
34376     clearInvalid : function(){
34377         
34378         if(!this.el || this.preventMark){
34379             return;
34380         }
34381         
34382         this.el.removeClass([this.invalidClass]);
34383         
34384         this.fireEvent('valid', this);
34385     }
34386     
34387 });
34388
34389 Roo.apply(Roo.bootstrap.RadioSet, {
34390     
34391     groups: {},
34392     
34393     register : function(set)
34394     {
34395         this.groups[set.name] = set;
34396     },
34397     
34398     get: function(name) 
34399     {
34400         if (typeof(this.groups[name]) == 'undefined') {
34401             return false;
34402         }
34403         
34404         return this.groups[name] ;
34405     }
34406     
34407 });
34408 /*
34409  * Based on:
34410  * Ext JS Library 1.1.1
34411  * Copyright(c) 2006-2007, Ext JS, LLC.
34412  *
34413  * Originally Released Under LGPL - original licence link has changed is not relivant.
34414  *
34415  * Fork - LGPL
34416  * <script type="text/javascript">
34417  */
34418
34419
34420 /**
34421  * @class Roo.bootstrap.SplitBar
34422  * @extends Roo.util.Observable
34423  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34424  * <br><br>
34425  * Usage:
34426  * <pre><code>
34427 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34428                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34429 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34430 split.minSize = 100;
34431 split.maxSize = 600;
34432 split.animate = true;
34433 split.on('moved', splitterMoved);
34434 </code></pre>
34435  * @constructor
34436  * Create a new SplitBar
34437  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34438  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34439  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34440  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34441                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34442                         position of the SplitBar).
34443  */
34444 Roo.bootstrap.SplitBar = function(cfg){
34445     
34446     /** @private */
34447     
34448     //{
34449     //  dragElement : elm
34450     //  resizingElement: el,
34451         // optional..
34452     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34453     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34454         // existingProxy ???
34455     //}
34456     
34457     this.el = Roo.get(cfg.dragElement, true);
34458     this.el.dom.unselectable = "on";
34459     /** @private */
34460     this.resizingEl = Roo.get(cfg.resizingElement, true);
34461
34462     /**
34463      * @private
34464      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34465      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34466      * @type Number
34467      */
34468     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34469     
34470     /**
34471      * The minimum size of the resizing element. (Defaults to 0)
34472      * @type Number
34473      */
34474     this.minSize = 0;
34475     
34476     /**
34477      * The maximum size of the resizing element. (Defaults to 2000)
34478      * @type Number
34479      */
34480     this.maxSize = 2000;
34481     
34482     /**
34483      * Whether to animate the transition to the new size
34484      * @type Boolean
34485      */
34486     this.animate = false;
34487     
34488     /**
34489      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34490      * @type Boolean
34491      */
34492     this.useShim = false;
34493     
34494     /** @private */
34495     this.shim = null;
34496     
34497     if(!cfg.existingProxy){
34498         /** @private */
34499         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34500     }else{
34501         this.proxy = Roo.get(cfg.existingProxy).dom;
34502     }
34503     /** @private */
34504     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34505     
34506     /** @private */
34507     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34508     
34509     /** @private */
34510     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34511     
34512     /** @private */
34513     this.dragSpecs = {};
34514     
34515     /**
34516      * @private The adapter to use to positon and resize elements
34517      */
34518     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34519     this.adapter.init(this);
34520     
34521     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34522         /** @private */
34523         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34524         this.el.addClass("roo-splitbar-h");
34525     }else{
34526         /** @private */
34527         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34528         this.el.addClass("roo-splitbar-v");
34529     }
34530     
34531     this.addEvents({
34532         /**
34533          * @event resize
34534          * Fires when the splitter is moved (alias for {@link #event-moved})
34535          * @param {Roo.bootstrap.SplitBar} this
34536          * @param {Number} newSize the new width or height
34537          */
34538         "resize" : true,
34539         /**
34540          * @event moved
34541          * Fires when the splitter is moved
34542          * @param {Roo.bootstrap.SplitBar} this
34543          * @param {Number} newSize the new width or height
34544          */
34545         "moved" : true,
34546         /**
34547          * @event beforeresize
34548          * Fires before the splitter is dragged
34549          * @param {Roo.bootstrap.SplitBar} this
34550          */
34551         "beforeresize" : true,
34552
34553         "beforeapply" : true
34554     });
34555
34556     Roo.util.Observable.call(this);
34557 };
34558
34559 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34560     onStartProxyDrag : function(x, y){
34561         this.fireEvent("beforeresize", this);
34562         if(!this.overlay){
34563             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34564             o.unselectable();
34565             o.enableDisplayMode("block");
34566             // all splitbars share the same overlay
34567             Roo.bootstrap.SplitBar.prototype.overlay = o;
34568         }
34569         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34570         this.overlay.show();
34571         Roo.get(this.proxy).setDisplayed("block");
34572         var size = this.adapter.getElementSize(this);
34573         this.activeMinSize = this.getMinimumSize();;
34574         this.activeMaxSize = this.getMaximumSize();;
34575         var c1 = size - this.activeMinSize;
34576         var c2 = Math.max(this.activeMaxSize - size, 0);
34577         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34578             this.dd.resetConstraints();
34579             this.dd.setXConstraint(
34580                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34581                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34582             );
34583             this.dd.setYConstraint(0, 0);
34584         }else{
34585             this.dd.resetConstraints();
34586             this.dd.setXConstraint(0, 0);
34587             this.dd.setYConstraint(
34588                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34589                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34590             );
34591          }
34592         this.dragSpecs.startSize = size;
34593         this.dragSpecs.startPoint = [x, y];
34594         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34595     },
34596     
34597     /** 
34598      * @private Called after the drag operation by the DDProxy
34599      */
34600     onEndProxyDrag : function(e){
34601         Roo.get(this.proxy).setDisplayed(false);
34602         var endPoint = Roo.lib.Event.getXY(e);
34603         if(this.overlay){
34604             this.overlay.hide();
34605         }
34606         var newSize;
34607         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34608             newSize = this.dragSpecs.startSize + 
34609                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34610                     endPoint[0] - this.dragSpecs.startPoint[0] :
34611                     this.dragSpecs.startPoint[0] - endPoint[0]
34612                 );
34613         }else{
34614             newSize = this.dragSpecs.startSize + 
34615                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34616                     endPoint[1] - this.dragSpecs.startPoint[1] :
34617                     this.dragSpecs.startPoint[1] - endPoint[1]
34618                 );
34619         }
34620         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34621         if(newSize != this.dragSpecs.startSize){
34622             if(this.fireEvent('beforeapply', this, newSize) !== false){
34623                 this.adapter.setElementSize(this, newSize);
34624                 this.fireEvent("moved", this, newSize);
34625                 this.fireEvent("resize", this, newSize);
34626             }
34627         }
34628     },
34629     
34630     /**
34631      * Get the adapter this SplitBar uses
34632      * @return The adapter object
34633      */
34634     getAdapter : function(){
34635         return this.adapter;
34636     },
34637     
34638     /**
34639      * Set the adapter this SplitBar uses
34640      * @param {Object} adapter A SplitBar adapter object
34641      */
34642     setAdapter : function(adapter){
34643         this.adapter = adapter;
34644         this.adapter.init(this);
34645     },
34646     
34647     /**
34648      * Gets the minimum size for the resizing element
34649      * @return {Number} The minimum size
34650      */
34651     getMinimumSize : function(){
34652         return this.minSize;
34653     },
34654     
34655     /**
34656      * Sets the minimum size for the resizing element
34657      * @param {Number} minSize The minimum size
34658      */
34659     setMinimumSize : function(minSize){
34660         this.minSize = minSize;
34661     },
34662     
34663     /**
34664      * Gets the maximum size for the resizing element
34665      * @return {Number} The maximum size
34666      */
34667     getMaximumSize : function(){
34668         return this.maxSize;
34669     },
34670     
34671     /**
34672      * Sets the maximum size for the resizing element
34673      * @param {Number} maxSize The maximum size
34674      */
34675     setMaximumSize : function(maxSize){
34676         this.maxSize = maxSize;
34677     },
34678     
34679     /**
34680      * Sets the initialize size for the resizing element
34681      * @param {Number} size The initial size
34682      */
34683     setCurrentSize : function(size){
34684         var oldAnimate = this.animate;
34685         this.animate = false;
34686         this.adapter.setElementSize(this, size);
34687         this.animate = oldAnimate;
34688     },
34689     
34690     /**
34691      * Destroy this splitbar. 
34692      * @param {Boolean} removeEl True to remove the element
34693      */
34694     destroy : function(removeEl){
34695         if(this.shim){
34696             this.shim.remove();
34697         }
34698         this.dd.unreg();
34699         this.proxy.parentNode.removeChild(this.proxy);
34700         if(removeEl){
34701             this.el.remove();
34702         }
34703     }
34704 });
34705
34706 /**
34707  * @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.
34708  */
34709 Roo.bootstrap.SplitBar.createProxy = function(dir){
34710     var proxy = new Roo.Element(document.createElement("div"));
34711     proxy.unselectable();
34712     var cls = 'roo-splitbar-proxy';
34713     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34714     document.body.appendChild(proxy.dom);
34715     return proxy.dom;
34716 };
34717
34718 /** 
34719  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34720  * Default Adapter. It assumes the splitter and resizing element are not positioned
34721  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34722  */
34723 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34724 };
34725
34726 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34727     // do nothing for now
34728     init : function(s){
34729     
34730     },
34731     /**
34732      * Called before drag operations to get the current size of the resizing element. 
34733      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34734      */
34735      getElementSize : function(s){
34736         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34737             return s.resizingEl.getWidth();
34738         }else{
34739             return s.resizingEl.getHeight();
34740         }
34741     },
34742     
34743     /**
34744      * Called after drag operations to set the size of the resizing element.
34745      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34746      * @param {Number} newSize The new size to set
34747      * @param {Function} onComplete A function to be invoked when resizing is complete
34748      */
34749     setElementSize : function(s, newSize, onComplete){
34750         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34751             if(!s.animate){
34752                 s.resizingEl.setWidth(newSize);
34753                 if(onComplete){
34754                     onComplete(s, newSize);
34755                 }
34756             }else{
34757                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34758             }
34759         }else{
34760             
34761             if(!s.animate){
34762                 s.resizingEl.setHeight(newSize);
34763                 if(onComplete){
34764                     onComplete(s, newSize);
34765                 }
34766             }else{
34767                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34768             }
34769         }
34770     }
34771 };
34772
34773 /** 
34774  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34775  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34776  * Adapter that  moves the splitter element to align with the resized sizing element. 
34777  * Used with an absolute positioned SplitBar.
34778  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34779  * document.body, make sure you assign an id to the body element.
34780  */
34781 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34782     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34783     this.container = Roo.get(container);
34784 };
34785
34786 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34787     init : function(s){
34788         this.basic.init(s);
34789     },
34790     
34791     getElementSize : function(s){
34792         return this.basic.getElementSize(s);
34793     },
34794     
34795     setElementSize : function(s, newSize, onComplete){
34796         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34797     },
34798     
34799     moveSplitter : function(s){
34800         var yes = Roo.bootstrap.SplitBar;
34801         switch(s.placement){
34802             case yes.LEFT:
34803                 s.el.setX(s.resizingEl.getRight());
34804                 break;
34805             case yes.RIGHT:
34806                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34807                 break;
34808             case yes.TOP:
34809                 s.el.setY(s.resizingEl.getBottom());
34810                 break;
34811             case yes.BOTTOM:
34812                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34813                 break;
34814         }
34815     }
34816 };
34817
34818 /**
34819  * Orientation constant - Create a vertical SplitBar
34820  * @static
34821  * @type Number
34822  */
34823 Roo.bootstrap.SplitBar.VERTICAL = 1;
34824
34825 /**
34826  * Orientation constant - Create a horizontal SplitBar
34827  * @static
34828  * @type Number
34829  */
34830 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34831
34832 /**
34833  * Placement constant - The resizing element is to the left of the splitter element
34834  * @static
34835  * @type Number
34836  */
34837 Roo.bootstrap.SplitBar.LEFT = 1;
34838
34839 /**
34840  * Placement constant - The resizing element is to the right of the splitter element
34841  * @static
34842  * @type Number
34843  */
34844 Roo.bootstrap.SplitBar.RIGHT = 2;
34845
34846 /**
34847  * Placement constant - The resizing element is positioned above the splitter element
34848  * @static
34849  * @type Number
34850  */
34851 Roo.bootstrap.SplitBar.TOP = 3;
34852
34853 /**
34854  * Placement constant - The resizing element is positioned under splitter element
34855  * @static
34856  * @type Number
34857  */
34858 Roo.bootstrap.SplitBar.BOTTOM = 4;
34859 Roo.namespace("Roo.bootstrap.layout");/*
34860  * Based on:
34861  * Ext JS Library 1.1.1
34862  * Copyright(c) 2006-2007, Ext JS, LLC.
34863  *
34864  * Originally Released Under LGPL - original licence link has changed is not relivant.
34865  *
34866  * Fork - LGPL
34867  * <script type="text/javascript">
34868  */
34869
34870 /**
34871  * @class Roo.bootstrap.layout.Manager
34872  * @extends Roo.bootstrap.Component
34873  * Base class for layout managers.
34874  */
34875 Roo.bootstrap.layout.Manager = function(config)
34876 {
34877     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34878
34879
34880
34881
34882
34883     /** false to disable window resize monitoring @type Boolean */
34884     this.monitorWindowResize = true;
34885     this.regions = {};
34886     this.addEvents({
34887         /**
34888          * @event layout
34889          * Fires when a layout is performed.
34890          * @param {Roo.LayoutManager} this
34891          */
34892         "layout" : true,
34893         /**
34894          * @event regionresized
34895          * Fires when the user resizes a region.
34896          * @param {Roo.LayoutRegion} region The resized region
34897          * @param {Number} newSize The new size (width for east/west, height for north/south)
34898          */
34899         "regionresized" : true,
34900         /**
34901          * @event regioncollapsed
34902          * Fires when a region is collapsed.
34903          * @param {Roo.LayoutRegion} region The collapsed region
34904          */
34905         "regioncollapsed" : true,
34906         /**
34907          * @event regionexpanded
34908          * Fires when a region is expanded.
34909          * @param {Roo.LayoutRegion} region The expanded region
34910          */
34911         "regionexpanded" : true
34912     });
34913     this.updating = false;
34914
34915     if (config.el) {
34916         this.el = Roo.get(config.el);
34917         this.initEvents();
34918     }
34919
34920 };
34921
34922 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34923
34924
34925     regions : null,
34926
34927     monitorWindowResize : true,
34928
34929
34930     updating : false,
34931
34932
34933     onRender : function(ct, position)
34934     {
34935         if(!this.el){
34936             this.el = Roo.get(ct);
34937             this.initEvents();
34938         }
34939         //this.fireEvent('render',this);
34940     },
34941
34942
34943     initEvents: function()
34944     {
34945
34946
34947         // ie scrollbar fix
34948         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34949             document.body.scroll = "no";
34950         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34951             this.el.position('relative');
34952         }
34953         this.id = this.el.id;
34954         this.el.addClass("roo-layout-container");
34955         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34956         if(this.el.dom != document.body ) {
34957             this.el.on('resize', this.layout,this);
34958             this.el.on('show', this.layout,this);
34959         }
34960
34961     },
34962
34963     /**
34964      * Returns true if this layout is currently being updated
34965      * @return {Boolean}
34966      */
34967     isUpdating : function(){
34968         return this.updating;
34969     },
34970
34971     /**
34972      * Suspend the LayoutManager from doing auto-layouts while
34973      * making multiple add or remove calls
34974      */
34975     beginUpdate : function(){
34976         this.updating = true;
34977     },
34978
34979     /**
34980      * Restore auto-layouts and optionally disable the manager from performing a layout
34981      * @param {Boolean} noLayout true to disable a layout update
34982      */
34983     endUpdate : function(noLayout){
34984         this.updating = false;
34985         if(!noLayout){
34986             this.layout();
34987         }
34988     },
34989
34990     layout: function(){
34991         // abstract...
34992     },
34993
34994     onRegionResized : function(region, newSize){
34995         this.fireEvent("regionresized", region, newSize);
34996         this.layout();
34997     },
34998
34999     onRegionCollapsed : function(region){
35000         this.fireEvent("regioncollapsed", region);
35001     },
35002
35003     onRegionExpanded : function(region){
35004         this.fireEvent("regionexpanded", region);
35005     },
35006
35007     /**
35008      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35009      * performs box-model adjustments.
35010      * @return {Object} The size as an object {width: (the width), height: (the height)}
35011      */
35012     getViewSize : function()
35013     {
35014         var size;
35015         if(this.el.dom != document.body){
35016             size = this.el.getSize();
35017         }else{
35018             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35019         }
35020         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35021         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35022         return size;
35023     },
35024
35025     /**
35026      * Returns the Element this layout is bound to.
35027      * @return {Roo.Element}
35028      */
35029     getEl : function(){
35030         return this.el;
35031     },
35032
35033     /**
35034      * Returns the specified region.
35035      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35036      * @return {Roo.LayoutRegion}
35037      */
35038     getRegion : function(target){
35039         return this.regions[target.toLowerCase()];
35040     },
35041
35042     onWindowResize : function(){
35043         if(this.monitorWindowResize){
35044             this.layout();
35045         }
35046     }
35047 });
35048 /*
35049  * Based on:
35050  * Ext JS Library 1.1.1
35051  * Copyright(c) 2006-2007, Ext JS, LLC.
35052  *
35053  * Originally Released Under LGPL - original licence link has changed is not relivant.
35054  *
35055  * Fork - LGPL
35056  * <script type="text/javascript">
35057  */
35058 /**
35059  * @class Roo.bootstrap.layout.Border
35060  * @extends Roo.bootstrap.layout.Manager
35061  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35062  * please see: examples/bootstrap/nested.html<br><br>
35063  
35064 <b>The container the layout is rendered into can be either the body element or any other element.
35065 If it is not the body element, the container needs to either be an absolute positioned element,
35066 or you will need to add "position:relative" to the css of the container.  You will also need to specify
35067 the container size if it is not the body element.</b>
35068
35069 * @constructor
35070 * Create a new Border
35071 * @param {Object} config Configuration options
35072  */
35073 Roo.bootstrap.layout.Border = function(config){
35074     config = config || {};
35075     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35076     
35077     
35078     
35079     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35080         if(config[region]){
35081             config[region].region = region;
35082             this.addRegion(config[region]);
35083         }
35084     },this);
35085     
35086 };
35087
35088 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35089
35090 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35091     /**
35092      * Creates and adds a new region if it doesn't already exist.
35093      * @param {String} target The target region key (north, south, east, west or center).
35094      * @param {Object} config The regions config object
35095      * @return {BorderLayoutRegion} The new region
35096      */
35097     addRegion : function(config)
35098     {
35099         if(!this.regions[config.region]){
35100             var r = this.factory(config);
35101             this.bindRegion(r);
35102         }
35103         return this.regions[config.region];
35104     },
35105
35106     // private (kinda)
35107     bindRegion : function(r){
35108         this.regions[r.config.region] = r;
35109         
35110         r.on("visibilitychange",    this.layout, this);
35111         r.on("paneladded",          this.layout, this);
35112         r.on("panelremoved",        this.layout, this);
35113         r.on("invalidated",         this.layout, this);
35114         r.on("resized",             this.onRegionResized, this);
35115         r.on("collapsed",           this.onRegionCollapsed, this);
35116         r.on("expanded",            this.onRegionExpanded, this);
35117     },
35118
35119     /**
35120      * Performs a layout update.
35121      */
35122     layout : function()
35123     {
35124         if(this.updating) {
35125             return;
35126         }
35127         
35128         // render all the rebions if they have not been done alreayd?
35129         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35130             if(this.regions[region] && !this.regions[region].bodyEl){
35131                 this.regions[region].onRender(this.el)
35132             }
35133         },this);
35134         
35135         var size = this.getViewSize();
35136         var w = size.width;
35137         var h = size.height;
35138         var centerW = w;
35139         var centerH = h;
35140         var centerY = 0;
35141         var centerX = 0;
35142         //var x = 0, y = 0;
35143
35144         var rs = this.regions;
35145         var north = rs["north"];
35146         var south = rs["south"]; 
35147         var west = rs["west"];
35148         var east = rs["east"];
35149         var center = rs["center"];
35150         //if(this.hideOnLayout){ // not supported anymore
35151             //c.el.setStyle("display", "none");
35152         //}
35153         if(north && north.isVisible()){
35154             var b = north.getBox();
35155             var m = north.getMargins();
35156             b.width = w - (m.left+m.right);
35157             b.x = m.left;
35158             b.y = m.top;
35159             centerY = b.height + b.y + m.bottom;
35160             centerH -= centerY;
35161             north.updateBox(this.safeBox(b));
35162         }
35163         if(south && south.isVisible()){
35164             var b = south.getBox();
35165             var m = south.getMargins();
35166             b.width = w - (m.left+m.right);
35167             b.x = m.left;
35168             var totalHeight = (b.height + m.top + m.bottom);
35169             b.y = h - totalHeight + m.top;
35170             centerH -= totalHeight;
35171             south.updateBox(this.safeBox(b));
35172         }
35173         if(west && west.isVisible()){
35174             var b = west.getBox();
35175             var m = west.getMargins();
35176             b.height = centerH - (m.top+m.bottom);
35177             b.x = m.left;
35178             b.y = centerY + m.top;
35179             var totalWidth = (b.width + m.left + m.right);
35180             centerX += totalWidth;
35181             centerW -= totalWidth;
35182             west.updateBox(this.safeBox(b));
35183         }
35184         if(east && east.isVisible()){
35185             var b = east.getBox();
35186             var m = east.getMargins();
35187             b.height = centerH - (m.top+m.bottom);
35188             var totalWidth = (b.width + m.left + m.right);
35189             b.x = w - totalWidth + m.left;
35190             b.y = centerY + m.top;
35191             centerW -= totalWidth;
35192             east.updateBox(this.safeBox(b));
35193         }
35194         if(center){
35195             var m = center.getMargins();
35196             var centerBox = {
35197                 x: centerX + m.left,
35198                 y: centerY + m.top,
35199                 width: centerW - (m.left+m.right),
35200                 height: centerH - (m.top+m.bottom)
35201             };
35202             //if(this.hideOnLayout){
35203                 //center.el.setStyle("display", "block");
35204             //}
35205             center.updateBox(this.safeBox(centerBox));
35206         }
35207         this.el.repaint();
35208         this.fireEvent("layout", this);
35209     },
35210
35211     // private
35212     safeBox : function(box){
35213         box.width = Math.max(0, box.width);
35214         box.height = Math.max(0, box.height);
35215         return box;
35216     },
35217
35218     /**
35219      * Adds a ContentPanel (or subclass) to this layout.
35220      * @param {String} target The target region key (north, south, east, west or center).
35221      * @param {Roo.ContentPanel} panel The panel to add
35222      * @return {Roo.ContentPanel} The added panel
35223      */
35224     add : function(target, panel){
35225          
35226         target = target.toLowerCase();
35227         return this.regions[target].add(panel);
35228     },
35229
35230     /**
35231      * Remove a ContentPanel (or subclass) to this layout.
35232      * @param {String} target The target region key (north, south, east, west or center).
35233      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35234      * @return {Roo.ContentPanel} The removed panel
35235      */
35236     remove : function(target, panel){
35237         target = target.toLowerCase();
35238         return this.regions[target].remove(panel);
35239     },
35240
35241     /**
35242      * Searches all regions for a panel with the specified id
35243      * @param {String} panelId
35244      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35245      */
35246     findPanel : function(panelId){
35247         var rs = this.regions;
35248         for(var target in rs){
35249             if(typeof rs[target] != "function"){
35250                 var p = rs[target].getPanel(panelId);
35251                 if(p){
35252                     return p;
35253                 }
35254             }
35255         }
35256         return null;
35257     },
35258
35259     /**
35260      * Searches all regions for a panel with the specified id and activates (shows) it.
35261      * @param {String/ContentPanel} panelId The panels id or the panel itself
35262      * @return {Roo.ContentPanel} The shown panel or null
35263      */
35264     showPanel : function(panelId) {
35265       var rs = this.regions;
35266       for(var target in rs){
35267          var r = rs[target];
35268          if(typeof r != "function"){
35269             if(r.hasPanel(panelId)){
35270                return r.showPanel(panelId);
35271             }
35272          }
35273       }
35274       return null;
35275    },
35276
35277    /**
35278      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35279      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35280      */
35281    /*
35282     restoreState : function(provider){
35283         if(!provider){
35284             provider = Roo.state.Manager;
35285         }
35286         var sm = new Roo.LayoutStateManager();
35287         sm.init(this, provider);
35288     },
35289 */
35290  
35291  
35292     /**
35293      * Adds a xtype elements to the layout.
35294      * <pre><code>
35295
35296 layout.addxtype({
35297        xtype : 'ContentPanel',
35298        region: 'west',
35299        items: [ .... ]
35300    }
35301 );
35302
35303 layout.addxtype({
35304         xtype : 'NestedLayoutPanel',
35305         region: 'west',
35306         layout: {
35307            center: { },
35308            west: { }   
35309         },
35310         items : [ ... list of content panels or nested layout panels.. ]
35311    }
35312 );
35313 </code></pre>
35314      * @param {Object} cfg Xtype definition of item to add.
35315      */
35316     addxtype : function(cfg)
35317     {
35318         // basically accepts a pannel...
35319         // can accept a layout region..!?!?
35320         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35321         
35322         
35323         // theory?  children can only be panels??
35324         
35325         //if (!cfg.xtype.match(/Panel$/)) {
35326         //    return false;
35327         //}
35328         var ret = false;
35329         
35330         if (typeof(cfg.region) == 'undefined') {
35331             Roo.log("Failed to add Panel, region was not set");
35332             Roo.log(cfg);
35333             return false;
35334         }
35335         var region = cfg.region;
35336         delete cfg.region;
35337         
35338           
35339         var xitems = [];
35340         if (cfg.items) {
35341             xitems = cfg.items;
35342             delete cfg.items;
35343         }
35344         var nb = false;
35345         
35346         switch(cfg.xtype) 
35347         {
35348             case 'Content':  // ContentPanel (el, cfg)
35349             case 'Scroll':  // ContentPanel (el, cfg)
35350             case 'View': 
35351                 cfg.autoCreate = true;
35352                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35353                 //} else {
35354                 //    var el = this.el.createChild();
35355                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35356                 //}
35357                 
35358                 this.add(region, ret);
35359                 break;
35360             
35361             /*
35362             case 'TreePanel': // our new panel!
35363                 cfg.el = this.el.createChild();
35364                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35365                 this.add(region, ret);
35366                 break;
35367             */
35368             
35369             case 'Nest': 
35370                 // create a new Layout (which is  a Border Layout...
35371                 
35372                 var clayout = cfg.layout;
35373                 clayout.el  = this.el.createChild();
35374                 clayout.items   = clayout.items  || [];
35375                 
35376                 delete cfg.layout;
35377                 
35378                 // replace this exitems with the clayout ones..
35379                 xitems = clayout.items;
35380                  
35381                 // force background off if it's in center...
35382                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35383                     cfg.background = false;
35384                 }
35385                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35386                 
35387                 
35388                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35389                 //console.log('adding nested layout panel '  + cfg.toSource());
35390                 this.add(region, ret);
35391                 nb = {}; /// find first...
35392                 break;
35393             
35394             case 'Grid':
35395                 
35396                 // needs grid and region
35397                 
35398                 //var el = this.getRegion(region).el.createChild();
35399                 /*
35400                  *var el = this.el.createChild();
35401                 // create the grid first...
35402                 cfg.grid.container = el;
35403                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35404                 */
35405                 
35406                 if (region == 'center' && this.active ) {
35407                     cfg.background = false;
35408                 }
35409                 
35410                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35411                 
35412                 this.add(region, ret);
35413                 /*
35414                 if (cfg.background) {
35415                     // render grid on panel activation (if panel background)
35416                     ret.on('activate', function(gp) {
35417                         if (!gp.grid.rendered) {
35418                     //        gp.grid.render(el);
35419                         }
35420                     });
35421                 } else {
35422                   //  cfg.grid.render(el);
35423                 }
35424                 */
35425                 break;
35426            
35427            
35428             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35429                 // it was the old xcomponent building that caused this before.
35430                 // espeically if border is the top element in the tree.
35431                 ret = this;
35432                 break; 
35433                 
35434                     
35435                 
35436                 
35437                 
35438             default:
35439                 /*
35440                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35441                     
35442                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35443                     this.add(region, ret);
35444                 } else {
35445                 */
35446                     Roo.log(cfg);
35447                     throw "Can not add '" + cfg.xtype + "' to Border";
35448                     return null;
35449              
35450                                 
35451              
35452         }
35453         this.beginUpdate();
35454         // add children..
35455         var region = '';
35456         var abn = {};
35457         Roo.each(xitems, function(i)  {
35458             region = nb && i.region ? i.region : false;
35459             
35460             var add = ret.addxtype(i);
35461            
35462             if (region) {
35463                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35464                 if (!i.background) {
35465                     abn[region] = nb[region] ;
35466                 }
35467             }
35468             
35469         });
35470         this.endUpdate();
35471
35472         // make the last non-background panel active..
35473         //if (nb) { Roo.log(abn); }
35474         if (nb) {
35475             
35476             for(var r in abn) {
35477                 region = this.getRegion(r);
35478                 if (region) {
35479                     // tried using nb[r], but it does not work..
35480                      
35481                     region.showPanel(abn[r]);
35482                    
35483                 }
35484             }
35485         }
35486         return ret;
35487         
35488     },
35489     
35490     
35491 // private
35492     factory : function(cfg)
35493     {
35494         
35495         var validRegions = Roo.bootstrap.layout.Border.regions;
35496
35497         var target = cfg.region;
35498         cfg.mgr = this;
35499         
35500         var r = Roo.bootstrap.layout;
35501         Roo.log(target);
35502         switch(target){
35503             case "north":
35504                 return new r.North(cfg);
35505             case "south":
35506                 return new r.South(cfg);
35507             case "east":
35508                 return new r.East(cfg);
35509             case "west":
35510                 return new r.West(cfg);
35511             case "center":
35512                 return new r.Center(cfg);
35513         }
35514         throw 'Layout region "'+target+'" not supported.';
35515     }
35516     
35517     
35518 });
35519  /*
35520  * Based on:
35521  * Ext JS Library 1.1.1
35522  * Copyright(c) 2006-2007, Ext JS, LLC.
35523  *
35524  * Originally Released Under LGPL - original licence link has changed is not relivant.
35525  *
35526  * Fork - LGPL
35527  * <script type="text/javascript">
35528  */
35529  
35530 /**
35531  * @class Roo.bootstrap.layout.Basic
35532  * @extends Roo.util.Observable
35533  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35534  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35535  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35536  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35537  * @cfg {string}   region  the region that it inhabits..
35538  * @cfg {bool}   skipConfig skip config?
35539  * 
35540
35541  */
35542 Roo.bootstrap.layout.Basic = function(config){
35543     
35544     this.mgr = config.mgr;
35545     
35546     this.position = config.region;
35547     
35548     var skipConfig = config.skipConfig;
35549     
35550     this.events = {
35551         /**
35552          * @scope Roo.BasicLayoutRegion
35553          */
35554         
35555         /**
35556          * @event beforeremove
35557          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35558          * @param {Roo.LayoutRegion} this
35559          * @param {Roo.ContentPanel} panel The panel
35560          * @param {Object} e The cancel event object
35561          */
35562         "beforeremove" : true,
35563         /**
35564          * @event invalidated
35565          * Fires when the layout for this region is changed.
35566          * @param {Roo.LayoutRegion} this
35567          */
35568         "invalidated" : true,
35569         /**
35570          * @event visibilitychange
35571          * Fires when this region is shown or hidden 
35572          * @param {Roo.LayoutRegion} this
35573          * @param {Boolean} visibility true or false
35574          */
35575         "visibilitychange" : true,
35576         /**
35577          * @event paneladded
35578          * Fires when a panel is added. 
35579          * @param {Roo.LayoutRegion} this
35580          * @param {Roo.ContentPanel} panel The panel
35581          */
35582         "paneladded" : true,
35583         /**
35584          * @event panelremoved
35585          * Fires when a panel is removed. 
35586          * @param {Roo.LayoutRegion} this
35587          * @param {Roo.ContentPanel} panel The panel
35588          */
35589         "panelremoved" : true,
35590         /**
35591          * @event beforecollapse
35592          * Fires when this region before collapse.
35593          * @param {Roo.LayoutRegion} this
35594          */
35595         "beforecollapse" : true,
35596         /**
35597          * @event collapsed
35598          * Fires when this region is collapsed.
35599          * @param {Roo.LayoutRegion} this
35600          */
35601         "collapsed" : true,
35602         /**
35603          * @event expanded
35604          * Fires when this region is expanded.
35605          * @param {Roo.LayoutRegion} this
35606          */
35607         "expanded" : true,
35608         /**
35609          * @event slideshow
35610          * Fires when this region is slid into view.
35611          * @param {Roo.LayoutRegion} this
35612          */
35613         "slideshow" : true,
35614         /**
35615          * @event slidehide
35616          * Fires when this region slides out of view. 
35617          * @param {Roo.LayoutRegion} this
35618          */
35619         "slidehide" : true,
35620         /**
35621          * @event panelactivated
35622          * Fires when a panel is activated. 
35623          * @param {Roo.LayoutRegion} this
35624          * @param {Roo.ContentPanel} panel The activated panel
35625          */
35626         "panelactivated" : true,
35627         /**
35628          * @event resized
35629          * Fires when the user resizes this region. 
35630          * @param {Roo.LayoutRegion} this
35631          * @param {Number} newSize The new size (width for east/west, height for north/south)
35632          */
35633         "resized" : true
35634     };
35635     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35636     this.panels = new Roo.util.MixedCollection();
35637     this.panels.getKey = this.getPanelId.createDelegate(this);
35638     this.box = null;
35639     this.activePanel = null;
35640     // ensure listeners are added...
35641     
35642     if (config.listeners || config.events) {
35643         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35644             listeners : config.listeners || {},
35645             events : config.events || {}
35646         });
35647     }
35648     
35649     if(skipConfig !== true){
35650         this.applyConfig(config);
35651     }
35652 };
35653
35654 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35655 {
35656     getPanelId : function(p){
35657         return p.getId();
35658     },
35659     
35660     applyConfig : function(config){
35661         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35662         this.config = config;
35663         
35664     },
35665     
35666     /**
35667      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35668      * the width, for horizontal (north, south) the height.
35669      * @param {Number} newSize The new width or height
35670      */
35671     resizeTo : function(newSize){
35672         var el = this.el ? this.el :
35673                  (this.activePanel ? this.activePanel.getEl() : null);
35674         if(el){
35675             switch(this.position){
35676                 case "east":
35677                 case "west":
35678                     el.setWidth(newSize);
35679                     this.fireEvent("resized", this, newSize);
35680                 break;
35681                 case "north":
35682                 case "south":
35683                     el.setHeight(newSize);
35684                     this.fireEvent("resized", this, newSize);
35685                 break;                
35686             }
35687         }
35688     },
35689     
35690     getBox : function(){
35691         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35692     },
35693     
35694     getMargins : function(){
35695         return this.margins;
35696     },
35697     
35698     updateBox : function(box){
35699         this.box = box;
35700         var el = this.activePanel.getEl();
35701         el.dom.style.left = box.x + "px";
35702         el.dom.style.top = box.y + "px";
35703         this.activePanel.setSize(box.width, box.height);
35704     },
35705     
35706     /**
35707      * Returns the container element for this region.
35708      * @return {Roo.Element}
35709      */
35710     getEl : function(){
35711         return this.activePanel;
35712     },
35713     
35714     /**
35715      * Returns true if this region is currently visible.
35716      * @return {Boolean}
35717      */
35718     isVisible : function(){
35719         return this.activePanel ? true : false;
35720     },
35721     
35722     setActivePanel : function(panel){
35723         panel = this.getPanel(panel);
35724         if(this.activePanel && this.activePanel != panel){
35725             this.activePanel.setActiveState(false);
35726             this.activePanel.getEl().setLeftTop(-10000,-10000);
35727         }
35728         this.activePanel = panel;
35729         panel.setActiveState(true);
35730         if(this.box){
35731             panel.setSize(this.box.width, this.box.height);
35732         }
35733         this.fireEvent("panelactivated", this, panel);
35734         this.fireEvent("invalidated");
35735     },
35736     
35737     /**
35738      * Show the specified panel.
35739      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35740      * @return {Roo.ContentPanel} The shown panel or null
35741      */
35742     showPanel : function(panel){
35743         panel = this.getPanel(panel);
35744         if(panel){
35745             this.setActivePanel(panel);
35746         }
35747         return panel;
35748     },
35749     
35750     /**
35751      * Get the active panel for this region.
35752      * @return {Roo.ContentPanel} The active panel or null
35753      */
35754     getActivePanel : function(){
35755         return this.activePanel;
35756     },
35757     
35758     /**
35759      * Add the passed ContentPanel(s)
35760      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35761      * @return {Roo.ContentPanel} The panel added (if only one was added)
35762      */
35763     add : function(panel){
35764         if(arguments.length > 1){
35765             for(var i = 0, len = arguments.length; i < len; i++) {
35766                 this.add(arguments[i]);
35767             }
35768             return null;
35769         }
35770         if(this.hasPanel(panel)){
35771             this.showPanel(panel);
35772             return panel;
35773         }
35774         var el = panel.getEl();
35775         if(el.dom.parentNode != this.mgr.el.dom){
35776             this.mgr.el.dom.appendChild(el.dom);
35777         }
35778         if(panel.setRegion){
35779             panel.setRegion(this);
35780         }
35781         this.panels.add(panel);
35782         el.setStyle("position", "absolute");
35783         if(!panel.background){
35784             this.setActivePanel(panel);
35785             if(this.config.initialSize && this.panels.getCount()==1){
35786                 this.resizeTo(this.config.initialSize);
35787             }
35788         }
35789         this.fireEvent("paneladded", this, panel);
35790         return panel;
35791     },
35792     
35793     /**
35794      * Returns true if the panel is in this region.
35795      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35796      * @return {Boolean}
35797      */
35798     hasPanel : function(panel){
35799         if(typeof panel == "object"){ // must be panel obj
35800             panel = panel.getId();
35801         }
35802         return this.getPanel(panel) ? true : false;
35803     },
35804     
35805     /**
35806      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35807      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35808      * @param {Boolean} preservePanel Overrides the config preservePanel option
35809      * @return {Roo.ContentPanel} The panel that was removed
35810      */
35811     remove : function(panel, preservePanel){
35812         panel = this.getPanel(panel);
35813         if(!panel){
35814             return null;
35815         }
35816         var e = {};
35817         this.fireEvent("beforeremove", this, panel, e);
35818         if(e.cancel === true){
35819             return null;
35820         }
35821         var panelId = panel.getId();
35822         this.panels.removeKey(panelId);
35823         return panel;
35824     },
35825     
35826     /**
35827      * Returns the panel specified or null if it's not in this region.
35828      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35829      * @return {Roo.ContentPanel}
35830      */
35831     getPanel : function(id){
35832         if(typeof id == "object"){ // must be panel obj
35833             return id;
35834         }
35835         return this.panels.get(id);
35836     },
35837     
35838     /**
35839      * Returns this regions position (north/south/east/west/center).
35840      * @return {String} 
35841      */
35842     getPosition: function(){
35843         return this.position;    
35844     }
35845 });/*
35846  * Based on:
35847  * Ext JS Library 1.1.1
35848  * Copyright(c) 2006-2007, Ext JS, LLC.
35849  *
35850  * Originally Released Under LGPL - original licence link has changed is not relivant.
35851  *
35852  * Fork - LGPL
35853  * <script type="text/javascript">
35854  */
35855  
35856 /**
35857  * @class Roo.bootstrap.layout.Region
35858  * @extends Roo.bootstrap.layout.Basic
35859  * This class represents a region in a layout manager.
35860  
35861  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35862  * @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})
35863  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35864  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35865  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35866  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35867  * @cfg {String}    title           The title for the region (overrides panel titles)
35868  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35869  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35870  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35871  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35872  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35873  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35874  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35875  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35876  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35877  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35878
35879  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35880  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35881  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35882  * @cfg {Number}    width           For East/West panels
35883  * @cfg {Number}    height          For North/South panels
35884  * @cfg {Boolean}   split           To show the splitter
35885  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35886  * 
35887  * @cfg {string}   cls             Extra CSS classes to add to region
35888  * 
35889  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35890  * @cfg {string}   region  the region that it inhabits..
35891  *
35892
35893  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35894  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35895
35896  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35897  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35898  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35899  */
35900 Roo.bootstrap.layout.Region = function(config)
35901 {
35902     this.applyConfig(config);
35903
35904     var mgr = config.mgr;
35905     var pos = config.region;
35906     config.skipConfig = true;
35907     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35908     
35909     if (mgr.el) {
35910         this.onRender(mgr.el);   
35911     }
35912      
35913     this.visible = true;
35914     this.collapsed = false;
35915     this.unrendered_panels = [];
35916 };
35917
35918 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35919
35920     position: '', // set by wrapper (eg. north/south etc..)
35921     unrendered_panels : null,  // unrendered panels.
35922     createBody : function(){
35923         /** This region's body element 
35924         * @type Roo.Element */
35925         this.bodyEl = this.el.createChild({
35926                 tag: "div",
35927                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35928         });
35929     },
35930
35931     onRender: function(ctr, pos)
35932     {
35933         var dh = Roo.DomHelper;
35934         /** This region's container element 
35935         * @type Roo.Element */
35936         this.el = dh.append(ctr.dom, {
35937                 tag: "div",
35938                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35939             }, true);
35940         /** This region's title element 
35941         * @type Roo.Element */
35942     
35943         this.titleEl = dh.append(this.el.dom,
35944             {
35945                     tag: "div",
35946                     unselectable: "on",
35947                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35948                     children:[
35949                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35950                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35951                     ]}, true);
35952         
35953         this.titleEl.enableDisplayMode();
35954         /** This region's title text element 
35955         * @type HTMLElement */
35956         this.titleTextEl = this.titleEl.dom.firstChild;
35957         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35958         /*
35959         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35960         this.closeBtn.enableDisplayMode();
35961         this.closeBtn.on("click", this.closeClicked, this);
35962         this.closeBtn.hide();
35963     */
35964         this.createBody(this.config);
35965         if(this.config.hideWhenEmpty){
35966             this.hide();
35967             this.on("paneladded", this.validateVisibility, this);
35968             this.on("panelremoved", this.validateVisibility, this);
35969         }
35970         if(this.autoScroll){
35971             this.bodyEl.setStyle("overflow", "auto");
35972         }else{
35973             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35974         }
35975         //if(c.titlebar !== false){
35976             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35977                 this.titleEl.hide();
35978             }else{
35979                 this.titleEl.show();
35980                 if(this.config.title){
35981                     this.titleTextEl.innerHTML = this.config.title;
35982                 }
35983             }
35984         //}
35985         if(this.config.collapsed){
35986             this.collapse(true);
35987         }
35988         if(this.config.hidden){
35989             this.hide();
35990         }
35991         
35992         if (this.unrendered_panels && this.unrendered_panels.length) {
35993             for (var i =0;i< this.unrendered_panels.length; i++) {
35994                 this.add(this.unrendered_panels[i]);
35995             }
35996             this.unrendered_panels = null;
35997             
35998         }
35999         
36000     },
36001     
36002     applyConfig : function(c)
36003     {
36004         /*
36005          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36006             var dh = Roo.DomHelper;
36007             if(c.titlebar !== false){
36008                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36009                 this.collapseBtn.on("click", this.collapse, this);
36010                 this.collapseBtn.enableDisplayMode();
36011                 /*
36012                 if(c.showPin === true || this.showPin){
36013                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36014                     this.stickBtn.enableDisplayMode();
36015                     this.stickBtn.on("click", this.expand, this);
36016                     this.stickBtn.hide();
36017                 }
36018                 
36019             }
36020             */
36021             /** This region's collapsed element
36022             * @type Roo.Element */
36023             /*
36024              *
36025             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36026                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36027             ]}, true);
36028             
36029             if(c.floatable !== false){
36030                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36031                this.collapsedEl.on("click", this.collapseClick, this);
36032             }
36033
36034             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36035                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36036                    id: "message", unselectable: "on", style:{"float":"left"}});
36037                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36038              }
36039             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36040             this.expandBtn.on("click", this.expand, this);
36041             
36042         }
36043         
36044         if(this.collapseBtn){
36045             this.collapseBtn.setVisible(c.collapsible == true);
36046         }
36047         
36048         this.cmargins = c.cmargins || this.cmargins ||
36049                          (this.position == "west" || this.position == "east" ?
36050                              {top: 0, left: 2, right:2, bottom: 0} :
36051                              {top: 2, left: 0, right:0, bottom: 2});
36052         */
36053         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36054         
36055         
36056         this.bottomTabs = c.tabPosition != "top";
36057         
36058         this.autoScroll = c.autoScroll || false;
36059         
36060         
36061        
36062         
36063         this.duration = c.duration || .30;
36064         this.slideDuration = c.slideDuration || .45;
36065         this.config = c;
36066        
36067     },
36068     /**
36069      * Returns true if this region is currently visible.
36070      * @return {Boolean}
36071      */
36072     isVisible : function(){
36073         return this.visible;
36074     },
36075
36076     /**
36077      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36078      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36079      */
36080     //setCollapsedTitle : function(title){
36081     //    title = title || "&#160;";
36082      //   if(this.collapsedTitleTextEl){
36083       //      this.collapsedTitleTextEl.innerHTML = title;
36084        // }
36085     //},
36086
36087     getBox : function(){
36088         var b;
36089       //  if(!this.collapsed){
36090             b = this.el.getBox(false, true);
36091        // }else{
36092           //  b = this.collapsedEl.getBox(false, true);
36093         //}
36094         return b;
36095     },
36096
36097     getMargins : function(){
36098         return this.margins;
36099         //return this.collapsed ? this.cmargins : this.margins;
36100     },
36101 /*
36102     highlight : function(){
36103         this.el.addClass("x-layout-panel-dragover");
36104     },
36105
36106     unhighlight : function(){
36107         this.el.removeClass("x-layout-panel-dragover");
36108     },
36109 */
36110     updateBox : function(box)
36111     {
36112         if (!this.bodyEl) {
36113             return; // not rendered yet..
36114         }
36115         
36116         this.box = box;
36117         if(!this.collapsed){
36118             this.el.dom.style.left = box.x + "px";
36119             this.el.dom.style.top = box.y + "px";
36120             this.updateBody(box.width, box.height);
36121         }else{
36122             this.collapsedEl.dom.style.left = box.x + "px";
36123             this.collapsedEl.dom.style.top = box.y + "px";
36124             this.collapsedEl.setSize(box.width, box.height);
36125         }
36126         if(this.tabs){
36127             this.tabs.autoSizeTabs();
36128         }
36129     },
36130
36131     updateBody : function(w, h)
36132     {
36133         if(w !== null){
36134             this.el.setWidth(w);
36135             w -= this.el.getBorderWidth("rl");
36136             if(this.config.adjustments){
36137                 w += this.config.adjustments[0];
36138             }
36139         }
36140         if(h !== null && h > 0){
36141             this.el.setHeight(h);
36142             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36143             h -= this.el.getBorderWidth("tb");
36144             if(this.config.adjustments){
36145                 h += this.config.adjustments[1];
36146             }
36147             this.bodyEl.setHeight(h);
36148             if(this.tabs){
36149                 h = this.tabs.syncHeight(h);
36150             }
36151         }
36152         if(this.panelSize){
36153             w = w !== null ? w : this.panelSize.width;
36154             h = h !== null ? h : this.panelSize.height;
36155         }
36156         if(this.activePanel){
36157             var el = this.activePanel.getEl();
36158             w = w !== null ? w : el.getWidth();
36159             h = h !== null ? h : el.getHeight();
36160             this.panelSize = {width: w, height: h};
36161             this.activePanel.setSize(w, h);
36162         }
36163         if(Roo.isIE && this.tabs){
36164             this.tabs.el.repaint();
36165         }
36166     },
36167
36168     /**
36169      * Returns the container element for this region.
36170      * @return {Roo.Element}
36171      */
36172     getEl : function(){
36173         return this.el;
36174     },
36175
36176     /**
36177      * Hides this region.
36178      */
36179     hide : function(){
36180         //if(!this.collapsed){
36181             this.el.dom.style.left = "-2000px";
36182             this.el.hide();
36183         //}else{
36184          //   this.collapsedEl.dom.style.left = "-2000px";
36185          //   this.collapsedEl.hide();
36186        // }
36187         this.visible = false;
36188         this.fireEvent("visibilitychange", this, false);
36189     },
36190
36191     /**
36192      * Shows this region if it was previously hidden.
36193      */
36194     show : function(){
36195         //if(!this.collapsed){
36196             this.el.show();
36197         //}else{
36198         //    this.collapsedEl.show();
36199        // }
36200         this.visible = true;
36201         this.fireEvent("visibilitychange", this, true);
36202     },
36203 /*
36204     closeClicked : function(){
36205         if(this.activePanel){
36206             this.remove(this.activePanel);
36207         }
36208     },
36209
36210     collapseClick : function(e){
36211         if(this.isSlid){
36212            e.stopPropagation();
36213            this.slideIn();
36214         }else{
36215            e.stopPropagation();
36216            this.slideOut();
36217         }
36218     },
36219 */
36220     /**
36221      * Collapses this region.
36222      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36223      */
36224     /*
36225     collapse : function(skipAnim, skipCheck = false){
36226         if(this.collapsed) {
36227             return;
36228         }
36229         
36230         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36231             
36232             this.collapsed = true;
36233             if(this.split){
36234                 this.split.el.hide();
36235             }
36236             if(this.config.animate && skipAnim !== true){
36237                 this.fireEvent("invalidated", this);
36238                 this.animateCollapse();
36239             }else{
36240                 this.el.setLocation(-20000,-20000);
36241                 this.el.hide();
36242                 this.collapsedEl.show();
36243                 this.fireEvent("collapsed", this);
36244                 this.fireEvent("invalidated", this);
36245             }
36246         }
36247         
36248     },
36249 */
36250     animateCollapse : function(){
36251         // overridden
36252     },
36253
36254     /**
36255      * Expands this region if it was previously collapsed.
36256      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36257      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36258      */
36259     /*
36260     expand : function(e, skipAnim){
36261         if(e) {
36262             e.stopPropagation();
36263         }
36264         if(!this.collapsed || this.el.hasActiveFx()) {
36265             return;
36266         }
36267         if(this.isSlid){
36268             this.afterSlideIn();
36269             skipAnim = true;
36270         }
36271         this.collapsed = false;
36272         if(this.config.animate && skipAnim !== true){
36273             this.animateExpand();
36274         }else{
36275             this.el.show();
36276             if(this.split){
36277                 this.split.el.show();
36278             }
36279             this.collapsedEl.setLocation(-2000,-2000);
36280             this.collapsedEl.hide();
36281             this.fireEvent("invalidated", this);
36282             this.fireEvent("expanded", this);
36283         }
36284     },
36285 */
36286     animateExpand : function(){
36287         // overridden
36288     },
36289
36290     initTabs : function()
36291     {
36292         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36293         
36294         var ts = new Roo.bootstrap.panel.Tabs({
36295                 el: this.bodyEl.dom,
36296                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36297                 disableTooltips: this.config.disableTabTips,
36298                 toolbar : this.config.toolbar
36299             });
36300         
36301         if(this.config.hideTabs){
36302             ts.stripWrap.setDisplayed(false);
36303         }
36304         this.tabs = ts;
36305         ts.resizeTabs = this.config.resizeTabs === true;
36306         ts.minTabWidth = this.config.minTabWidth || 40;
36307         ts.maxTabWidth = this.config.maxTabWidth || 250;
36308         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36309         ts.monitorResize = false;
36310         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36311         ts.bodyEl.addClass('roo-layout-tabs-body');
36312         this.panels.each(this.initPanelAsTab, this);
36313     },
36314
36315     initPanelAsTab : function(panel){
36316         var ti = this.tabs.addTab(
36317             panel.getEl().id,
36318             panel.getTitle(),
36319             null,
36320             this.config.closeOnTab && panel.isClosable(),
36321             panel.tpl
36322         );
36323         if(panel.tabTip !== undefined){
36324             ti.setTooltip(panel.tabTip);
36325         }
36326         ti.on("activate", function(){
36327               this.setActivePanel(panel);
36328         }, this);
36329         
36330         if(this.config.closeOnTab){
36331             ti.on("beforeclose", function(t, e){
36332                 e.cancel = true;
36333                 this.remove(panel);
36334             }, this);
36335         }
36336         
36337         panel.tabItem = ti;
36338         
36339         return ti;
36340     },
36341
36342     updatePanelTitle : function(panel, title)
36343     {
36344         if(this.activePanel == panel){
36345             this.updateTitle(title);
36346         }
36347         if(this.tabs){
36348             var ti = this.tabs.getTab(panel.getEl().id);
36349             ti.setText(title);
36350             if(panel.tabTip !== undefined){
36351                 ti.setTooltip(panel.tabTip);
36352             }
36353         }
36354     },
36355
36356     updateTitle : function(title){
36357         if(this.titleTextEl && !this.config.title){
36358             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36359         }
36360     },
36361
36362     setActivePanel : function(panel)
36363     {
36364         panel = this.getPanel(panel);
36365         if(this.activePanel && this.activePanel != panel){
36366             if(this.activePanel.setActiveState(false) === false){
36367                 return;
36368             }
36369         }
36370         this.activePanel = panel;
36371         panel.setActiveState(true);
36372         if(this.panelSize){
36373             panel.setSize(this.panelSize.width, this.panelSize.height);
36374         }
36375         if(this.closeBtn){
36376             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36377         }
36378         this.updateTitle(panel.getTitle());
36379         if(this.tabs){
36380             this.fireEvent("invalidated", this);
36381         }
36382         this.fireEvent("panelactivated", this, panel);
36383     },
36384
36385     /**
36386      * Shows the specified panel.
36387      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36388      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36389      */
36390     showPanel : function(panel)
36391     {
36392         panel = this.getPanel(panel);
36393         if(panel){
36394             if(this.tabs){
36395                 var tab = this.tabs.getTab(panel.getEl().id);
36396                 if(tab.isHidden()){
36397                     this.tabs.unhideTab(tab.id);
36398                 }
36399                 tab.activate();
36400             }else{
36401                 this.setActivePanel(panel);
36402             }
36403         }
36404         return panel;
36405     },
36406
36407     /**
36408      * Get the active panel for this region.
36409      * @return {Roo.ContentPanel} The active panel or null
36410      */
36411     getActivePanel : function(){
36412         return this.activePanel;
36413     },
36414
36415     validateVisibility : function(){
36416         if(this.panels.getCount() < 1){
36417             this.updateTitle("&#160;");
36418             this.closeBtn.hide();
36419             this.hide();
36420         }else{
36421             if(!this.isVisible()){
36422                 this.show();
36423             }
36424         }
36425     },
36426
36427     /**
36428      * Adds the passed ContentPanel(s) to this region.
36429      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36430      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36431      */
36432     add : function(panel)
36433     {
36434         if(arguments.length > 1){
36435             for(var i = 0, len = arguments.length; i < len; i++) {
36436                 this.add(arguments[i]);
36437             }
36438             return null;
36439         }
36440         
36441         // if we have not been rendered yet, then we can not really do much of this..
36442         if (!this.bodyEl) {
36443             this.unrendered_panels.push(panel);
36444             return panel;
36445         }
36446         
36447         
36448         
36449         
36450         if(this.hasPanel(panel)){
36451             this.showPanel(panel);
36452             return panel;
36453         }
36454         panel.setRegion(this);
36455         this.panels.add(panel);
36456        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36457             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36458             // and hide them... ???
36459             this.bodyEl.dom.appendChild(panel.getEl().dom);
36460             if(panel.background !== true){
36461                 this.setActivePanel(panel);
36462             }
36463             this.fireEvent("paneladded", this, panel);
36464             return panel;
36465         }
36466         */
36467         if(!this.tabs){
36468             this.initTabs();
36469         }else{
36470             this.initPanelAsTab(panel);
36471         }
36472         
36473         
36474         if(panel.background !== true){
36475             this.tabs.activate(panel.getEl().id);
36476         }
36477         this.fireEvent("paneladded", this, panel);
36478         return panel;
36479     },
36480
36481     /**
36482      * Hides the tab for the specified panel.
36483      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36484      */
36485     hidePanel : function(panel){
36486         if(this.tabs && (panel = this.getPanel(panel))){
36487             this.tabs.hideTab(panel.getEl().id);
36488         }
36489     },
36490
36491     /**
36492      * Unhides the tab for a previously hidden panel.
36493      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36494      */
36495     unhidePanel : function(panel){
36496         if(this.tabs && (panel = this.getPanel(panel))){
36497             this.tabs.unhideTab(panel.getEl().id);
36498         }
36499     },
36500
36501     clearPanels : function(){
36502         while(this.panels.getCount() > 0){
36503              this.remove(this.panels.first());
36504         }
36505     },
36506
36507     /**
36508      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36509      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36510      * @param {Boolean} preservePanel Overrides the config preservePanel option
36511      * @return {Roo.ContentPanel} The panel that was removed
36512      */
36513     remove : function(panel, preservePanel)
36514     {
36515         panel = this.getPanel(panel);
36516         if(!panel){
36517             return null;
36518         }
36519         var e = {};
36520         this.fireEvent("beforeremove", this, panel, e);
36521         if(e.cancel === true){
36522             return null;
36523         }
36524         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36525         var panelId = panel.getId();
36526         this.panels.removeKey(panelId);
36527         if(preservePanel){
36528             document.body.appendChild(panel.getEl().dom);
36529         }
36530         if(this.tabs){
36531             this.tabs.removeTab(panel.getEl().id);
36532         }else if (!preservePanel){
36533             this.bodyEl.dom.removeChild(panel.getEl().dom);
36534         }
36535         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36536             var p = this.panels.first();
36537             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36538             tempEl.appendChild(p.getEl().dom);
36539             this.bodyEl.update("");
36540             this.bodyEl.dom.appendChild(p.getEl().dom);
36541             tempEl = null;
36542             this.updateTitle(p.getTitle());
36543             this.tabs = null;
36544             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36545             this.setActivePanel(p);
36546         }
36547         panel.setRegion(null);
36548         if(this.activePanel == panel){
36549             this.activePanel = null;
36550         }
36551         if(this.config.autoDestroy !== false && preservePanel !== true){
36552             try{panel.destroy();}catch(e){}
36553         }
36554         this.fireEvent("panelremoved", this, panel);
36555         return panel;
36556     },
36557
36558     /**
36559      * Returns the TabPanel component used by this region
36560      * @return {Roo.TabPanel}
36561      */
36562     getTabs : function(){
36563         return this.tabs;
36564     },
36565
36566     createTool : function(parentEl, className){
36567         var btn = Roo.DomHelper.append(parentEl, {
36568             tag: "div",
36569             cls: "x-layout-tools-button",
36570             children: [ {
36571                 tag: "div",
36572                 cls: "roo-layout-tools-button-inner " + className,
36573                 html: "&#160;"
36574             }]
36575         }, true);
36576         btn.addClassOnOver("roo-layout-tools-button-over");
36577         return btn;
36578     }
36579 });/*
36580  * Based on:
36581  * Ext JS Library 1.1.1
36582  * Copyright(c) 2006-2007, Ext JS, LLC.
36583  *
36584  * Originally Released Under LGPL - original licence link has changed is not relivant.
36585  *
36586  * Fork - LGPL
36587  * <script type="text/javascript">
36588  */
36589  
36590
36591
36592 /**
36593  * @class Roo.SplitLayoutRegion
36594  * @extends Roo.LayoutRegion
36595  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36596  */
36597 Roo.bootstrap.layout.Split = function(config){
36598     this.cursor = config.cursor;
36599     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36600 };
36601
36602 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36603 {
36604     splitTip : "Drag to resize.",
36605     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36606     useSplitTips : false,
36607
36608     applyConfig : function(config){
36609         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36610     },
36611     
36612     onRender : function(ctr,pos) {
36613         
36614         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36615         if(!this.config.split){
36616             return;
36617         }
36618         if(!this.split){
36619             
36620             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36621                             tag: "div",
36622                             id: this.el.id + "-split",
36623                             cls: "roo-layout-split roo-layout-split-"+this.position,
36624                             html: "&#160;"
36625             });
36626             /** The SplitBar for this region 
36627             * @type Roo.SplitBar */
36628             // does not exist yet...
36629             Roo.log([this.position, this.orientation]);
36630             
36631             this.split = new Roo.bootstrap.SplitBar({
36632                 dragElement : splitEl,
36633                 resizingElement: this.el,
36634                 orientation : this.orientation
36635             });
36636             
36637             this.split.on("moved", this.onSplitMove, this);
36638             this.split.useShim = this.config.useShim === true;
36639             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36640             if(this.useSplitTips){
36641                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36642             }
36643             //if(config.collapsible){
36644             //    this.split.el.on("dblclick", this.collapse,  this);
36645             //}
36646         }
36647         if(typeof this.config.minSize != "undefined"){
36648             this.split.minSize = this.config.minSize;
36649         }
36650         if(typeof this.config.maxSize != "undefined"){
36651             this.split.maxSize = this.config.maxSize;
36652         }
36653         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36654             this.hideSplitter();
36655         }
36656         
36657     },
36658
36659     getHMaxSize : function(){
36660          var cmax = this.config.maxSize || 10000;
36661          var center = this.mgr.getRegion("center");
36662          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36663     },
36664
36665     getVMaxSize : function(){
36666          var cmax = this.config.maxSize || 10000;
36667          var center = this.mgr.getRegion("center");
36668          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36669     },
36670
36671     onSplitMove : function(split, newSize){
36672         this.fireEvent("resized", this, newSize);
36673     },
36674     
36675     /** 
36676      * Returns the {@link Roo.SplitBar} for this region.
36677      * @return {Roo.SplitBar}
36678      */
36679     getSplitBar : function(){
36680         return this.split;
36681     },
36682     
36683     hide : function(){
36684         this.hideSplitter();
36685         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36686     },
36687
36688     hideSplitter : function(){
36689         if(this.split){
36690             this.split.el.setLocation(-2000,-2000);
36691             this.split.el.hide();
36692         }
36693     },
36694
36695     show : function(){
36696         if(this.split){
36697             this.split.el.show();
36698         }
36699         Roo.bootstrap.layout.Split.superclass.show.call(this);
36700     },
36701     
36702     beforeSlide: function(){
36703         if(Roo.isGecko){// firefox overflow auto bug workaround
36704             this.bodyEl.clip();
36705             if(this.tabs) {
36706                 this.tabs.bodyEl.clip();
36707             }
36708             if(this.activePanel){
36709                 this.activePanel.getEl().clip();
36710                 
36711                 if(this.activePanel.beforeSlide){
36712                     this.activePanel.beforeSlide();
36713                 }
36714             }
36715         }
36716     },
36717     
36718     afterSlide : function(){
36719         if(Roo.isGecko){// firefox overflow auto bug workaround
36720             this.bodyEl.unclip();
36721             if(this.tabs) {
36722                 this.tabs.bodyEl.unclip();
36723             }
36724             if(this.activePanel){
36725                 this.activePanel.getEl().unclip();
36726                 if(this.activePanel.afterSlide){
36727                     this.activePanel.afterSlide();
36728                 }
36729             }
36730         }
36731     },
36732
36733     initAutoHide : function(){
36734         if(this.autoHide !== false){
36735             if(!this.autoHideHd){
36736                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36737                 this.autoHideHd = {
36738                     "mouseout": function(e){
36739                         if(!e.within(this.el, true)){
36740                             st.delay(500);
36741                         }
36742                     },
36743                     "mouseover" : function(e){
36744                         st.cancel();
36745                     },
36746                     scope : this
36747                 };
36748             }
36749             this.el.on(this.autoHideHd);
36750         }
36751     },
36752
36753     clearAutoHide : function(){
36754         if(this.autoHide !== false){
36755             this.el.un("mouseout", this.autoHideHd.mouseout);
36756             this.el.un("mouseover", this.autoHideHd.mouseover);
36757         }
36758     },
36759
36760     clearMonitor : function(){
36761         Roo.get(document).un("click", this.slideInIf, this);
36762     },
36763
36764     // these names are backwards but not changed for compat
36765     slideOut : function(){
36766         if(this.isSlid || this.el.hasActiveFx()){
36767             return;
36768         }
36769         this.isSlid = true;
36770         if(this.collapseBtn){
36771             this.collapseBtn.hide();
36772         }
36773         this.closeBtnState = this.closeBtn.getStyle('display');
36774         this.closeBtn.hide();
36775         if(this.stickBtn){
36776             this.stickBtn.show();
36777         }
36778         this.el.show();
36779         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36780         this.beforeSlide();
36781         this.el.setStyle("z-index", 10001);
36782         this.el.slideIn(this.getSlideAnchor(), {
36783             callback: function(){
36784                 this.afterSlide();
36785                 this.initAutoHide();
36786                 Roo.get(document).on("click", this.slideInIf, this);
36787                 this.fireEvent("slideshow", this);
36788             },
36789             scope: this,
36790             block: true
36791         });
36792     },
36793
36794     afterSlideIn : function(){
36795         this.clearAutoHide();
36796         this.isSlid = false;
36797         this.clearMonitor();
36798         this.el.setStyle("z-index", "");
36799         if(this.collapseBtn){
36800             this.collapseBtn.show();
36801         }
36802         this.closeBtn.setStyle('display', this.closeBtnState);
36803         if(this.stickBtn){
36804             this.stickBtn.hide();
36805         }
36806         this.fireEvent("slidehide", this);
36807     },
36808
36809     slideIn : function(cb){
36810         if(!this.isSlid || this.el.hasActiveFx()){
36811             Roo.callback(cb);
36812             return;
36813         }
36814         this.isSlid = false;
36815         this.beforeSlide();
36816         this.el.slideOut(this.getSlideAnchor(), {
36817             callback: function(){
36818                 this.el.setLeftTop(-10000, -10000);
36819                 this.afterSlide();
36820                 this.afterSlideIn();
36821                 Roo.callback(cb);
36822             },
36823             scope: this,
36824             block: true
36825         });
36826     },
36827     
36828     slideInIf : function(e){
36829         if(!e.within(this.el)){
36830             this.slideIn();
36831         }
36832     },
36833
36834     animateCollapse : function(){
36835         this.beforeSlide();
36836         this.el.setStyle("z-index", 20000);
36837         var anchor = this.getSlideAnchor();
36838         this.el.slideOut(anchor, {
36839             callback : function(){
36840                 this.el.setStyle("z-index", "");
36841                 this.collapsedEl.slideIn(anchor, {duration:.3});
36842                 this.afterSlide();
36843                 this.el.setLocation(-10000,-10000);
36844                 this.el.hide();
36845                 this.fireEvent("collapsed", this);
36846             },
36847             scope: this,
36848             block: true
36849         });
36850     },
36851
36852     animateExpand : function(){
36853         this.beforeSlide();
36854         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36855         this.el.setStyle("z-index", 20000);
36856         this.collapsedEl.hide({
36857             duration:.1
36858         });
36859         this.el.slideIn(this.getSlideAnchor(), {
36860             callback : function(){
36861                 this.el.setStyle("z-index", "");
36862                 this.afterSlide();
36863                 if(this.split){
36864                     this.split.el.show();
36865                 }
36866                 this.fireEvent("invalidated", this);
36867                 this.fireEvent("expanded", this);
36868             },
36869             scope: this,
36870             block: true
36871         });
36872     },
36873
36874     anchors : {
36875         "west" : "left",
36876         "east" : "right",
36877         "north" : "top",
36878         "south" : "bottom"
36879     },
36880
36881     sanchors : {
36882         "west" : "l",
36883         "east" : "r",
36884         "north" : "t",
36885         "south" : "b"
36886     },
36887
36888     canchors : {
36889         "west" : "tl-tr",
36890         "east" : "tr-tl",
36891         "north" : "tl-bl",
36892         "south" : "bl-tl"
36893     },
36894
36895     getAnchor : function(){
36896         return this.anchors[this.position];
36897     },
36898
36899     getCollapseAnchor : function(){
36900         return this.canchors[this.position];
36901     },
36902
36903     getSlideAnchor : function(){
36904         return this.sanchors[this.position];
36905     },
36906
36907     getAlignAdj : function(){
36908         var cm = this.cmargins;
36909         switch(this.position){
36910             case "west":
36911                 return [0, 0];
36912             break;
36913             case "east":
36914                 return [0, 0];
36915             break;
36916             case "north":
36917                 return [0, 0];
36918             break;
36919             case "south":
36920                 return [0, 0];
36921             break;
36922         }
36923     },
36924
36925     getExpandAdj : function(){
36926         var c = this.collapsedEl, cm = this.cmargins;
36927         switch(this.position){
36928             case "west":
36929                 return [-(cm.right+c.getWidth()+cm.left), 0];
36930             break;
36931             case "east":
36932                 return [cm.right+c.getWidth()+cm.left, 0];
36933             break;
36934             case "north":
36935                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36936             break;
36937             case "south":
36938                 return [0, cm.top+cm.bottom+c.getHeight()];
36939             break;
36940         }
36941     }
36942 });/*
36943  * Based on:
36944  * Ext JS Library 1.1.1
36945  * Copyright(c) 2006-2007, Ext JS, LLC.
36946  *
36947  * Originally Released Under LGPL - original licence link has changed is not relivant.
36948  *
36949  * Fork - LGPL
36950  * <script type="text/javascript">
36951  */
36952 /*
36953  * These classes are private internal classes
36954  */
36955 Roo.bootstrap.layout.Center = function(config){
36956     config.region = "center";
36957     Roo.bootstrap.layout.Region.call(this, config);
36958     this.visible = true;
36959     this.minWidth = config.minWidth || 20;
36960     this.minHeight = config.minHeight || 20;
36961 };
36962
36963 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36964     hide : function(){
36965         // center panel can't be hidden
36966     },
36967     
36968     show : function(){
36969         // center panel can't be hidden
36970     },
36971     
36972     getMinWidth: function(){
36973         return this.minWidth;
36974     },
36975     
36976     getMinHeight: function(){
36977         return this.minHeight;
36978     }
36979 });
36980
36981
36982
36983
36984  
36985
36986
36987
36988
36989
36990 Roo.bootstrap.layout.North = function(config)
36991 {
36992     config.region = 'north';
36993     config.cursor = 'n-resize';
36994     
36995     Roo.bootstrap.layout.Split.call(this, config);
36996     
36997     
36998     if(this.split){
36999         this.split.placement = Roo.bootstrap.SplitBar.TOP;
37000         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37001         this.split.el.addClass("roo-layout-split-v");
37002     }
37003     var size = config.initialSize || config.height;
37004     if(typeof size != "undefined"){
37005         this.el.setHeight(size);
37006     }
37007 };
37008 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37009 {
37010     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37011     
37012     
37013     
37014     getBox : function(){
37015         if(this.collapsed){
37016             return this.collapsedEl.getBox();
37017         }
37018         var box = this.el.getBox();
37019         if(this.split){
37020             box.height += this.split.el.getHeight();
37021         }
37022         return box;
37023     },
37024     
37025     updateBox : function(box){
37026         if(this.split && !this.collapsed){
37027             box.height -= this.split.el.getHeight();
37028             this.split.el.setLeft(box.x);
37029             this.split.el.setTop(box.y+box.height);
37030             this.split.el.setWidth(box.width);
37031         }
37032         if(this.collapsed){
37033             this.updateBody(box.width, null);
37034         }
37035         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37036     }
37037 });
37038
37039
37040
37041
37042
37043 Roo.bootstrap.layout.South = function(config){
37044     config.region = 'south';
37045     config.cursor = 's-resize';
37046     Roo.bootstrap.layout.Split.call(this, config);
37047     if(this.split){
37048         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37049         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37050         this.split.el.addClass("roo-layout-split-v");
37051     }
37052     var size = config.initialSize || config.height;
37053     if(typeof size != "undefined"){
37054         this.el.setHeight(size);
37055     }
37056 };
37057
37058 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37059     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37060     getBox : function(){
37061         if(this.collapsed){
37062             return this.collapsedEl.getBox();
37063         }
37064         var box = this.el.getBox();
37065         if(this.split){
37066             var sh = this.split.el.getHeight();
37067             box.height += sh;
37068             box.y -= sh;
37069         }
37070         return box;
37071     },
37072     
37073     updateBox : function(box){
37074         if(this.split && !this.collapsed){
37075             var sh = this.split.el.getHeight();
37076             box.height -= sh;
37077             box.y += sh;
37078             this.split.el.setLeft(box.x);
37079             this.split.el.setTop(box.y-sh);
37080             this.split.el.setWidth(box.width);
37081         }
37082         if(this.collapsed){
37083             this.updateBody(box.width, null);
37084         }
37085         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37086     }
37087 });
37088
37089 Roo.bootstrap.layout.East = function(config){
37090     config.region = "east";
37091     config.cursor = "e-resize";
37092     Roo.bootstrap.layout.Split.call(this, config);
37093     if(this.split){
37094         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37095         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37096         this.split.el.addClass("roo-layout-split-h");
37097     }
37098     var size = config.initialSize || config.width;
37099     if(typeof size != "undefined"){
37100         this.el.setWidth(size);
37101     }
37102 };
37103 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37104     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37105     getBox : function(){
37106         if(this.collapsed){
37107             return this.collapsedEl.getBox();
37108         }
37109         var box = this.el.getBox();
37110         if(this.split){
37111             var sw = this.split.el.getWidth();
37112             box.width += sw;
37113             box.x -= sw;
37114         }
37115         return box;
37116     },
37117
37118     updateBox : function(box){
37119         if(this.split && !this.collapsed){
37120             var sw = this.split.el.getWidth();
37121             box.width -= sw;
37122             this.split.el.setLeft(box.x);
37123             this.split.el.setTop(box.y);
37124             this.split.el.setHeight(box.height);
37125             box.x += sw;
37126         }
37127         if(this.collapsed){
37128             this.updateBody(null, box.height);
37129         }
37130         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37131     }
37132 });
37133
37134 Roo.bootstrap.layout.West = function(config){
37135     config.region = "west";
37136     config.cursor = "w-resize";
37137     
37138     Roo.bootstrap.layout.Split.call(this, config);
37139     if(this.split){
37140         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37141         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37142         this.split.el.addClass("roo-layout-split-h");
37143     }
37144     
37145 };
37146 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37147     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37148     
37149     onRender: function(ctr, pos)
37150     {
37151         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37152         var size = this.config.initialSize || this.config.width;
37153         if(typeof size != "undefined"){
37154             this.el.setWidth(size);
37155         }
37156     },
37157     
37158     getBox : function(){
37159         if(this.collapsed){
37160             return this.collapsedEl.getBox();
37161         }
37162         var box = this.el.getBox();
37163         if(this.split){
37164             box.width += this.split.el.getWidth();
37165         }
37166         return box;
37167     },
37168     
37169     updateBox : function(box){
37170         if(this.split && !this.collapsed){
37171             var sw = this.split.el.getWidth();
37172             box.width -= sw;
37173             this.split.el.setLeft(box.x+box.width);
37174             this.split.el.setTop(box.y);
37175             this.split.el.setHeight(box.height);
37176         }
37177         if(this.collapsed){
37178             this.updateBody(null, box.height);
37179         }
37180         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37181     }
37182 });
37183 Roo.namespace("Roo.bootstrap.panel");/*
37184  * Based on:
37185  * Ext JS Library 1.1.1
37186  * Copyright(c) 2006-2007, Ext JS, LLC.
37187  *
37188  * Originally Released Under LGPL - original licence link has changed is not relivant.
37189  *
37190  * Fork - LGPL
37191  * <script type="text/javascript">
37192  */
37193 /**
37194  * @class Roo.ContentPanel
37195  * @extends Roo.util.Observable
37196  * A basic ContentPanel element.
37197  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37198  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37199  * @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
37200  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37201  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37202  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37203  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37204  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37205  * @cfg {String} title          The title for this panel
37206  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37207  * @cfg {String} url            Calls {@link #setUrl} with this value
37208  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37209  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37210  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37211  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37212  * @cfg {Boolean} badges render the badges
37213
37214  * @constructor
37215  * Create a new ContentPanel.
37216  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37217  * @param {String/Object} config A string to set only the title or a config object
37218  * @param {String} content (optional) Set the HTML content for this panel
37219  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37220  */
37221 Roo.bootstrap.panel.Content = function( config){
37222     
37223     this.tpl = config.tpl || false;
37224     
37225     var el = config.el;
37226     var content = config.content;
37227
37228     if(config.autoCreate){ // xtype is available if this is called from factory
37229         el = Roo.id();
37230     }
37231     this.el = Roo.get(el);
37232     if(!this.el && config && config.autoCreate){
37233         if(typeof config.autoCreate == "object"){
37234             if(!config.autoCreate.id){
37235                 config.autoCreate.id = config.id||el;
37236             }
37237             this.el = Roo.DomHelper.append(document.body,
37238                         config.autoCreate, true);
37239         }else{
37240             var elcfg =  {   tag: "div",
37241                             cls: "roo-layout-inactive-content",
37242                             id: config.id||el
37243                             };
37244             if (config.html) {
37245                 elcfg.html = config.html;
37246                 
37247             }
37248                         
37249             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37250         }
37251     } 
37252     this.closable = false;
37253     this.loaded = false;
37254     this.active = false;
37255    
37256       
37257     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37258         
37259         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37260         
37261         this.wrapEl = this.el; //this.el.wrap();
37262         var ti = [];
37263         if (config.toolbar.items) {
37264             ti = config.toolbar.items ;
37265             delete config.toolbar.items ;
37266         }
37267         
37268         var nitems = [];
37269         this.toolbar.render(this.wrapEl, 'before');
37270         for(var i =0;i < ti.length;i++) {
37271           //  Roo.log(['add child', items[i]]);
37272             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37273         }
37274         this.toolbar.items = nitems;
37275         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37276         delete config.toolbar;
37277         
37278     }
37279     /*
37280     // xtype created footer. - not sure if will work as we normally have to render first..
37281     if (this.footer && !this.footer.el && this.footer.xtype) {
37282         if (!this.wrapEl) {
37283             this.wrapEl = this.el.wrap();
37284         }
37285     
37286         this.footer.container = this.wrapEl.createChild();
37287          
37288         this.footer = Roo.factory(this.footer, Roo);
37289         
37290     }
37291     */
37292     
37293      if(typeof config == "string"){
37294         this.title = config;
37295     }else{
37296         Roo.apply(this, config);
37297     }
37298     
37299     if(this.resizeEl){
37300         this.resizeEl = Roo.get(this.resizeEl, true);
37301     }else{
37302         this.resizeEl = this.el;
37303     }
37304     // handle view.xtype
37305     
37306  
37307     
37308     
37309     this.addEvents({
37310         /**
37311          * @event activate
37312          * Fires when this panel is activated. 
37313          * @param {Roo.ContentPanel} this
37314          */
37315         "activate" : true,
37316         /**
37317          * @event deactivate
37318          * Fires when this panel is activated. 
37319          * @param {Roo.ContentPanel} this
37320          */
37321         "deactivate" : true,
37322
37323         /**
37324          * @event resize
37325          * Fires when this panel is resized if fitToFrame is true.
37326          * @param {Roo.ContentPanel} this
37327          * @param {Number} width The width after any component adjustments
37328          * @param {Number} height The height after any component adjustments
37329          */
37330         "resize" : true,
37331         
37332          /**
37333          * @event render
37334          * Fires when this tab is created
37335          * @param {Roo.ContentPanel} this
37336          */
37337         "render" : true
37338         
37339         
37340         
37341     });
37342     
37343
37344     
37345     
37346     if(this.autoScroll){
37347         this.resizeEl.setStyle("overflow", "auto");
37348     } else {
37349         // fix randome scrolling
37350         //this.el.on('scroll', function() {
37351         //    Roo.log('fix random scolling');
37352         //    this.scrollTo('top',0); 
37353         //});
37354     }
37355     content = content || this.content;
37356     if(content){
37357         this.setContent(content);
37358     }
37359     if(config && config.url){
37360         this.setUrl(this.url, this.params, this.loadOnce);
37361     }
37362     
37363     
37364     
37365     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37366     
37367     if (this.view && typeof(this.view.xtype) != 'undefined') {
37368         this.view.el = this.el.appendChild(document.createElement("div"));
37369         this.view = Roo.factory(this.view); 
37370         this.view.render  &&  this.view.render(false, '');  
37371     }
37372     
37373     
37374     this.fireEvent('render', this);
37375 };
37376
37377 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37378     
37379     tabTip : '',
37380     
37381     setRegion : function(region){
37382         this.region = region;
37383         this.setActiveClass(region && !this.background);
37384     },
37385     
37386     
37387     setActiveClass: function(state)
37388     {
37389         if(state){
37390            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37391            this.el.setStyle('position','relative');
37392         }else{
37393            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37394            this.el.setStyle('position', 'absolute');
37395         } 
37396     },
37397     
37398     /**
37399      * Returns the toolbar for this Panel if one was configured. 
37400      * @return {Roo.Toolbar} 
37401      */
37402     getToolbar : function(){
37403         return this.toolbar;
37404     },
37405     
37406     setActiveState : function(active)
37407     {
37408         this.active = active;
37409         this.setActiveClass(active);
37410         if(!active){
37411             if(this.fireEvent("deactivate", this) === false){
37412                 return false;
37413             }
37414             return true;
37415         }
37416         this.fireEvent("activate", this);
37417         return true;
37418     },
37419     /**
37420      * Updates this panel's element
37421      * @param {String} content The new content
37422      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37423     */
37424     setContent : function(content, loadScripts){
37425         this.el.update(content, loadScripts);
37426     },
37427
37428     ignoreResize : function(w, h){
37429         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37430             return true;
37431         }else{
37432             this.lastSize = {width: w, height: h};
37433             return false;
37434         }
37435     },
37436     /**
37437      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37438      * @return {Roo.UpdateManager} The UpdateManager
37439      */
37440     getUpdateManager : function(){
37441         return this.el.getUpdateManager();
37442     },
37443      /**
37444      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37445      * @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:
37446 <pre><code>
37447 panel.load({
37448     url: "your-url.php",
37449     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37450     callback: yourFunction,
37451     scope: yourObject, //(optional scope)
37452     discardUrl: false,
37453     nocache: false,
37454     text: "Loading...",
37455     timeout: 30,
37456     scripts: false
37457 });
37458 </code></pre>
37459      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37460      * 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.
37461      * @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}
37462      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37463      * @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.
37464      * @return {Roo.ContentPanel} this
37465      */
37466     load : function(){
37467         var um = this.el.getUpdateManager();
37468         um.update.apply(um, arguments);
37469         return this;
37470     },
37471
37472
37473     /**
37474      * 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.
37475      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37476      * @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)
37477      * @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)
37478      * @return {Roo.UpdateManager} The UpdateManager
37479      */
37480     setUrl : function(url, params, loadOnce){
37481         if(this.refreshDelegate){
37482             this.removeListener("activate", this.refreshDelegate);
37483         }
37484         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37485         this.on("activate", this.refreshDelegate);
37486         return this.el.getUpdateManager();
37487     },
37488     
37489     _handleRefresh : function(url, params, loadOnce){
37490         if(!loadOnce || !this.loaded){
37491             var updater = this.el.getUpdateManager();
37492             updater.update(url, params, this._setLoaded.createDelegate(this));
37493         }
37494     },
37495     
37496     _setLoaded : function(){
37497         this.loaded = true;
37498     }, 
37499     
37500     /**
37501      * Returns this panel's id
37502      * @return {String} 
37503      */
37504     getId : function(){
37505         return this.el.id;
37506     },
37507     
37508     /** 
37509      * Returns this panel's element - used by regiosn to add.
37510      * @return {Roo.Element} 
37511      */
37512     getEl : function(){
37513         return this.wrapEl || this.el;
37514     },
37515     
37516    
37517     
37518     adjustForComponents : function(width, height)
37519     {
37520         //Roo.log('adjustForComponents ');
37521         if(this.resizeEl != this.el){
37522             width -= this.el.getFrameWidth('lr');
37523             height -= this.el.getFrameWidth('tb');
37524         }
37525         if(this.toolbar){
37526             var te = this.toolbar.getEl();
37527             te.setWidth(width);
37528             height -= te.getHeight();
37529         }
37530         if(this.footer){
37531             var te = this.footer.getEl();
37532             te.setWidth(width);
37533             height -= te.getHeight();
37534         }
37535         
37536         
37537         if(this.adjustments){
37538             width += this.adjustments[0];
37539             height += this.adjustments[1];
37540         }
37541         return {"width": width, "height": height};
37542     },
37543     
37544     setSize : function(width, height){
37545         if(this.fitToFrame && !this.ignoreResize(width, height)){
37546             if(this.fitContainer && this.resizeEl != this.el){
37547                 this.el.setSize(width, height);
37548             }
37549             var size = this.adjustForComponents(width, height);
37550             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37551             this.fireEvent('resize', this, size.width, size.height);
37552         }
37553     },
37554     
37555     /**
37556      * Returns this panel's title
37557      * @return {String} 
37558      */
37559     getTitle : function(){
37560         
37561         if (typeof(this.title) != 'object') {
37562             return this.title;
37563         }
37564         
37565         var t = '';
37566         for (var k in this.title) {
37567             if (!this.title.hasOwnProperty(k)) {
37568                 continue;
37569             }
37570             
37571             if (k.indexOf('-') >= 0) {
37572                 var s = k.split('-');
37573                 for (var i = 0; i<s.length; i++) {
37574                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37575                 }
37576             } else {
37577                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37578             }
37579         }
37580         return t;
37581     },
37582     
37583     /**
37584      * Set this panel's title
37585      * @param {String} title
37586      */
37587     setTitle : function(title){
37588         this.title = title;
37589         if(this.region){
37590             this.region.updatePanelTitle(this, title);
37591         }
37592     },
37593     
37594     /**
37595      * Returns true is this panel was configured to be closable
37596      * @return {Boolean} 
37597      */
37598     isClosable : function(){
37599         return this.closable;
37600     },
37601     
37602     beforeSlide : function(){
37603         this.el.clip();
37604         this.resizeEl.clip();
37605     },
37606     
37607     afterSlide : function(){
37608         this.el.unclip();
37609         this.resizeEl.unclip();
37610     },
37611     
37612     /**
37613      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37614      *   Will fail silently if the {@link #setUrl} method has not been called.
37615      *   This does not activate the panel, just updates its content.
37616      */
37617     refresh : function(){
37618         if(this.refreshDelegate){
37619            this.loaded = false;
37620            this.refreshDelegate();
37621         }
37622     },
37623     
37624     /**
37625      * Destroys this panel
37626      */
37627     destroy : function(){
37628         this.el.removeAllListeners();
37629         var tempEl = document.createElement("span");
37630         tempEl.appendChild(this.el.dom);
37631         tempEl.innerHTML = "";
37632         this.el.remove();
37633         this.el = null;
37634     },
37635     
37636     /**
37637      * form - if the content panel contains a form - this is a reference to it.
37638      * @type {Roo.form.Form}
37639      */
37640     form : false,
37641     /**
37642      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37643      *    This contains a reference to it.
37644      * @type {Roo.View}
37645      */
37646     view : false,
37647     
37648       /**
37649      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37650      * <pre><code>
37651
37652 layout.addxtype({
37653        xtype : 'Form',
37654        items: [ .... ]
37655    }
37656 );
37657
37658 </code></pre>
37659      * @param {Object} cfg Xtype definition of item to add.
37660      */
37661     
37662     
37663     getChildContainer: function () {
37664         return this.getEl();
37665     }
37666     
37667     
37668     /*
37669         var  ret = new Roo.factory(cfg);
37670         return ret;
37671         
37672         
37673         // add form..
37674         if (cfg.xtype.match(/^Form$/)) {
37675             
37676             var el;
37677             //if (this.footer) {
37678             //    el = this.footer.container.insertSibling(false, 'before');
37679             //} else {
37680                 el = this.el.createChild();
37681             //}
37682
37683             this.form = new  Roo.form.Form(cfg);
37684             
37685             
37686             if ( this.form.allItems.length) {
37687                 this.form.render(el.dom);
37688             }
37689             return this.form;
37690         }
37691         // should only have one of theses..
37692         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37693             // views.. should not be just added - used named prop 'view''
37694             
37695             cfg.el = this.el.appendChild(document.createElement("div"));
37696             // factory?
37697             
37698             var ret = new Roo.factory(cfg);
37699              
37700              ret.render && ret.render(false, ''); // render blank..
37701             this.view = ret;
37702             return ret;
37703         }
37704         return false;
37705     }
37706     \*/
37707 });
37708  
37709 /**
37710  * @class Roo.bootstrap.panel.Grid
37711  * @extends Roo.bootstrap.panel.Content
37712  * @constructor
37713  * Create a new GridPanel.
37714  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37715  * @param {Object} config A the config object
37716   
37717  */
37718
37719
37720
37721 Roo.bootstrap.panel.Grid = function(config)
37722 {
37723     
37724       
37725     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37726         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37727
37728     config.el = this.wrapper;
37729     //this.el = this.wrapper;
37730     
37731       if (config.container) {
37732         // ctor'ed from a Border/panel.grid
37733         
37734         
37735         this.wrapper.setStyle("overflow", "hidden");
37736         this.wrapper.addClass('roo-grid-container');
37737
37738     }
37739     
37740     
37741     if(config.toolbar){
37742         var tool_el = this.wrapper.createChild();    
37743         this.toolbar = Roo.factory(config.toolbar);
37744         var ti = [];
37745         if (config.toolbar.items) {
37746             ti = config.toolbar.items ;
37747             delete config.toolbar.items ;
37748         }
37749         
37750         var nitems = [];
37751         this.toolbar.render(tool_el);
37752         for(var i =0;i < ti.length;i++) {
37753           //  Roo.log(['add child', items[i]]);
37754             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37755         }
37756         this.toolbar.items = nitems;
37757         
37758         delete config.toolbar;
37759     }
37760     
37761     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37762     config.grid.scrollBody = true;;
37763     config.grid.monitorWindowResize = false; // turn off autosizing
37764     config.grid.autoHeight = false;
37765     config.grid.autoWidth = false;
37766     
37767     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37768     
37769     if (config.background) {
37770         // render grid on panel activation (if panel background)
37771         this.on('activate', function(gp) {
37772             if (!gp.grid.rendered) {
37773                 gp.grid.render(this.wrapper);
37774                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37775             }
37776         });
37777             
37778     } else {
37779         this.grid.render(this.wrapper);
37780         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37781
37782     }
37783     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37784     // ??? needed ??? config.el = this.wrapper;
37785     
37786     
37787     
37788   
37789     // xtype created footer. - not sure if will work as we normally have to render first..
37790     if (this.footer && !this.footer.el && this.footer.xtype) {
37791         
37792         var ctr = this.grid.getView().getFooterPanel(true);
37793         this.footer.dataSource = this.grid.dataSource;
37794         this.footer = Roo.factory(this.footer, Roo);
37795         this.footer.render(ctr);
37796         
37797     }
37798     
37799     
37800     
37801     
37802      
37803 };
37804
37805 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37806     getId : function(){
37807         return this.grid.id;
37808     },
37809     
37810     /**
37811      * Returns the grid for this panel
37812      * @return {Roo.bootstrap.Table} 
37813      */
37814     getGrid : function(){
37815         return this.grid;    
37816     },
37817     
37818     setSize : function(width, height){
37819         if(!this.ignoreResize(width, height)){
37820             var grid = this.grid;
37821             var size = this.adjustForComponents(width, height);
37822             var gridel = grid.getGridEl();
37823             gridel.setSize(size.width, size.height);
37824             /*
37825             var thd = grid.getGridEl().select('thead',true).first();
37826             var tbd = grid.getGridEl().select('tbody', true).first();
37827             if (tbd) {
37828                 tbd.setSize(width, height - thd.getHeight());
37829             }
37830             */
37831             grid.autoSize();
37832         }
37833     },
37834      
37835     
37836     
37837     beforeSlide : function(){
37838         this.grid.getView().scroller.clip();
37839     },
37840     
37841     afterSlide : function(){
37842         this.grid.getView().scroller.unclip();
37843     },
37844     
37845     destroy : function(){
37846         this.grid.destroy();
37847         delete this.grid;
37848         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37849     }
37850 });
37851
37852 /**
37853  * @class Roo.bootstrap.panel.Nest
37854  * @extends Roo.bootstrap.panel.Content
37855  * @constructor
37856  * Create a new Panel, that can contain a layout.Border.
37857  * 
37858  * 
37859  * @param {Roo.BorderLayout} layout The layout for this panel
37860  * @param {String/Object} config A string to set only the title or a config object
37861  */
37862 Roo.bootstrap.panel.Nest = function(config)
37863 {
37864     // construct with only one argument..
37865     /* FIXME - implement nicer consturctors
37866     if (layout.layout) {
37867         config = layout;
37868         layout = config.layout;
37869         delete config.layout;
37870     }
37871     if (layout.xtype && !layout.getEl) {
37872         // then layout needs constructing..
37873         layout = Roo.factory(layout, Roo);
37874     }
37875     */
37876     
37877     config.el =  config.layout.getEl();
37878     
37879     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37880     
37881     config.layout.monitorWindowResize = false; // turn off autosizing
37882     this.layout = config.layout;
37883     this.layout.getEl().addClass("roo-layout-nested-layout");
37884     
37885     
37886     
37887     
37888 };
37889
37890 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37891
37892     setSize : function(width, height){
37893         if(!this.ignoreResize(width, height)){
37894             var size = this.adjustForComponents(width, height);
37895             var el = this.layout.getEl();
37896             if (size.height < 1) {
37897                 el.setWidth(size.width);   
37898             } else {
37899                 el.setSize(size.width, size.height);
37900             }
37901             var touch = el.dom.offsetWidth;
37902             this.layout.layout();
37903             // ie requires a double layout on the first pass
37904             if(Roo.isIE && !this.initialized){
37905                 this.initialized = true;
37906                 this.layout.layout();
37907             }
37908         }
37909     },
37910     
37911     // activate all subpanels if not currently active..
37912     
37913     setActiveState : function(active){
37914         this.active = active;
37915         this.setActiveClass(active);
37916         
37917         if(!active){
37918             this.fireEvent("deactivate", this);
37919             return;
37920         }
37921         
37922         this.fireEvent("activate", this);
37923         // not sure if this should happen before or after..
37924         if (!this.layout) {
37925             return; // should not happen..
37926         }
37927         var reg = false;
37928         for (var r in this.layout.regions) {
37929             reg = this.layout.getRegion(r);
37930             if (reg.getActivePanel()) {
37931                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37932                 reg.setActivePanel(reg.getActivePanel());
37933                 continue;
37934             }
37935             if (!reg.panels.length) {
37936                 continue;
37937             }
37938             reg.showPanel(reg.getPanel(0));
37939         }
37940         
37941         
37942         
37943         
37944     },
37945     
37946     /**
37947      * Returns the nested BorderLayout for this panel
37948      * @return {Roo.BorderLayout} 
37949      */
37950     getLayout : function(){
37951         return this.layout;
37952     },
37953     
37954      /**
37955      * Adds a xtype elements to the layout of the nested panel
37956      * <pre><code>
37957
37958 panel.addxtype({
37959        xtype : 'ContentPanel',
37960        region: 'west',
37961        items: [ .... ]
37962    }
37963 );
37964
37965 panel.addxtype({
37966         xtype : 'NestedLayoutPanel',
37967         region: 'west',
37968         layout: {
37969            center: { },
37970            west: { }   
37971         },
37972         items : [ ... list of content panels or nested layout panels.. ]
37973    }
37974 );
37975 </code></pre>
37976      * @param {Object} cfg Xtype definition of item to add.
37977      */
37978     addxtype : function(cfg) {
37979         return this.layout.addxtype(cfg);
37980     
37981     }
37982 });        /*
37983  * Based on:
37984  * Ext JS Library 1.1.1
37985  * Copyright(c) 2006-2007, Ext JS, LLC.
37986  *
37987  * Originally Released Under LGPL - original licence link has changed is not relivant.
37988  *
37989  * Fork - LGPL
37990  * <script type="text/javascript">
37991  */
37992 /**
37993  * @class Roo.TabPanel
37994  * @extends Roo.util.Observable
37995  * A lightweight tab container.
37996  * <br><br>
37997  * Usage:
37998  * <pre><code>
37999 // basic tabs 1, built from existing content
38000 var tabs = new Roo.TabPanel("tabs1");
38001 tabs.addTab("script", "View Script");
38002 tabs.addTab("markup", "View Markup");
38003 tabs.activate("script");
38004
38005 // more advanced tabs, built from javascript
38006 var jtabs = new Roo.TabPanel("jtabs");
38007 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38008
38009 // set up the UpdateManager
38010 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38011 var updater = tab2.getUpdateManager();
38012 updater.setDefaultUrl("ajax1.htm");
38013 tab2.on('activate', updater.refresh, updater, true);
38014
38015 // Use setUrl for Ajax loading
38016 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38017 tab3.setUrl("ajax2.htm", null, true);
38018
38019 // Disabled tab
38020 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38021 tab4.disable();
38022
38023 jtabs.activate("jtabs-1");
38024  * </code></pre>
38025  * @constructor
38026  * Create a new TabPanel.
38027  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38028  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38029  */
38030 Roo.bootstrap.panel.Tabs = function(config){
38031     /**
38032     * The container element for this TabPanel.
38033     * @type Roo.Element
38034     */
38035     this.el = Roo.get(config.el);
38036     delete config.el;
38037     if(config){
38038         if(typeof config == "boolean"){
38039             this.tabPosition = config ? "bottom" : "top";
38040         }else{
38041             Roo.apply(this, config);
38042         }
38043     }
38044     
38045     if(this.tabPosition == "bottom"){
38046         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38047         this.el.addClass("roo-tabs-bottom");
38048     }
38049     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38050     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38051     this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38052     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38053     if(Roo.isIE){
38054         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38055     }
38056     if(this.tabPosition != "bottom"){
38057         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38058          * @type Roo.Element
38059          */
38060         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38061         this.el.addClass("roo-tabs-top");
38062     }
38063     this.items = [];
38064
38065     this.bodyEl.setStyle("position", "relative");
38066
38067     this.active = null;
38068     this.activateDelegate = this.activate.createDelegate(this);
38069
38070     this.addEvents({
38071         /**
38072          * @event tabchange
38073          * Fires when the active tab changes
38074          * @param {Roo.TabPanel} this
38075          * @param {Roo.TabPanelItem} activePanel The new active tab
38076          */
38077         "tabchange": true,
38078         /**
38079          * @event beforetabchange
38080          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38081          * @param {Roo.TabPanel} this
38082          * @param {Object} e Set cancel to true on this object to cancel the tab change
38083          * @param {Roo.TabPanelItem} tab The tab being changed to
38084          */
38085         "beforetabchange" : true
38086     });
38087
38088     Roo.EventManager.onWindowResize(this.onResize, this);
38089     this.cpad = this.el.getPadding("lr");
38090     this.hiddenCount = 0;
38091
38092
38093     // toolbar on the tabbar support...
38094     if (this.toolbar) {
38095         alert("no toolbar support yet");
38096         this.toolbar  = false;
38097         /*
38098         var tcfg = this.toolbar;
38099         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38100         this.toolbar = new Roo.Toolbar(tcfg);
38101         if (Roo.isSafari) {
38102             var tbl = tcfg.container.child('table', true);
38103             tbl.setAttribute('width', '100%');
38104         }
38105         */
38106         
38107     }
38108    
38109
38110
38111     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38112 };
38113
38114 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38115     /*
38116      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38117      */
38118     tabPosition : "top",
38119     /*
38120      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38121      */
38122     currentTabWidth : 0,
38123     /*
38124      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38125      */
38126     minTabWidth : 40,
38127     /*
38128      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38129      */
38130     maxTabWidth : 250,
38131     /*
38132      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38133      */
38134     preferredTabWidth : 175,
38135     /*
38136      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38137      */
38138     resizeTabs : false,
38139     /*
38140      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38141      */
38142     monitorResize : true,
38143     /*
38144      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38145      */
38146     toolbar : false,
38147
38148     /**
38149      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38150      * @param {String} id The id of the div to use <b>or create</b>
38151      * @param {String} text The text for the tab
38152      * @param {String} content (optional) Content to put in the TabPanelItem body
38153      * @param {Boolean} closable (optional) True to create a close icon on the tab
38154      * @return {Roo.TabPanelItem} The created TabPanelItem
38155      */
38156     addTab : function(id, text, content, closable, tpl)
38157     {
38158         var item = new Roo.bootstrap.panel.TabItem({
38159             panel: this,
38160             id : id,
38161             text : text,
38162             closable : closable,
38163             tpl : tpl
38164         });
38165         this.addTabItem(item);
38166         if(content){
38167             item.setContent(content);
38168         }
38169         return item;
38170     },
38171
38172     /**
38173      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38174      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38175      * @return {Roo.TabPanelItem}
38176      */
38177     getTab : function(id){
38178         return this.items[id];
38179     },
38180
38181     /**
38182      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38183      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38184      */
38185     hideTab : function(id){
38186         var t = this.items[id];
38187         if(!t.isHidden()){
38188            t.setHidden(true);
38189            this.hiddenCount++;
38190            this.autoSizeTabs();
38191         }
38192     },
38193
38194     /**
38195      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38196      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38197      */
38198     unhideTab : function(id){
38199         var t = this.items[id];
38200         if(t.isHidden()){
38201            t.setHidden(false);
38202            this.hiddenCount--;
38203            this.autoSizeTabs();
38204         }
38205     },
38206
38207     /**
38208      * Adds an existing {@link Roo.TabPanelItem}.
38209      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38210      */
38211     addTabItem : function(item)
38212     {
38213         this.items[item.id] = item;
38214         this.items.push(item);
38215         this.autoSizeTabs();
38216       //  if(this.resizeTabs){
38217     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38218   //         this.autoSizeTabs();
38219 //        }else{
38220 //            item.autoSize();
38221        // }
38222     },
38223
38224     /**
38225      * Removes a {@link Roo.TabPanelItem}.
38226      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38227      */
38228     removeTab : function(id){
38229         var items = this.items;
38230         var tab = items[id];
38231         if(!tab) { return; }
38232         var index = items.indexOf(tab);
38233         if(this.active == tab && items.length > 1){
38234             var newTab = this.getNextAvailable(index);
38235             if(newTab) {
38236                 newTab.activate();
38237             }
38238         }
38239         this.stripEl.dom.removeChild(tab.pnode.dom);
38240         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38241             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38242         }
38243         items.splice(index, 1);
38244         delete this.items[tab.id];
38245         tab.fireEvent("close", tab);
38246         tab.purgeListeners();
38247         this.autoSizeTabs();
38248     },
38249
38250     getNextAvailable : function(start){
38251         var items = this.items;
38252         var index = start;
38253         // look for a next tab that will slide over to
38254         // replace the one being removed
38255         while(index < items.length){
38256             var item = items[++index];
38257             if(item && !item.isHidden()){
38258                 return item;
38259             }
38260         }
38261         // if one isn't found select the previous tab (on the left)
38262         index = start;
38263         while(index >= 0){
38264             var item = items[--index];
38265             if(item && !item.isHidden()){
38266                 return item;
38267             }
38268         }
38269         return null;
38270     },
38271
38272     /**
38273      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38274      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38275      */
38276     disableTab : function(id){
38277         var tab = this.items[id];
38278         if(tab && this.active != tab){
38279             tab.disable();
38280         }
38281     },
38282
38283     /**
38284      * Enables a {@link Roo.TabPanelItem} that is disabled.
38285      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38286      */
38287     enableTab : function(id){
38288         var tab = this.items[id];
38289         tab.enable();
38290     },
38291
38292     /**
38293      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38294      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38295      * @return {Roo.TabPanelItem} The TabPanelItem.
38296      */
38297     activate : function(id)
38298     {
38299         var tab = this.items[id];
38300         if(!tab){
38301             return null;
38302         }
38303         if(tab == this.active || tab.disabled){
38304             return tab;
38305         }
38306         var e = {};
38307         this.fireEvent("beforetabchange", this, e, tab);
38308         if(e.cancel !== true && !tab.disabled){
38309             if(this.active){
38310                 this.active.hide();
38311             }
38312             this.active = this.items[id];
38313             this.active.show();
38314             this.fireEvent("tabchange", this, this.active);
38315         }
38316         return tab;
38317     },
38318
38319     /**
38320      * Gets the active {@link Roo.TabPanelItem}.
38321      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38322      */
38323     getActiveTab : function(){
38324         return this.active;
38325     },
38326
38327     /**
38328      * Updates the tab body element to fit the height of the container element
38329      * for overflow scrolling
38330      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38331      */
38332     syncHeight : function(targetHeight){
38333         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38334         var bm = this.bodyEl.getMargins();
38335         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38336         this.bodyEl.setHeight(newHeight);
38337         return newHeight;
38338     },
38339
38340     onResize : function(){
38341         if(this.monitorResize){
38342             this.autoSizeTabs();
38343         }
38344     },
38345
38346     /**
38347      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38348      */
38349     beginUpdate : function(){
38350         this.updating = true;
38351     },
38352
38353     /**
38354      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38355      */
38356     endUpdate : function(){
38357         this.updating = false;
38358         this.autoSizeTabs();
38359     },
38360
38361     /**
38362      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38363      */
38364     autoSizeTabs : function()
38365     {
38366         var count = this.items.length;
38367         var vcount = count - this.hiddenCount;
38368         
38369         if (vcount < 2) {
38370             this.stripEl.hide();
38371         } else {
38372             this.stripEl.show();
38373         }
38374         
38375         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38376             return;
38377         }
38378         
38379         
38380         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38381         var availWidth = Math.floor(w / vcount);
38382         var b = this.stripBody;
38383         if(b.getWidth() > w){
38384             var tabs = this.items;
38385             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38386             if(availWidth < this.minTabWidth){
38387                 /*if(!this.sleft){    // incomplete scrolling code
38388                     this.createScrollButtons();
38389                 }
38390                 this.showScroll();
38391                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38392             }
38393         }else{
38394             if(this.currentTabWidth < this.preferredTabWidth){
38395                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38396             }
38397         }
38398     },
38399
38400     /**
38401      * Returns the number of tabs in this TabPanel.
38402      * @return {Number}
38403      */
38404      getCount : function(){
38405          return this.items.length;
38406      },
38407
38408     /**
38409      * Resizes all the tabs to the passed width
38410      * @param {Number} The new width
38411      */
38412     setTabWidth : function(width){
38413         this.currentTabWidth = width;
38414         for(var i = 0, len = this.items.length; i < len; i++) {
38415                 if(!this.items[i].isHidden()) {
38416                 this.items[i].setWidth(width);
38417             }
38418         }
38419     },
38420
38421     /**
38422      * Destroys this TabPanel
38423      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38424      */
38425     destroy : function(removeEl){
38426         Roo.EventManager.removeResizeListener(this.onResize, this);
38427         for(var i = 0, len = this.items.length; i < len; i++){
38428             this.items[i].purgeListeners();
38429         }
38430         if(removeEl === true){
38431             this.el.update("");
38432             this.el.remove();
38433         }
38434     },
38435     
38436     createStrip : function(container)
38437     {
38438         var strip = document.createElement("nav");
38439         strip.className = Roo.bootstrap.version == 4 ?
38440             "navbar-light bg-light" : 
38441             "navbar navbar-default"; //"x-tabs-wrap";
38442         container.appendChild(strip);
38443         return strip;
38444     },
38445     
38446     createStripList : function(strip)
38447     {
38448         // div wrapper for retard IE
38449         // returns the "tr" element.
38450         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38451         //'<div class="x-tabs-strip-wrap">'+
38452           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38453           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38454         return strip.firstChild; //.firstChild.firstChild.firstChild;
38455     },
38456     createBody : function(container)
38457     {
38458         var body = document.createElement("div");
38459         Roo.id(body, "tab-body");
38460         //Roo.fly(body).addClass("x-tabs-body");
38461         Roo.fly(body).addClass("tab-content");
38462         container.appendChild(body);
38463         return body;
38464     },
38465     createItemBody :function(bodyEl, id){
38466         var body = Roo.getDom(id);
38467         if(!body){
38468             body = document.createElement("div");
38469             body.id = id;
38470         }
38471         //Roo.fly(body).addClass("x-tabs-item-body");
38472         Roo.fly(body).addClass("tab-pane");
38473          bodyEl.insertBefore(body, bodyEl.firstChild);
38474         return body;
38475     },
38476     /** @private */
38477     createStripElements :  function(stripEl, text, closable, tpl)
38478     {
38479         var td = document.createElement("li"); // was td..
38480         td.className = 'nav-item';
38481         
38482         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38483         
38484         
38485         stripEl.appendChild(td);
38486         /*if(closable){
38487             td.className = "x-tabs-closable";
38488             if(!this.closeTpl){
38489                 this.closeTpl = new Roo.Template(
38490                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38491                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38492                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38493                 );
38494             }
38495             var el = this.closeTpl.overwrite(td, {"text": text});
38496             var close = el.getElementsByTagName("div")[0];
38497             var inner = el.getElementsByTagName("em")[0];
38498             return {"el": el, "close": close, "inner": inner};
38499         } else {
38500         */
38501         // not sure what this is..
38502 //            if(!this.tabTpl){
38503                 //this.tabTpl = new Roo.Template(
38504                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38505                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38506                 //);
38507 //                this.tabTpl = new Roo.Template(
38508 //                   '<a href="#">' +
38509 //                   '<span unselectable="on"' +
38510 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38511 //                            ' >{text}</span></a>'
38512 //                );
38513 //                
38514 //            }
38515
38516
38517             var template = tpl || this.tabTpl || false;
38518             
38519             if(!template){
38520                 template =  new Roo.Template(
38521                         Roo.bootstrap.version == 4 ? 
38522                             (
38523                                 '<a class="nav-link" href="#" unselectable="on"' +
38524                                      (this.disableTooltips ? '' : ' title="{text}"') +
38525                                      ' >{text}</a>'
38526                             ) : (
38527                                 '<a class="nav-link" href="#">' +
38528                                 '<span unselectable="on"' +
38529                                          (this.disableTooltips ? '' : ' title="{text}"') +
38530                                     ' >{text}</span></a>'
38531                             )
38532                 );
38533             }
38534             
38535             switch (typeof(template)) {
38536                 case 'object' :
38537                     break;
38538                 case 'string' :
38539                     template = new Roo.Template(template);
38540                     break;
38541                 default :
38542                     break;
38543             }
38544             
38545             var el = template.overwrite(td, {"text": text});
38546             
38547             var inner = el.getElementsByTagName("span")[0];
38548             
38549             return {"el": el, "inner": inner};
38550             
38551     }
38552         
38553     
38554 });
38555
38556 /**
38557  * @class Roo.TabPanelItem
38558  * @extends Roo.util.Observable
38559  * Represents an individual item (tab plus body) in a TabPanel.
38560  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38561  * @param {String} id The id of this TabPanelItem
38562  * @param {String} text The text for the tab of this TabPanelItem
38563  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38564  */
38565 Roo.bootstrap.panel.TabItem = function(config){
38566     /**
38567      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38568      * @type Roo.TabPanel
38569      */
38570     this.tabPanel = config.panel;
38571     /**
38572      * The id for this TabPanelItem
38573      * @type String
38574      */
38575     this.id = config.id;
38576     /** @private */
38577     this.disabled = false;
38578     /** @private */
38579     this.text = config.text;
38580     /** @private */
38581     this.loaded = false;
38582     this.closable = config.closable;
38583
38584     /**
38585      * The body element for this TabPanelItem.
38586      * @type Roo.Element
38587      */
38588     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38589     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38590     this.bodyEl.setStyle("display", "block");
38591     this.bodyEl.setStyle("zoom", "1");
38592     //this.hideAction();
38593
38594     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38595     /** @private */
38596     this.el = Roo.get(els.el);
38597     this.inner = Roo.get(els.inner, true);
38598      this.textEl = Roo.bootstrap.version == 4 ?
38599         this.el : Roo.get(this.el.dom.firstChild, true);
38600
38601     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38602     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38603
38604     
38605 //    this.el.on("mousedown", this.onTabMouseDown, this);
38606     this.el.on("click", this.onTabClick, this);
38607     /** @private */
38608     if(config.closable){
38609         var c = Roo.get(els.close, true);
38610         c.dom.title = this.closeText;
38611         c.addClassOnOver("close-over");
38612         c.on("click", this.closeClick, this);
38613      }
38614
38615     this.addEvents({
38616          /**
38617          * @event activate
38618          * Fires when this tab becomes the active tab.
38619          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38620          * @param {Roo.TabPanelItem} this
38621          */
38622         "activate": true,
38623         /**
38624          * @event beforeclose
38625          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38626          * @param {Roo.TabPanelItem} this
38627          * @param {Object} e Set cancel to true on this object to cancel the close.
38628          */
38629         "beforeclose": true,
38630         /**
38631          * @event close
38632          * Fires when this tab is closed.
38633          * @param {Roo.TabPanelItem} this
38634          */
38635          "close": true,
38636         /**
38637          * @event deactivate
38638          * Fires when this tab is no longer the active tab.
38639          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38640          * @param {Roo.TabPanelItem} this
38641          */
38642          "deactivate" : true
38643     });
38644     this.hidden = false;
38645
38646     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38647 };
38648
38649 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38650            {
38651     purgeListeners : function(){
38652        Roo.util.Observable.prototype.purgeListeners.call(this);
38653        this.el.removeAllListeners();
38654     },
38655     /**
38656      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38657      */
38658     show : function(){
38659         this.status_node.addClass("active");
38660         this.showAction();
38661         if(Roo.isOpera){
38662             this.tabPanel.stripWrap.repaint();
38663         }
38664         this.fireEvent("activate", this.tabPanel, this);
38665     },
38666
38667     /**
38668      * Returns true if this tab is the active tab.
38669      * @return {Boolean}
38670      */
38671     isActive : function(){
38672         return this.tabPanel.getActiveTab() == this;
38673     },
38674
38675     /**
38676      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38677      */
38678     hide : function(){
38679         this.status_node.removeClass("active");
38680         this.hideAction();
38681         this.fireEvent("deactivate", this.tabPanel, this);
38682     },
38683
38684     hideAction : function(){
38685         this.bodyEl.hide();
38686         this.bodyEl.setStyle("position", "absolute");
38687         this.bodyEl.setLeft("-20000px");
38688         this.bodyEl.setTop("-20000px");
38689     },
38690
38691     showAction : function(){
38692         this.bodyEl.setStyle("position", "relative");
38693         this.bodyEl.setTop("");
38694         this.bodyEl.setLeft("");
38695         this.bodyEl.show();
38696     },
38697
38698     /**
38699      * Set the tooltip for the tab.
38700      * @param {String} tooltip The tab's tooltip
38701      */
38702     setTooltip : function(text){
38703         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38704             this.textEl.dom.qtip = text;
38705             this.textEl.dom.removeAttribute('title');
38706         }else{
38707             this.textEl.dom.title = text;
38708         }
38709     },
38710
38711     onTabClick : function(e){
38712         e.preventDefault();
38713         this.tabPanel.activate(this.id);
38714     },
38715
38716     onTabMouseDown : function(e){
38717         e.preventDefault();
38718         this.tabPanel.activate(this.id);
38719     },
38720 /*
38721     getWidth : function(){
38722         return this.inner.getWidth();
38723     },
38724
38725     setWidth : function(width){
38726         var iwidth = width - this.linode.getPadding("lr");
38727         this.inner.setWidth(iwidth);
38728         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38729         this.linode.setWidth(width);
38730     },
38731 */
38732     /**
38733      * Show or hide the tab
38734      * @param {Boolean} hidden True to hide or false to show.
38735      */
38736     setHidden : function(hidden){
38737         this.hidden = hidden;
38738         this.linode.setStyle("display", hidden ? "none" : "");
38739     },
38740
38741     /**
38742      * Returns true if this tab is "hidden"
38743      * @return {Boolean}
38744      */
38745     isHidden : function(){
38746         return this.hidden;
38747     },
38748
38749     /**
38750      * Returns the text for this tab
38751      * @return {String}
38752      */
38753     getText : function(){
38754         return this.text;
38755     },
38756     /*
38757     autoSize : function(){
38758         //this.el.beginMeasure();
38759         this.textEl.setWidth(1);
38760         /*
38761          *  #2804 [new] Tabs in Roojs
38762          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38763          */
38764         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38765         //this.el.endMeasure();
38766     //},
38767
38768     /**
38769      * Sets the text for the tab (Note: this also sets the tooltip text)
38770      * @param {String} text The tab's text and tooltip
38771      */
38772     setText : function(text){
38773         this.text = text;
38774         this.textEl.update(text);
38775         this.setTooltip(text);
38776         //if(!this.tabPanel.resizeTabs){
38777         //    this.autoSize();
38778         //}
38779     },
38780     /**
38781      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38782      */
38783     activate : function(){
38784         this.tabPanel.activate(this.id);
38785     },
38786
38787     /**
38788      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38789      */
38790     disable : function(){
38791         if(this.tabPanel.active != this){
38792             this.disabled = true;
38793             this.status_node.addClass("disabled");
38794         }
38795     },
38796
38797     /**
38798      * Enables this TabPanelItem if it was previously disabled.
38799      */
38800     enable : function(){
38801         this.disabled = false;
38802         this.status_node.removeClass("disabled");
38803     },
38804
38805     /**
38806      * Sets the content for this TabPanelItem.
38807      * @param {String} content The content
38808      * @param {Boolean} loadScripts true to look for and load scripts
38809      */
38810     setContent : function(content, loadScripts){
38811         this.bodyEl.update(content, loadScripts);
38812     },
38813
38814     /**
38815      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38816      * @return {Roo.UpdateManager} The UpdateManager
38817      */
38818     getUpdateManager : function(){
38819         return this.bodyEl.getUpdateManager();
38820     },
38821
38822     /**
38823      * Set a URL to be used to load the content for this TabPanelItem.
38824      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38825      * @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)
38826      * @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)
38827      * @return {Roo.UpdateManager} The UpdateManager
38828      */
38829     setUrl : function(url, params, loadOnce){
38830         if(this.refreshDelegate){
38831             this.un('activate', this.refreshDelegate);
38832         }
38833         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38834         this.on("activate", this.refreshDelegate);
38835         return this.bodyEl.getUpdateManager();
38836     },
38837
38838     /** @private */
38839     _handleRefresh : function(url, params, loadOnce){
38840         if(!loadOnce || !this.loaded){
38841             var updater = this.bodyEl.getUpdateManager();
38842             updater.update(url, params, this._setLoaded.createDelegate(this));
38843         }
38844     },
38845
38846     /**
38847      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38848      *   Will fail silently if the setUrl method has not been called.
38849      *   This does not activate the panel, just updates its content.
38850      */
38851     refresh : function(){
38852         if(this.refreshDelegate){
38853            this.loaded = false;
38854            this.refreshDelegate();
38855         }
38856     },
38857
38858     /** @private */
38859     _setLoaded : function(){
38860         this.loaded = true;
38861     },
38862
38863     /** @private */
38864     closeClick : function(e){
38865         var o = {};
38866         e.stopEvent();
38867         this.fireEvent("beforeclose", this, o);
38868         if(o.cancel !== true){
38869             this.tabPanel.removeTab(this.id);
38870         }
38871     },
38872     /**
38873      * The text displayed in the tooltip for the close icon.
38874      * @type String
38875      */
38876     closeText : "Close this tab"
38877 });
38878 /**
38879 *    This script refer to:
38880 *    Title: International Telephone Input
38881 *    Author: Jack O'Connor
38882 *    Code version:  v12.1.12
38883 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38884 **/
38885
38886 Roo.bootstrap.PhoneInputData = function() {
38887     var d = [
38888       [
38889         "Afghanistan (‫افغانستان‬‎)",
38890         "af",
38891         "93"
38892       ],
38893       [
38894         "Albania (Shqipëri)",
38895         "al",
38896         "355"
38897       ],
38898       [
38899         "Algeria (‫الجزائر‬‎)",
38900         "dz",
38901         "213"
38902       ],
38903       [
38904         "American Samoa",
38905         "as",
38906         "1684"
38907       ],
38908       [
38909         "Andorra",
38910         "ad",
38911         "376"
38912       ],
38913       [
38914         "Angola",
38915         "ao",
38916         "244"
38917       ],
38918       [
38919         "Anguilla",
38920         "ai",
38921         "1264"
38922       ],
38923       [
38924         "Antigua and Barbuda",
38925         "ag",
38926         "1268"
38927       ],
38928       [
38929         "Argentina",
38930         "ar",
38931         "54"
38932       ],
38933       [
38934         "Armenia (Հայաստան)",
38935         "am",
38936         "374"
38937       ],
38938       [
38939         "Aruba",
38940         "aw",
38941         "297"
38942       ],
38943       [
38944         "Australia",
38945         "au",
38946         "61",
38947         0
38948       ],
38949       [
38950         "Austria (Österreich)",
38951         "at",
38952         "43"
38953       ],
38954       [
38955         "Azerbaijan (Azərbaycan)",
38956         "az",
38957         "994"
38958       ],
38959       [
38960         "Bahamas",
38961         "bs",
38962         "1242"
38963       ],
38964       [
38965         "Bahrain (‫البحرين‬‎)",
38966         "bh",
38967         "973"
38968       ],
38969       [
38970         "Bangladesh (বাংলাদেশ)",
38971         "bd",
38972         "880"
38973       ],
38974       [
38975         "Barbados",
38976         "bb",
38977         "1246"
38978       ],
38979       [
38980         "Belarus (Беларусь)",
38981         "by",
38982         "375"
38983       ],
38984       [
38985         "Belgium (België)",
38986         "be",
38987         "32"
38988       ],
38989       [
38990         "Belize",
38991         "bz",
38992         "501"
38993       ],
38994       [
38995         "Benin (Bénin)",
38996         "bj",
38997         "229"
38998       ],
38999       [
39000         "Bermuda",
39001         "bm",
39002         "1441"
39003       ],
39004       [
39005         "Bhutan (འབྲུག)",
39006         "bt",
39007         "975"
39008       ],
39009       [
39010         "Bolivia",
39011         "bo",
39012         "591"
39013       ],
39014       [
39015         "Bosnia and Herzegovina (Босна и Херцеговина)",
39016         "ba",
39017         "387"
39018       ],
39019       [
39020         "Botswana",
39021         "bw",
39022         "267"
39023       ],
39024       [
39025         "Brazil (Brasil)",
39026         "br",
39027         "55"
39028       ],
39029       [
39030         "British Indian Ocean Territory",
39031         "io",
39032         "246"
39033       ],
39034       [
39035         "British Virgin Islands",
39036         "vg",
39037         "1284"
39038       ],
39039       [
39040         "Brunei",
39041         "bn",
39042         "673"
39043       ],
39044       [
39045         "Bulgaria (България)",
39046         "bg",
39047         "359"
39048       ],
39049       [
39050         "Burkina Faso",
39051         "bf",
39052         "226"
39053       ],
39054       [
39055         "Burundi (Uburundi)",
39056         "bi",
39057         "257"
39058       ],
39059       [
39060         "Cambodia (កម្ពុជា)",
39061         "kh",
39062         "855"
39063       ],
39064       [
39065         "Cameroon (Cameroun)",
39066         "cm",
39067         "237"
39068       ],
39069       [
39070         "Canada",
39071         "ca",
39072         "1",
39073         1,
39074         ["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"]
39075       ],
39076       [
39077         "Cape Verde (Kabu Verdi)",
39078         "cv",
39079         "238"
39080       ],
39081       [
39082         "Caribbean Netherlands",
39083         "bq",
39084         "599",
39085         1
39086       ],
39087       [
39088         "Cayman Islands",
39089         "ky",
39090         "1345"
39091       ],
39092       [
39093         "Central African Republic (République centrafricaine)",
39094         "cf",
39095         "236"
39096       ],
39097       [
39098         "Chad (Tchad)",
39099         "td",
39100         "235"
39101       ],
39102       [
39103         "Chile",
39104         "cl",
39105         "56"
39106       ],
39107       [
39108         "China (中国)",
39109         "cn",
39110         "86"
39111       ],
39112       [
39113         "Christmas Island",
39114         "cx",
39115         "61",
39116         2
39117       ],
39118       [
39119         "Cocos (Keeling) Islands",
39120         "cc",
39121         "61",
39122         1
39123       ],
39124       [
39125         "Colombia",
39126         "co",
39127         "57"
39128       ],
39129       [
39130         "Comoros (‫جزر القمر‬‎)",
39131         "km",
39132         "269"
39133       ],
39134       [
39135         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39136         "cd",
39137         "243"
39138       ],
39139       [
39140         "Congo (Republic) (Congo-Brazzaville)",
39141         "cg",
39142         "242"
39143       ],
39144       [
39145         "Cook Islands",
39146         "ck",
39147         "682"
39148       ],
39149       [
39150         "Costa Rica",
39151         "cr",
39152         "506"
39153       ],
39154       [
39155         "Côte d’Ivoire",
39156         "ci",
39157         "225"
39158       ],
39159       [
39160         "Croatia (Hrvatska)",
39161         "hr",
39162         "385"
39163       ],
39164       [
39165         "Cuba",
39166         "cu",
39167         "53"
39168       ],
39169       [
39170         "Curaçao",
39171         "cw",
39172         "599",
39173         0
39174       ],
39175       [
39176         "Cyprus (Κύπρος)",
39177         "cy",
39178         "357"
39179       ],
39180       [
39181         "Czech Republic (Česká republika)",
39182         "cz",
39183         "420"
39184       ],
39185       [
39186         "Denmark (Danmark)",
39187         "dk",
39188         "45"
39189       ],
39190       [
39191         "Djibouti",
39192         "dj",
39193         "253"
39194       ],
39195       [
39196         "Dominica",
39197         "dm",
39198         "1767"
39199       ],
39200       [
39201         "Dominican Republic (República Dominicana)",
39202         "do",
39203         "1",
39204         2,
39205         ["809", "829", "849"]
39206       ],
39207       [
39208         "Ecuador",
39209         "ec",
39210         "593"
39211       ],
39212       [
39213         "Egypt (‫مصر‬‎)",
39214         "eg",
39215         "20"
39216       ],
39217       [
39218         "El Salvador",
39219         "sv",
39220         "503"
39221       ],
39222       [
39223         "Equatorial Guinea (Guinea Ecuatorial)",
39224         "gq",
39225         "240"
39226       ],
39227       [
39228         "Eritrea",
39229         "er",
39230         "291"
39231       ],
39232       [
39233         "Estonia (Eesti)",
39234         "ee",
39235         "372"
39236       ],
39237       [
39238         "Ethiopia",
39239         "et",
39240         "251"
39241       ],
39242       [
39243         "Falkland Islands (Islas Malvinas)",
39244         "fk",
39245         "500"
39246       ],
39247       [
39248         "Faroe Islands (Føroyar)",
39249         "fo",
39250         "298"
39251       ],
39252       [
39253         "Fiji",
39254         "fj",
39255         "679"
39256       ],
39257       [
39258         "Finland (Suomi)",
39259         "fi",
39260         "358",
39261         0
39262       ],
39263       [
39264         "France",
39265         "fr",
39266         "33"
39267       ],
39268       [
39269         "French Guiana (Guyane française)",
39270         "gf",
39271         "594"
39272       ],
39273       [
39274         "French Polynesia (Polynésie française)",
39275         "pf",
39276         "689"
39277       ],
39278       [
39279         "Gabon",
39280         "ga",
39281         "241"
39282       ],
39283       [
39284         "Gambia",
39285         "gm",
39286         "220"
39287       ],
39288       [
39289         "Georgia (საქართველო)",
39290         "ge",
39291         "995"
39292       ],
39293       [
39294         "Germany (Deutschland)",
39295         "de",
39296         "49"
39297       ],
39298       [
39299         "Ghana (Gaana)",
39300         "gh",
39301         "233"
39302       ],
39303       [
39304         "Gibraltar",
39305         "gi",
39306         "350"
39307       ],
39308       [
39309         "Greece (Ελλάδα)",
39310         "gr",
39311         "30"
39312       ],
39313       [
39314         "Greenland (Kalaallit Nunaat)",
39315         "gl",
39316         "299"
39317       ],
39318       [
39319         "Grenada",
39320         "gd",
39321         "1473"
39322       ],
39323       [
39324         "Guadeloupe",
39325         "gp",
39326         "590",
39327         0
39328       ],
39329       [
39330         "Guam",
39331         "gu",
39332         "1671"
39333       ],
39334       [
39335         "Guatemala",
39336         "gt",
39337         "502"
39338       ],
39339       [
39340         "Guernsey",
39341         "gg",
39342         "44",
39343         1
39344       ],
39345       [
39346         "Guinea (Guinée)",
39347         "gn",
39348         "224"
39349       ],
39350       [
39351         "Guinea-Bissau (Guiné Bissau)",
39352         "gw",
39353         "245"
39354       ],
39355       [
39356         "Guyana",
39357         "gy",
39358         "592"
39359       ],
39360       [
39361         "Haiti",
39362         "ht",
39363         "509"
39364       ],
39365       [
39366         "Honduras",
39367         "hn",
39368         "504"
39369       ],
39370       [
39371         "Hong Kong (香港)",
39372         "hk",
39373         "852"
39374       ],
39375       [
39376         "Hungary (Magyarország)",
39377         "hu",
39378         "36"
39379       ],
39380       [
39381         "Iceland (Ísland)",
39382         "is",
39383         "354"
39384       ],
39385       [
39386         "India (भारत)",
39387         "in",
39388         "91"
39389       ],
39390       [
39391         "Indonesia",
39392         "id",
39393         "62"
39394       ],
39395       [
39396         "Iran (‫ایران‬‎)",
39397         "ir",
39398         "98"
39399       ],
39400       [
39401         "Iraq (‫العراق‬‎)",
39402         "iq",
39403         "964"
39404       ],
39405       [
39406         "Ireland",
39407         "ie",
39408         "353"
39409       ],
39410       [
39411         "Isle of Man",
39412         "im",
39413         "44",
39414         2
39415       ],
39416       [
39417         "Israel (‫ישראל‬‎)",
39418         "il",
39419         "972"
39420       ],
39421       [
39422         "Italy (Italia)",
39423         "it",
39424         "39",
39425         0
39426       ],
39427       [
39428         "Jamaica",
39429         "jm",
39430         "1876"
39431       ],
39432       [
39433         "Japan (日本)",
39434         "jp",
39435         "81"
39436       ],
39437       [
39438         "Jersey",
39439         "je",
39440         "44",
39441         3
39442       ],
39443       [
39444         "Jordan (‫الأردن‬‎)",
39445         "jo",
39446         "962"
39447       ],
39448       [
39449         "Kazakhstan (Казахстан)",
39450         "kz",
39451         "7",
39452         1
39453       ],
39454       [
39455         "Kenya",
39456         "ke",
39457         "254"
39458       ],
39459       [
39460         "Kiribati",
39461         "ki",
39462         "686"
39463       ],
39464       [
39465         "Kosovo",
39466         "xk",
39467         "383"
39468       ],
39469       [
39470         "Kuwait (‫الكويت‬‎)",
39471         "kw",
39472         "965"
39473       ],
39474       [
39475         "Kyrgyzstan (Кыргызстан)",
39476         "kg",
39477         "996"
39478       ],
39479       [
39480         "Laos (ລາວ)",
39481         "la",
39482         "856"
39483       ],
39484       [
39485         "Latvia (Latvija)",
39486         "lv",
39487         "371"
39488       ],
39489       [
39490         "Lebanon (‫لبنان‬‎)",
39491         "lb",
39492         "961"
39493       ],
39494       [
39495         "Lesotho",
39496         "ls",
39497         "266"
39498       ],
39499       [
39500         "Liberia",
39501         "lr",
39502         "231"
39503       ],
39504       [
39505         "Libya (‫ليبيا‬‎)",
39506         "ly",
39507         "218"
39508       ],
39509       [
39510         "Liechtenstein",
39511         "li",
39512         "423"
39513       ],
39514       [
39515         "Lithuania (Lietuva)",
39516         "lt",
39517         "370"
39518       ],
39519       [
39520         "Luxembourg",
39521         "lu",
39522         "352"
39523       ],
39524       [
39525         "Macau (澳門)",
39526         "mo",
39527         "853"
39528       ],
39529       [
39530         "Macedonia (FYROM) (Македонија)",
39531         "mk",
39532         "389"
39533       ],
39534       [
39535         "Madagascar (Madagasikara)",
39536         "mg",
39537         "261"
39538       ],
39539       [
39540         "Malawi",
39541         "mw",
39542         "265"
39543       ],
39544       [
39545         "Malaysia",
39546         "my",
39547         "60"
39548       ],
39549       [
39550         "Maldives",
39551         "mv",
39552         "960"
39553       ],
39554       [
39555         "Mali",
39556         "ml",
39557         "223"
39558       ],
39559       [
39560         "Malta",
39561         "mt",
39562         "356"
39563       ],
39564       [
39565         "Marshall Islands",
39566         "mh",
39567         "692"
39568       ],
39569       [
39570         "Martinique",
39571         "mq",
39572         "596"
39573       ],
39574       [
39575         "Mauritania (‫موريتانيا‬‎)",
39576         "mr",
39577         "222"
39578       ],
39579       [
39580         "Mauritius (Moris)",
39581         "mu",
39582         "230"
39583       ],
39584       [
39585         "Mayotte",
39586         "yt",
39587         "262",
39588         1
39589       ],
39590       [
39591         "Mexico (México)",
39592         "mx",
39593         "52"
39594       ],
39595       [
39596         "Micronesia",
39597         "fm",
39598         "691"
39599       ],
39600       [
39601         "Moldova (Republica Moldova)",
39602         "md",
39603         "373"
39604       ],
39605       [
39606         "Monaco",
39607         "mc",
39608         "377"
39609       ],
39610       [
39611         "Mongolia (Монгол)",
39612         "mn",
39613         "976"
39614       ],
39615       [
39616         "Montenegro (Crna Gora)",
39617         "me",
39618         "382"
39619       ],
39620       [
39621         "Montserrat",
39622         "ms",
39623         "1664"
39624       ],
39625       [
39626         "Morocco (‫المغرب‬‎)",
39627         "ma",
39628         "212",
39629         0
39630       ],
39631       [
39632         "Mozambique (Moçambique)",
39633         "mz",
39634         "258"
39635       ],
39636       [
39637         "Myanmar (Burma) (မြန်မာ)",
39638         "mm",
39639         "95"
39640       ],
39641       [
39642         "Namibia (Namibië)",
39643         "na",
39644         "264"
39645       ],
39646       [
39647         "Nauru",
39648         "nr",
39649         "674"
39650       ],
39651       [
39652         "Nepal (नेपाल)",
39653         "np",
39654         "977"
39655       ],
39656       [
39657         "Netherlands (Nederland)",
39658         "nl",
39659         "31"
39660       ],
39661       [
39662         "New Caledonia (Nouvelle-Calédonie)",
39663         "nc",
39664         "687"
39665       ],
39666       [
39667         "New Zealand",
39668         "nz",
39669         "64"
39670       ],
39671       [
39672         "Nicaragua",
39673         "ni",
39674         "505"
39675       ],
39676       [
39677         "Niger (Nijar)",
39678         "ne",
39679         "227"
39680       ],
39681       [
39682         "Nigeria",
39683         "ng",
39684         "234"
39685       ],
39686       [
39687         "Niue",
39688         "nu",
39689         "683"
39690       ],
39691       [
39692         "Norfolk Island",
39693         "nf",
39694         "672"
39695       ],
39696       [
39697         "North Korea (조선 민주주의 인민 공화국)",
39698         "kp",
39699         "850"
39700       ],
39701       [
39702         "Northern Mariana Islands",
39703         "mp",
39704         "1670"
39705       ],
39706       [
39707         "Norway (Norge)",
39708         "no",
39709         "47",
39710         0
39711       ],
39712       [
39713         "Oman (‫عُمان‬‎)",
39714         "om",
39715         "968"
39716       ],
39717       [
39718         "Pakistan (‫پاکستان‬‎)",
39719         "pk",
39720         "92"
39721       ],
39722       [
39723         "Palau",
39724         "pw",
39725         "680"
39726       ],
39727       [
39728         "Palestine (‫فلسطين‬‎)",
39729         "ps",
39730         "970"
39731       ],
39732       [
39733         "Panama (Panamá)",
39734         "pa",
39735         "507"
39736       ],
39737       [
39738         "Papua New Guinea",
39739         "pg",
39740         "675"
39741       ],
39742       [
39743         "Paraguay",
39744         "py",
39745         "595"
39746       ],
39747       [
39748         "Peru (Perú)",
39749         "pe",
39750         "51"
39751       ],
39752       [
39753         "Philippines",
39754         "ph",
39755         "63"
39756       ],
39757       [
39758         "Poland (Polska)",
39759         "pl",
39760         "48"
39761       ],
39762       [
39763         "Portugal",
39764         "pt",
39765         "351"
39766       ],
39767       [
39768         "Puerto Rico",
39769         "pr",
39770         "1",
39771         3,
39772         ["787", "939"]
39773       ],
39774       [
39775         "Qatar (‫قطر‬‎)",
39776         "qa",
39777         "974"
39778       ],
39779       [
39780         "Réunion (La Réunion)",
39781         "re",
39782         "262",
39783         0
39784       ],
39785       [
39786         "Romania (România)",
39787         "ro",
39788         "40"
39789       ],
39790       [
39791         "Russia (Россия)",
39792         "ru",
39793         "7",
39794         0
39795       ],
39796       [
39797         "Rwanda",
39798         "rw",
39799         "250"
39800       ],
39801       [
39802         "Saint Barthélemy",
39803         "bl",
39804         "590",
39805         1
39806       ],
39807       [
39808         "Saint Helena",
39809         "sh",
39810         "290"
39811       ],
39812       [
39813         "Saint Kitts and Nevis",
39814         "kn",
39815         "1869"
39816       ],
39817       [
39818         "Saint Lucia",
39819         "lc",
39820         "1758"
39821       ],
39822       [
39823         "Saint Martin (Saint-Martin (partie française))",
39824         "mf",
39825         "590",
39826         2
39827       ],
39828       [
39829         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39830         "pm",
39831         "508"
39832       ],
39833       [
39834         "Saint Vincent and the Grenadines",
39835         "vc",
39836         "1784"
39837       ],
39838       [
39839         "Samoa",
39840         "ws",
39841         "685"
39842       ],
39843       [
39844         "San Marino",
39845         "sm",
39846         "378"
39847       ],
39848       [
39849         "São Tomé and Príncipe (São Tomé e Príncipe)",
39850         "st",
39851         "239"
39852       ],
39853       [
39854         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39855         "sa",
39856         "966"
39857       ],
39858       [
39859         "Senegal (Sénégal)",
39860         "sn",
39861         "221"
39862       ],
39863       [
39864         "Serbia (Србија)",
39865         "rs",
39866         "381"
39867       ],
39868       [
39869         "Seychelles",
39870         "sc",
39871         "248"
39872       ],
39873       [
39874         "Sierra Leone",
39875         "sl",
39876         "232"
39877       ],
39878       [
39879         "Singapore",
39880         "sg",
39881         "65"
39882       ],
39883       [
39884         "Sint Maarten",
39885         "sx",
39886         "1721"
39887       ],
39888       [
39889         "Slovakia (Slovensko)",
39890         "sk",
39891         "421"
39892       ],
39893       [
39894         "Slovenia (Slovenija)",
39895         "si",
39896         "386"
39897       ],
39898       [
39899         "Solomon Islands",
39900         "sb",
39901         "677"
39902       ],
39903       [
39904         "Somalia (Soomaaliya)",
39905         "so",
39906         "252"
39907       ],
39908       [
39909         "South Africa",
39910         "za",
39911         "27"
39912       ],
39913       [
39914         "South Korea (대한민국)",
39915         "kr",
39916         "82"
39917       ],
39918       [
39919         "South Sudan (‫جنوب السودان‬‎)",
39920         "ss",
39921         "211"
39922       ],
39923       [
39924         "Spain (España)",
39925         "es",
39926         "34"
39927       ],
39928       [
39929         "Sri Lanka (ශ්‍රී ලංකාව)",
39930         "lk",
39931         "94"
39932       ],
39933       [
39934         "Sudan (‫السودان‬‎)",
39935         "sd",
39936         "249"
39937       ],
39938       [
39939         "Suriname",
39940         "sr",
39941         "597"
39942       ],
39943       [
39944         "Svalbard and Jan Mayen",
39945         "sj",
39946         "47",
39947         1
39948       ],
39949       [
39950         "Swaziland",
39951         "sz",
39952         "268"
39953       ],
39954       [
39955         "Sweden (Sverige)",
39956         "se",
39957         "46"
39958       ],
39959       [
39960         "Switzerland (Schweiz)",
39961         "ch",
39962         "41"
39963       ],
39964       [
39965         "Syria (‫سوريا‬‎)",
39966         "sy",
39967         "963"
39968       ],
39969       [
39970         "Taiwan (台灣)",
39971         "tw",
39972         "886"
39973       ],
39974       [
39975         "Tajikistan",
39976         "tj",
39977         "992"
39978       ],
39979       [
39980         "Tanzania",
39981         "tz",
39982         "255"
39983       ],
39984       [
39985         "Thailand (ไทย)",
39986         "th",
39987         "66"
39988       ],
39989       [
39990         "Timor-Leste",
39991         "tl",
39992         "670"
39993       ],
39994       [
39995         "Togo",
39996         "tg",
39997         "228"
39998       ],
39999       [
40000         "Tokelau",
40001         "tk",
40002         "690"
40003       ],
40004       [
40005         "Tonga",
40006         "to",
40007         "676"
40008       ],
40009       [
40010         "Trinidad and Tobago",
40011         "tt",
40012         "1868"
40013       ],
40014       [
40015         "Tunisia (‫تونس‬‎)",
40016         "tn",
40017         "216"
40018       ],
40019       [
40020         "Turkey (Türkiye)",
40021         "tr",
40022         "90"
40023       ],
40024       [
40025         "Turkmenistan",
40026         "tm",
40027         "993"
40028       ],
40029       [
40030         "Turks and Caicos Islands",
40031         "tc",
40032         "1649"
40033       ],
40034       [
40035         "Tuvalu",
40036         "tv",
40037         "688"
40038       ],
40039       [
40040         "U.S. Virgin Islands",
40041         "vi",
40042         "1340"
40043       ],
40044       [
40045         "Uganda",
40046         "ug",
40047         "256"
40048       ],
40049       [
40050         "Ukraine (Україна)",
40051         "ua",
40052         "380"
40053       ],
40054       [
40055         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
40056         "ae",
40057         "971"
40058       ],
40059       [
40060         "United Kingdom",
40061         "gb",
40062         "44",
40063         0
40064       ],
40065       [
40066         "United States",
40067         "us",
40068         "1",
40069         0
40070       ],
40071       [
40072         "Uruguay",
40073         "uy",
40074         "598"
40075       ],
40076       [
40077         "Uzbekistan (Oʻzbekiston)",
40078         "uz",
40079         "998"
40080       ],
40081       [
40082         "Vanuatu",
40083         "vu",
40084         "678"
40085       ],
40086       [
40087         "Vatican City (Città del Vaticano)",
40088         "va",
40089         "39",
40090         1
40091       ],
40092       [
40093         "Venezuela",
40094         "ve",
40095         "58"
40096       ],
40097       [
40098         "Vietnam (Việt Nam)",
40099         "vn",
40100         "84"
40101       ],
40102       [
40103         "Wallis and Futuna (Wallis-et-Futuna)",
40104         "wf",
40105         "681"
40106       ],
40107       [
40108         "Western Sahara (‫الصحراء الغربية‬‎)",
40109         "eh",
40110         "212",
40111         1
40112       ],
40113       [
40114         "Yemen (‫اليمن‬‎)",
40115         "ye",
40116         "967"
40117       ],
40118       [
40119         "Zambia",
40120         "zm",
40121         "260"
40122       ],
40123       [
40124         "Zimbabwe",
40125         "zw",
40126         "263"
40127       ],
40128       [
40129         "Åland Islands",
40130         "ax",
40131         "358",
40132         1
40133       ]
40134   ];
40135   
40136   return d;
40137 }/**
40138 *    This script refer to:
40139 *    Title: International Telephone Input
40140 *    Author: Jack O'Connor
40141 *    Code version:  v12.1.12
40142 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40143 **/
40144
40145 /**
40146  * @class Roo.bootstrap.PhoneInput
40147  * @extends Roo.bootstrap.TriggerField
40148  * An input with International dial-code selection
40149  
40150  * @cfg {String} defaultDialCode default '+852'
40151  * @cfg {Array} preferedCountries default []
40152   
40153  * @constructor
40154  * Create a new PhoneInput.
40155  * @param {Object} config Configuration options
40156  */
40157
40158 Roo.bootstrap.PhoneInput = function(config) {
40159     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40160 };
40161
40162 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40163         
40164         listWidth: undefined,
40165         
40166         selectedClass: 'active',
40167         
40168         invalidClass : "has-warning",
40169         
40170         validClass: 'has-success',
40171         
40172         allowed: '0123456789',
40173         
40174         max_length: 15,
40175         
40176         /**
40177          * @cfg {String} defaultDialCode The default dial code when initializing the input
40178          */
40179         defaultDialCode: '+852',
40180         
40181         /**
40182          * @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
40183          */
40184         preferedCountries: false,
40185         
40186         getAutoCreate : function()
40187         {
40188             var data = Roo.bootstrap.PhoneInputData();
40189             var align = this.labelAlign || this.parentLabelAlign();
40190             var id = Roo.id();
40191             
40192             this.allCountries = [];
40193             this.dialCodeMapping = [];
40194             
40195             for (var i = 0; i < data.length; i++) {
40196               var c = data[i];
40197               this.allCountries[i] = {
40198                 name: c[0],
40199                 iso2: c[1],
40200                 dialCode: c[2],
40201                 priority: c[3] || 0,
40202                 areaCodes: c[4] || null
40203               };
40204               this.dialCodeMapping[c[2]] = {
40205                   name: c[0],
40206                   iso2: c[1],
40207                   priority: c[3] || 0,
40208                   areaCodes: c[4] || null
40209               };
40210             }
40211             
40212             var cfg = {
40213                 cls: 'form-group',
40214                 cn: []
40215             };
40216             
40217             var input =  {
40218                 tag: 'input',
40219                 id : id,
40220                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40221                 maxlength: this.max_length,
40222                 cls : 'form-control tel-input',
40223                 autocomplete: 'new-password'
40224             };
40225             
40226             var hiddenInput = {
40227                 tag: 'input',
40228                 type: 'hidden',
40229                 cls: 'hidden-tel-input'
40230             };
40231             
40232             if (this.name) {
40233                 hiddenInput.name = this.name;
40234             }
40235             
40236             if (this.disabled) {
40237                 input.disabled = true;
40238             }
40239             
40240             var flag_container = {
40241                 tag: 'div',
40242                 cls: 'flag-box',
40243                 cn: [
40244                     {
40245                         tag: 'div',
40246                         cls: 'flag'
40247                     },
40248                     {
40249                         tag: 'div',
40250                         cls: 'caret'
40251                     }
40252                 ]
40253             };
40254             
40255             var box = {
40256                 tag: 'div',
40257                 cls: this.hasFeedback ? 'has-feedback' : '',
40258                 cn: [
40259                     hiddenInput,
40260                     input,
40261                     {
40262                         tag: 'input',
40263                         cls: 'dial-code-holder',
40264                         disabled: true
40265                     }
40266                 ]
40267             };
40268             
40269             var container = {
40270                 cls: 'roo-select2-container input-group',
40271                 cn: [
40272                     flag_container,
40273                     box
40274                 ]
40275             };
40276             
40277             if (this.fieldLabel.length) {
40278                 var indicator = {
40279                     tag: 'i',
40280                     tooltip: 'This field is required'
40281                 };
40282                 
40283                 var label = {
40284                     tag: 'label',
40285                     'for':  id,
40286                     cls: 'control-label',
40287                     cn: []
40288                 };
40289                 
40290                 var label_text = {
40291                     tag: 'span',
40292                     html: this.fieldLabel
40293                 };
40294                 
40295                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40296                 label.cn = [
40297                     indicator,
40298                     label_text
40299                 ];
40300                 
40301                 if(this.indicatorpos == 'right') {
40302                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40303                     label.cn = [
40304                         label_text,
40305                         indicator
40306                     ];
40307                 }
40308                 
40309                 if(align == 'left') {
40310                     container = {
40311                         tag: 'div',
40312                         cn: [
40313                             container
40314                         ]
40315                     };
40316                     
40317                     if(this.labelWidth > 12){
40318                         label.style = "width: " + this.labelWidth + 'px';
40319                     }
40320                     if(this.labelWidth < 13 && this.labelmd == 0){
40321                         this.labelmd = this.labelWidth;
40322                     }
40323                     if(this.labellg > 0){
40324                         label.cls += ' col-lg-' + this.labellg;
40325                         input.cls += ' col-lg-' + (12 - this.labellg);
40326                     }
40327                     if(this.labelmd > 0){
40328                         label.cls += ' col-md-' + this.labelmd;
40329                         container.cls += ' col-md-' + (12 - this.labelmd);
40330                     }
40331                     if(this.labelsm > 0){
40332                         label.cls += ' col-sm-' + this.labelsm;
40333                         container.cls += ' col-sm-' + (12 - this.labelsm);
40334                     }
40335                     if(this.labelxs > 0){
40336                         label.cls += ' col-xs-' + this.labelxs;
40337                         container.cls += ' col-xs-' + (12 - this.labelxs);
40338                     }
40339                 }
40340             }
40341             
40342             cfg.cn = [
40343                 label,
40344                 container
40345             ];
40346             
40347             var settings = this;
40348             
40349             ['xs','sm','md','lg'].map(function(size){
40350                 if (settings[size]) {
40351                     cfg.cls += ' col-' + size + '-' + settings[size];
40352                 }
40353             });
40354             
40355             this.store = new Roo.data.Store({
40356                 proxy : new Roo.data.MemoryProxy({}),
40357                 reader : new Roo.data.JsonReader({
40358                     fields : [
40359                         {
40360                             'name' : 'name',
40361                             'type' : 'string'
40362                         },
40363                         {
40364                             'name' : 'iso2',
40365                             'type' : 'string'
40366                         },
40367                         {
40368                             'name' : 'dialCode',
40369                             'type' : 'string'
40370                         },
40371                         {
40372                             'name' : 'priority',
40373                             'type' : 'string'
40374                         },
40375                         {
40376                             'name' : 'areaCodes',
40377                             'type' : 'string'
40378                         }
40379                     ]
40380                 })
40381             });
40382             
40383             if(!this.preferedCountries) {
40384                 this.preferedCountries = [
40385                     'hk',
40386                     'gb',
40387                     'us'
40388                 ];
40389             }
40390             
40391             var p = this.preferedCountries.reverse();
40392             
40393             if(p) {
40394                 for (var i = 0; i < p.length; i++) {
40395                     for (var j = 0; j < this.allCountries.length; j++) {
40396                         if(this.allCountries[j].iso2 == p[i]) {
40397                             var t = this.allCountries[j];
40398                             this.allCountries.splice(j,1);
40399                             this.allCountries.unshift(t);
40400                         }
40401                     } 
40402                 }
40403             }
40404             
40405             this.store.proxy.data = {
40406                 success: true,
40407                 data: this.allCountries
40408             };
40409             
40410             return cfg;
40411         },
40412         
40413         initEvents : function()
40414         {
40415             this.createList();
40416             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40417             
40418             this.indicator = this.indicatorEl();
40419             this.flag = this.flagEl();
40420             this.dialCodeHolder = this.dialCodeHolderEl();
40421             
40422             this.trigger = this.el.select('div.flag-box',true).first();
40423             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40424             
40425             var _this = this;
40426             
40427             (function(){
40428                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40429                 _this.list.setWidth(lw);
40430             }).defer(100);
40431             
40432             this.list.on('mouseover', this.onViewOver, this);
40433             this.list.on('mousemove', this.onViewMove, this);
40434             this.inputEl().on("keyup", this.onKeyUp, this);
40435             this.inputEl().on("keypress", this.onKeyPress, this);
40436             
40437             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40438
40439             this.view = new Roo.View(this.list, this.tpl, {
40440                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40441             });
40442             
40443             this.view.on('click', this.onViewClick, this);
40444             this.setValue(this.defaultDialCode);
40445         },
40446         
40447         onTriggerClick : function(e)
40448         {
40449             Roo.log('trigger click');
40450             if(this.disabled){
40451                 return;
40452             }
40453             
40454             if(this.isExpanded()){
40455                 this.collapse();
40456                 this.hasFocus = false;
40457             }else {
40458                 this.store.load({});
40459                 this.hasFocus = true;
40460                 this.expand();
40461             }
40462         },
40463         
40464         isExpanded : function()
40465         {
40466             return this.list.isVisible();
40467         },
40468         
40469         collapse : function()
40470         {
40471             if(!this.isExpanded()){
40472                 return;
40473             }
40474             this.list.hide();
40475             Roo.get(document).un('mousedown', this.collapseIf, this);
40476             Roo.get(document).un('mousewheel', this.collapseIf, this);
40477             this.fireEvent('collapse', this);
40478             this.validate();
40479         },
40480         
40481         expand : function()
40482         {
40483             Roo.log('expand');
40484
40485             if(this.isExpanded() || !this.hasFocus){
40486                 return;
40487             }
40488             
40489             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40490             this.list.setWidth(lw);
40491             
40492             this.list.show();
40493             this.restrictHeight();
40494             
40495             Roo.get(document).on('mousedown', this.collapseIf, this);
40496             Roo.get(document).on('mousewheel', this.collapseIf, this);
40497             
40498             this.fireEvent('expand', this);
40499         },
40500         
40501         restrictHeight : function()
40502         {
40503             this.list.alignTo(this.inputEl(), this.listAlign);
40504             this.list.alignTo(this.inputEl(), this.listAlign);
40505         },
40506         
40507         onViewOver : function(e, t)
40508         {
40509             if(this.inKeyMode){
40510                 return;
40511             }
40512             var item = this.view.findItemFromChild(t);
40513             
40514             if(item){
40515                 var index = this.view.indexOf(item);
40516                 this.select(index, false);
40517             }
40518         },
40519
40520         // private
40521         onViewClick : function(view, doFocus, el, e)
40522         {
40523             var index = this.view.getSelectedIndexes()[0];
40524             
40525             var r = this.store.getAt(index);
40526             
40527             if(r){
40528                 this.onSelect(r, index);
40529             }
40530             if(doFocus !== false && !this.blockFocus){
40531                 this.inputEl().focus();
40532             }
40533         },
40534         
40535         onViewMove : function(e, t)
40536         {
40537             this.inKeyMode = false;
40538         },
40539         
40540         select : function(index, scrollIntoView)
40541         {
40542             this.selectedIndex = index;
40543             this.view.select(index);
40544             if(scrollIntoView !== false){
40545                 var el = this.view.getNode(index);
40546                 if(el){
40547                     this.list.scrollChildIntoView(el, false);
40548                 }
40549             }
40550         },
40551         
40552         createList : function()
40553         {
40554             this.list = Roo.get(document.body).createChild({
40555                 tag: 'ul',
40556                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40557                 style: 'display:none'
40558             });
40559             
40560             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40561         },
40562         
40563         collapseIf : function(e)
40564         {
40565             var in_combo  = e.within(this.el);
40566             var in_list =  e.within(this.list);
40567             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40568             
40569             if (in_combo || in_list || is_list) {
40570                 return;
40571             }
40572             this.collapse();
40573         },
40574         
40575         onSelect : function(record, index)
40576         {
40577             if(this.fireEvent('beforeselect', this, record, index) !== false){
40578                 
40579                 this.setFlagClass(record.data.iso2);
40580                 this.setDialCode(record.data.dialCode);
40581                 this.hasFocus = false;
40582                 this.collapse();
40583                 this.fireEvent('select', this, record, index);
40584             }
40585         },
40586         
40587         flagEl : function()
40588         {
40589             var flag = this.el.select('div.flag',true).first();
40590             if(!flag){
40591                 return false;
40592             }
40593             return flag;
40594         },
40595         
40596         dialCodeHolderEl : function()
40597         {
40598             var d = this.el.select('input.dial-code-holder',true).first();
40599             if(!d){
40600                 return false;
40601             }
40602             return d;
40603         },
40604         
40605         setDialCode : function(v)
40606         {
40607             this.dialCodeHolder.dom.value = '+'+v;
40608         },
40609         
40610         setFlagClass : function(n)
40611         {
40612             this.flag.dom.className = 'flag '+n;
40613         },
40614         
40615         getValue : function()
40616         {
40617             var v = this.inputEl().getValue();
40618             if(this.dialCodeHolder) {
40619                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40620             }
40621             return v;
40622         },
40623         
40624         setValue : function(v)
40625         {
40626             var d = this.getDialCode(v);
40627             
40628             //invalid dial code
40629             if(v.length == 0 || !d || d.length == 0) {
40630                 if(this.rendered){
40631                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40632                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40633                 }
40634                 return;
40635             }
40636             
40637             //valid dial code
40638             this.setFlagClass(this.dialCodeMapping[d].iso2);
40639             this.setDialCode(d);
40640             this.inputEl().dom.value = v.replace('+'+d,'');
40641             this.hiddenEl().dom.value = this.getValue();
40642             
40643             this.validate();
40644         },
40645         
40646         getDialCode : function(v)
40647         {
40648             v = v ||  '';
40649             
40650             if (v.length == 0) {
40651                 return this.dialCodeHolder.dom.value;
40652             }
40653             
40654             var dialCode = "";
40655             if (v.charAt(0) != "+") {
40656                 return false;
40657             }
40658             var numericChars = "";
40659             for (var i = 1; i < v.length; i++) {
40660               var c = v.charAt(i);
40661               if (!isNaN(c)) {
40662                 numericChars += c;
40663                 if (this.dialCodeMapping[numericChars]) {
40664                   dialCode = v.substr(1, i);
40665                 }
40666                 if (numericChars.length == 4) {
40667                   break;
40668                 }
40669               }
40670             }
40671             return dialCode;
40672         },
40673         
40674         reset : function()
40675         {
40676             this.setValue(this.defaultDialCode);
40677             this.validate();
40678         },
40679         
40680         hiddenEl : function()
40681         {
40682             return this.el.select('input.hidden-tel-input',true).first();
40683         },
40684         
40685         // after setting val
40686         onKeyUp : function(e){
40687             this.setValue(this.getValue());
40688         },
40689         
40690         onKeyPress : function(e){
40691             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40692                 e.stopEvent();
40693             }
40694         }
40695         
40696 });
40697 /**
40698  * @class Roo.bootstrap.MoneyField
40699  * @extends Roo.bootstrap.ComboBox
40700  * Bootstrap MoneyField class
40701  * 
40702  * @constructor
40703  * Create a new MoneyField.
40704  * @param {Object} config Configuration options
40705  */
40706
40707 Roo.bootstrap.MoneyField = function(config) {
40708     
40709     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40710     
40711 };
40712
40713 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40714     
40715     /**
40716      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40717      */
40718     allowDecimals : true,
40719     /**
40720      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40721      */
40722     decimalSeparator : ".",
40723     /**
40724      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40725      */
40726     decimalPrecision : 0,
40727     /**
40728      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40729      */
40730     allowNegative : true,
40731     /**
40732      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40733      */
40734     allowZero: true,
40735     /**
40736      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40737      */
40738     minValue : Number.NEGATIVE_INFINITY,
40739     /**
40740      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40741      */
40742     maxValue : Number.MAX_VALUE,
40743     /**
40744      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40745      */
40746     minText : "The minimum value for this field is {0}",
40747     /**
40748      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40749      */
40750     maxText : "The maximum value for this field is {0}",
40751     /**
40752      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40753      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40754      */
40755     nanText : "{0} is not a valid number",
40756     /**
40757      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40758      */
40759     castInt : true,
40760     /**
40761      * @cfg {String} defaults currency of the MoneyField
40762      * value should be in lkey
40763      */
40764     defaultCurrency : false,
40765     /**
40766      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40767      */
40768     thousandsDelimiter : false,
40769     /**
40770      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40771      */
40772     max_length: false,
40773     
40774     inputlg : 9,
40775     inputmd : 9,
40776     inputsm : 9,
40777     inputxs : 6,
40778     
40779     store : false,
40780     
40781     getAutoCreate : function()
40782     {
40783         var align = this.labelAlign || this.parentLabelAlign();
40784         
40785         var id = Roo.id();
40786
40787         var cfg = {
40788             cls: 'form-group',
40789             cn: []
40790         };
40791
40792         var input =  {
40793             tag: 'input',
40794             id : id,
40795             cls : 'form-control roo-money-amount-input',
40796             autocomplete: 'new-password'
40797         };
40798         
40799         var hiddenInput = {
40800             tag: 'input',
40801             type: 'hidden',
40802             id: Roo.id(),
40803             cls: 'hidden-number-input'
40804         };
40805         
40806         if(this.max_length) {
40807             input.maxlength = this.max_length; 
40808         }
40809         
40810         if (this.name) {
40811             hiddenInput.name = this.name;
40812         }
40813
40814         if (this.disabled) {
40815             input.disabled = true;
40816         }
40817
40818         var clg = 12 - this.inputlg;
40819         var cmd = 12 - this.inputmd;
40820         var csm = 12 - this.inputsm;
40821         var cxs = 12 - this.inputxs;
40822         
40823         var container = {
40824             tag : 'div',
40825             cls : 'row roo-money-field',
40826             cn : [
40827                 {
40828                     tag : 'div',
40829                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40830                     cn : [
40831                         {
40832                             tag : 'div',
40833                             cls: 'roo-select2-container input-group',
40834                             cn: [
40835                                 {
40836                                     tag : 'input',
40837                                     cls : 'form-control roo-money-currency-input',
40838                                     autocomplete: 'new-password',
40839                                     readOnly : 1,
40840                                     name : this.currencyName
40841                                 },
40842                                 {
40843                                     tag :'span',
40844                                     cls : 'input-group-addon',
40845                                     cn : [
40846                                         {
40847                                             tag: 'span',
40848                                             cls: 'caret'
40849                                         }
40850                                     ]
40851                                 }
40852                             ]
40853                         }
40854                     ]
40855                 },
40856                 {
40857                     tag : 'div',
40858                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40859                     cn : [
40860                         {
40861                             tag: 'div',
40862                             cls: this.hasFeedback ? 'has-feedback' : '',
40863                             cn: [
40864                                 input
40865                             ]
40866                         }
40867                     ]
40868                 }
40869             ]
40870             
40871         };
40872         
40873         if (this.fieldLabel.length) {
40874             var indicator = {
40875                 tag: 'i',
40876                 tooltip: 'This field is required'
40877             };
40878
40879             var label = {
40880                 tag: 'label',
40881                 'for':  id,
40882                 cls: 'control-label',
40883                 cn: []
40884             };
40885
40886             var label_text = {
40887                 tag: 'span',
40888                 html: this.fieldLabel
40889             };
40890
40891             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40892             label.cn = [
40893                 indicator,
40894                 label_text
40895             ];
40896
40897             if(this.indicatorpos == 'right') {
40898                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40899                 label.cn = [
40900                     label_text,
40901                     indicator
40902                 ];
40903             }
40904
40905             if(align == 'left') {
40906                 container = {
40907                     tag: 'div',
40908                     cn: [
40909                         container
40910                     ]
40911                 };
40912
40913                 if(this.labelWidth > 12){
40914                     label.style = "width: " + this.labelWidth + 'px';
40915                 }
40916                 if(this.labelWidth < 13 && this.labelmd == 0){
40917                     this.labelmd = this.labelWidth;
40918                 }
40919                 if(this.labellg > 0){
40920                     label.cls += ' col-lg-' + this.labellg;
40921                     input.cls += ' col-lg-' + (12 - this.labellg);
40922                 }
40923                 if(this.labelmd > 0){
40924                     label.cls += ' col-md-' + this.labelmd;
40925                     container.cls += ' col-md-' + (12 - this.labelmd);
40926                 }
40927                 if(this.labelsm > 0){
40928                     label.cls += ' col-sm-' + this.labelsm;
40929                     container.cls += ' col-sm-' + (12 - this.labelsm);
40930                 }
40931                 if(this.labelxs > 0){
40932                     label.cls += ' col-xs-' + this.labelxs;
40933                     container.cls += ' col-xs-' + (12 - this.labelxs);
40934                 }
40935             }
40936         }
40937
40938         cfg.cn = [
40939             label,
40940             container,
40941             hiddenInput
40942         ];
40943         
40944         var settings = this;
40945
40946         ['xs','sm','md','lg'].map(function(size){
40947             if (settings[size]) {
40948                 cfg.cls += ' col-' + size + '-' + settings[size];
40949             }
40950         });
40951         
40952         return cfg;
40953     },
40954     
40955     initEvents : function()
40956     {
40957         this.indicator = this.indicatorEl();
40958         
40959         this.initCurrencyEvent();
40960         
40961         this.initNumberEvent();
40962     },
40963     
40964     initCurrencyEvent : function()
40965     {
40966         if (!this.store) {
40967             throw "can not find store for combo";
40968         }
40969         
40970         this.store = Roo.factory(this.store, Roo.data);
40971         this.store.parent = this;
40972         
40973         this.createList();
40974         
40975         this.triggerEl = this.el.select('.input-group-addon', true).first();
40976         
40977         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40978         
40979         var _this = this;
40980         
40981         (function(){
40982             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40983             _this.list.setWidth(lw);
40984         }).defer(100);
40985         
40986         this.list.on('mouseover', this.onViewOver, this);
40987         this.list.on('mousemove', this.onViewMove, this);
40988         this.list.on('scroll', this.onViewScroll, this);
40989         
40990         if(!this.tpl){
40991             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40992         }
40993         
40994         this.view = new Roo.View(this.list, this.tpl, {
40995             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40996         });
40997         
40998         this.view.on('click', this.onViewClick, this);
40999         
41000         this.store.on('beforeload', this.onBeforeLoad, this);
41001         this.store.on('load', this.onLoad, this);
41002         this.store.on('loadexception', this.onLoadException, this);
41003         
41004         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41005             "up" : function(e){
41006                 this.inKeyMode = true;
41007                 this.selectPrev();
41008             },
41009
41010             "down" : function(e){
41011                 if(!this.isExpanded()){
41012                     this.onTriggerClick();
41013                 }else{
41014                     this.inKeyMode = true;
41015                     this.selectNext();
41016                 }
41017             },
41018
41019             "enter" : function(e){
41020                 this.collapse();
41021                 
41022                 if(this.fireEvent("specialkey", this, e)){
41023                     this.onViewClick(false);
41024                 }
41025                 
41026                 return true;
41027             },
41028
41029             "esc" : function(e){
41030                 this.collapse();
41031             },
41032
41033             "tab" : function(e){
41034                 this.collapse();
41035                 
41036                 if(this.fireEvent("specialkey", this, e)){
41037                     this.onViewClick(false);
41038                 }
41039                 
41040                 return true;
41041             },
41042
41043             scope : this,
41044
41045             doRelay : function(foo, bar, hname){
41046                 if(hname == 'down' || this.scope.isExpanded()){
41047                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41048                 }
41049                 return true;
41050             },
41051
41052             forceKeyDown: true
41053         });
41054         
41055         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41056         
41057     },
41058     
41059     initNumberEvent : function(e)
41060     {
41061         this.inputEl().on("keydown" , this.fireKey,  this);
41062         this.inputEl().on("focus", this.onFocus,  this);
41063         this.inputEl().on("blur", this.onBlur,  this);
41064         
41065         this.inputEl().relayEvent('keyup', this);
41066         
41067         if(this.indicator){
41068             this.indicator.addClass('invisible');
41069         }
41070  
41071         this.originalValue = this.getValue();
41072         
41073         if(this.validationEvent == 'keyup'){
41074             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41075             this.inputEl().on('keyup', this.filterValidation, this);
41076         }
41077         else if(this.validationEvent !== false){
41078             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41079         }
41080         
41081         if(this.selectOnFocus){
41082             this.on("focus", this.preFocus, this);
41083             
41084         }
41085         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41086             this.inputEl().on("keypress", this.filterKeys, this);
41087         } else {
41088             this.inputEl().relayEvent('keypress', this);
41089         }
41090         
41091         var allowed = "0123456789";
41092         
41093         if(this.allowDecimals){
41094             allowed += this.decimalSeparator;
41095         }
41096         
41097         if(this.allowNegative){
41098             allowed += "-";
41099         }
41100         
41101         if(this.thousandsDelimiter) {
41102             allowed += ",";
41103         }
41104         
41105         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41106         
41107         var keyPress = function(e){
41108             
41109             var k = e.getKey();
41110             
41111             var c = e.getCharCode();
41112             
41113             if(
41114                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41115                     allowed.indexOf(String.fromCharCode(c)) === -1
41116             ){
41117                 e.stopEvent();
41118                 return;
41119             }
41120             
41121             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41122                 return;
41123             }
41124             
41125             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41126                 e.stopEvent();
41127             }
41128         };
41129         
41130         this.inputEl().on("keypress", keyPress, this);
41131         
41132     },
41133     
41134     onTriggerClick : function(e)
41135     {   
41136         if(this.disabled){
41137             return;
41138         }
41139         
41140         this.page = 0;
41141         this.loadNext = false;
41142         
41143         if(this.isExpanded()){
41144             this.collapse();
41145             return;
41146         }
41147         
41148         this.hasFocus = true;
41149         
41150         if(this.triggerAction == 'all') {
41151             this.doQuery(this.allQuery, true);
41152             return;
41153         }
41154         
41155         this.doQuery(this.getRawValue());
41156     },
41157     
41158     getCurrency : function()
41159     {   
41160         var v = this.currencyEl().getValue();
41161         
41162         return v;
41163     },
41164     
41165     restrictHeight : function()
41166     {
41167         this.list.alignTo(this.currencyEl(), this.listAlign);
41168         this.list.alignTo(this.currencyEl(), this.listAlign);
41169     },
41170     
41171     onViewClick : function(view, doFocus, el, e)
41172     {
41173         var index = this.view.getSelectedIndexes()[0];
41174         
41175         var r = this.store.getAt(index);
41176         
41177         if(r){
41178             this.onSelect(r, index);
41179         }
41180     },
41181     
41182     onSelect : function(record, index){
41183         
41184         if(this.fireEvent('beforeselect', this, record, index) !== false){
41185         
41186             this.setFromCurrencyData(index > -1 ? record.data : false);
41187             
41188             this.collapse();
41189             
41190             this.fireEvent('select', this, record, index);
41191         }
41192     },
41193     
41194     setFromCurrencyData : function(o)
41195     {
41196         var currency = '';
41197         
41198         this.lastCurrency = o;
41199         
41200         if (this.currencyField) {
41201             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41202         } else {
41203             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41204         }
41205         
41206         this.lastSelectionText = currency;
41207         
41208         //setting default currency
41209         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41210             this.setCurrency(this.defaultCurrency);
41211             return;
41212         }
41213         
41214         this.setCurrency(currency);
41215     },
41216     
41217     setFromData : function(o)
41218     {
41219         var c = {};
41220         
41221         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41222         
41223         this.setFromCurrencyData(c);
41224         
41225         var value = '';
41226         
41227         if (this.name) {
41228             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41229         } else {
41230             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41231         }
41232         
41233         this.setValue(value);
41234         
41235     },
41236     
41237     setCurrency : function(v)
41238     {   
41239         this.currencyValue = v;
41240         
41241         if(this.rendered){
41242             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41243             this.validate();
41244         }
41245     },
41246     
41247     setValue : function(v)
41248     {
41249         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41250         
41251         this.value = v;
41252         
41253         if(this.rendered){
41254             
41255             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41256             
41257             this.inputEl().dom.value = (v == '') ? '' :
41258                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41259             
41260             if(!this.allowZero && v === '0') {
41261                 this.hiddenEl().dom.value = '';
41262                 this.inputEl().dom.value = '';
41263             }
41264             
41265             this.validate();
41266         }
41267     },
41268     
41269     getRawValue : function()
41270     {
41271         var v = this.inputEl().getValue();
41272         
41273         return v;
41274     },
41275     
41276     getValue : function()
41277     {
41278         return this.fixPrecision(this.parseValue(this.getRawValue()));
41279     },
41280     
41281     parseValue : function(value)
41282     {
41283         if(this.thousandsDelimiter) {
41284             value += "";
41285             r = new RegExp(",", "g");
41286             value = value.replace(r, "");
41287         }
41288         
41289         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41290         return isNaN(value) ? '' : value;
41291         
41292     },
41293     
41294     fixPrecision : function(value)
41295     {
41296         if(this.thousandsDelimiter) {
41297             value += "";
41298             r = new RegExp(",", "g");
41299             value = value.replace(r, "");
41300         }
41301         
41302         var nan = isNaN(value);
41303         
41304         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41305             return nan ? '' : value;
41306         }
41307         return parseFloat(value).toFixed(this.decimalPrecision);
41308     },
41309     
41310     decimalPrecisionFcn : function(v)
41311     {
41312         return Math.floor(v);
41313     },
41314     
41315     validateValue : function(value)
41316     {
41317         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41318             return false;
41319         }
41320         
41321         var num = this.parseValue(value);
41322         
41323         if(isNaN(num)){
41324             this.markInvalid(String.format(this.nanText, value));
41325             return false;
41326         }
41327         
41328         if(num < this.minValue){
41329             this.markInvalid(String.format(this.minText, this.minValue));
41330             return false;
41331         }
41332         
41333         if(num > this.maxValue){
41334             this.markInvalid(String.format(this.maxText, this.maxValue));
41335             return false;
41336         }
41337         
41338         return true;
41339     },
41340     
41341     validate : function()
41342     {
41343         if(this.disabled || this.allowBlank){
41344             this.markValid();
41345             return true;
41346         }
41347         
41348         var currency = this.getCurrency();
41349         
41350         if(this.validateValue(this.getRawValue()) && currency.length){
41351             this.markValid();
41352             return true;
41353         }
41354         
41355         this.markInvalid();
41356         return false;
41357     },
41358     
41359     getName: function()
41360     {
41361         return this.name;
41362     },
41363     
41364     beforeBlur : function()
41365     {
41366         if(!this.castInt){
41367             return;
41368         }
41369         
41370         var v = this.parseValue(this.getRawValue());
41371         
41372         if(v || v == 0){
41373             this.setValue(v);
41374         }
41375     },
41376     
41377     onBlur : function()
41378     {
41379         this.beforeBlur();
41380         
41381         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41382             //this.el.removeClass(this.focusClass);
41383         }
41384         
41385         this.hasFocus = false;
41386         
41387         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41388             this.validate();
41389         }
41390         
41391         var v = this.getValue();
41392         
41393         if(String(v) !== String(this.startValue)){
41394             this.fireEvent('change', this, v, this.startValue);
41395         }
41396         
41397         this.fireEvent("blur", this);
41398     },
41399     
41400     inputEl : function()
41401     {
41402         return this.el.select('.roo-money-amount-input', true).first();
41403     },
41404     
41405     currencyEl : function()
41406     {
41407         return this.el.select('.roo-money-currency-input', true).first();
41408     },
41409     
41410     hiddenEl : function()
41411     {
41412         return this.el.select('input.hidden-number-input',true).first();
41413     }
41414     
41415 });