86f8430322a62d0216a1f7a406196a9edf150391
[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         var cfg = {
18574             tag: 'div',
18575             // item is needed for carousel - not sure if it has any effect otherwise
18576             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18577             html: this.html || ''
18578         };
18579         
18580         if(this.active){
18581             cfg.cls += ' active';
18582         }
18583         
18584         if(this.tabId){
18585             cfg.tabId = this.tabId;
18586         }
18587         
18588         
18589         return cfg;
18590     },
18591     
18592     initEvents:  function()
18593     {
18594         var p = this.parent();
18595         
18596         this.navId = this.navId || p.navId;
18597         
18598         if (typeof(this.navId) != 'undefined') {
18599             // not really needed.. but just in case.. parent should be a NavGroup.
18600             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18601             
18602             tg.register(this);
18603             
18604             var i = tg.tabs.length - 1;
18605             
18606             if(this.active && tg.bullets > 0 && i < tg.bullets){
18607                 tg.setActiveBullet(i);
18608             }
18609         }
18610         
18611         this.el.on('click', this.onClick, this);
18612         
18613         if(Roo.isTouch){
18614             this.el.on("touchstart", this.onTouchStart, this);
18615             this.el.on("touchmove", this.onTouchMove, this);
18616             this.el.on("touchend", this.onTouchEnd, this);
18617         }
18618         
18619     },
18620     
18621     onRender : function(ct, position)
18622     {
18623         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18624     },
18625     
18626     setActive : function(state)
18627     {
18628         Roo.log("panel - set active " + this.tabId + "=" + state);
18629         
18630         this.active = state;
18631         if (!state) {
18632             this.el.removeClass('active');
18633             
18634         } else  if (!this.el.hasClass('active')) {
18635             this.el.addClass('active');
18636         }
18637         
18638         this.fireEvent('changed', this, state);
18639     },
18640     
18641     onClick : function(e)
18642     {
18643         e.preventDefault();
18644         
18645         if(!this.href.length){
18646             return;
18647         }
18648         
18649         window.location.href = this.href;
18650     },
18651     
18652     startX : 0,
18653     startY : 0,
18654     endX : 0,
18655     endY : 0,
18656     swiping : false,
18657     
18658     onTouchStart : function(e)
18659     {
18660         this.swiping = false;
18661         
18662         this.startX = e.browserEvent.touches[0].clientX;
18663         this.startY = e.browserEvent.touches[0].clientY;
18664     },
18665     
18666     onTouchMove : function(e)
18667     {
18668         this.swiping = true;
18669         
18670         this.endX = e.browserEvent.touches[0].clientX;
18671         this.endY = e.browserEvent.touches[0].clientY;
18672     },
18673     
18674     onTouchEnd : function(e)
18675     {
18676         if(!this.swiping){
18677             this.onClick(e);
18678             return;
18679         }
18680         
18681         var tabGroup = this.parent();
18682         
18683         if(this.endX > this.startX){ // swiping right
18684             tabGroup.showPanelPrev();
18685             return;
18686         }
18687         
18688         if(this.startX > this.endX){ // swiping left
18689             tabGroup.showPanelNext();
18690             return;
18691         }
18692     }
18693     
18694     
18695 });
18696  
18697
18698  
18699
18700  /*
18701  * - LGPL
18702  *
18703  * DateField
18704  * 
18705  */
18706
18707 /**
18708  * @class Roo.bootstrap.DateField
18709  * @extends Roo.bootstrap.Input
18710  * Bootstrap DateField class
18711  * @cfg {Number} weekStart default 0
18712  * @cfg {String} viewMode default empty, (months|years)
18713  * @cfg {String} minViewMode default empty, (months|years)
18714  * @cfg {Number} startDate default -Infinity
18715  * @cfg {Number} endDate default Infinity
18716  * @cfg {Boolean} todayHighlight default false
18717  * @cfg {Boolean} todayBtn default false
18718  * @cfg {Boolean} calendarWeeks default false
18719  * @cfg {Object} daysOfWeekDisabled default empty
18720  * @cfg {Boolean} singleMode default false (true | false)
18721  * 
18722  * @cfg {Boolean} keyboardNavigation default true
18723  * @cfg {String} language default en
18724  * 
18725  * @constructor
18726  * Create a new DateField
18727  * @param {Object} config The config object
18728  */
18729
18730 Roo.bootstrap.DateField = function(config){
18731     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18732      this.addEvents({
18733             /**
18734              * @event show
18735              * Fires when this field show.
18736              * @param {Roo.bootstrap.DateField} this
18737              * @param {Mixed} date The date value
18738              */
18739             show : true,
18740             /**
18741              * @event show
18742              * Fires when this field hide.
18743              * @param {Roo.bootstrap.DateField} this
18744              * @param {Mixed} date The date value
18745              */
18746             hide : true,
18747             /**
18748              * @event select
18749              * Fires when select a date.
18750              * @param {Roo.bootstrap.DateField} this
18751              * @param {Mixed} date The date value
18752              */
18753             select : true,
18754             /**
18755              * @event beforeselect
18756              * Fires when before select a date.
18757              * @param {Roo.bootstrap.DateField} this
18758              * @param {Mixed} date The date value
18759              */
18760             beforeselect : true
18761         });
18762 };
18763
18764 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18765     
18766     /**
18767      * @cfg {String} format
18768      * The default date format string which can be overriden for localization support.  The format must be
18769      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18770      */
18771     format : "m/d/y",
18772     /**
18773      * @cfg {String} altFormats
18774      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18775      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18776      */
18777     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18778     
18779     weekStart : 0,
18780     
18781     viewMode : '',
18782     
18783     minViewMode : '',
18784     
18785     todayHighlight : false,
18786     
18787     todayBtn: false,
18788     
18789     language: 'en',
18790     
18791     keyboardNavigation: true,
18792     
18793     calendarWeeks: false,
18794     
18795     startDate: -Infinity,
18796     
18797     endDate: Infinity,
18798     
18799     daysOfWeekDisabled: [],
18800     
18801     _events: [],
18802     
18803     singleMode : false,
18804     
18805     UTCDate: function()
18806     {
18807         return new Date(Date.UTC.apply(Date, arguments));
18808     },
18809     
18810     UTCToday: function()
18811     {
18812         var today = new Date();
18813         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18814     },
18815     
18816     getDate: function() {
18817             var d = this.getUTCDate();
18818             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18819     },
18820     
18821     getUTCDate: function() {
18822             return this.date;
18823     },
18824     
18825     setDate: function(d) {
18826             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18827     },
18828     
18829     setUTCDate: function(d) {
18830             this.date = d;
18831             this.setValue(this.formatDate(this.date));
18832     },
18833         
18834     onRender: function(ct, position)
18835     {
18836         
18837         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18838         
18839         this.language = this.language || 'en';
18840         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18841         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18842         
18843         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18844         this.format = this.format || 'm/d/y';
18845         this.isInline = false;
18846         this.isInput = true;
18847         this.component = this.el.select('.add-on', true).first() || false;
18848         this.component = (this.component && this.component.length === 0) ? false : this.component;
18849         this.hasInput = this.component && this.inputEl().length;
18850         
18851         if (typeof(this.minViewMode === 'string')) {
18852             switch (this.minViewMode) {
18853                 case 'months':
18854                     this.minViewMode = 1;
18855                     break;
18856                 case 'years':
18857                     this.minViewMode = 2;
18858                     break;
18859                 default:
18860                     this.minViewMode = 0;
18861                     break;
18862             }
18863         }
18864         
18865         if (typeof(this.viewMode === 'string')) {
18866             switch (this.viewMode) {
18867                 case 'months':
18868                     this.viewMode = 1;
18869                     break;
18870                 case 'years':
18871                     this.viewMode = 2;
18872                     break;
18873                 default:
18874                     this.viewMode = 0;
18875                     break;
18876             }
18877         }
18878                 
18879         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18880         
18881 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18882         
18883         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18884         
18885         this.picker().on('mousedown', this.onMousedown, this);
18886         this.picker().on('click', this.onClick, this);
18887         
18888         this.picker().addClass('datepicker-dropdown');
18889         
18890         this.startViewMode = this.viewMode;
18891         
18892         if(this.singleMode){
18893             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18894                 v.setVisibilityMode(Roo.Element.DISPLAY);
18895                 v.hide();
18896             });
18897             
18898             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18899                 v.setStyle('width', '189px');
18900             });
18901         }
18902         
18903         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18904             if(!this.calendarWeeks){
18905                 v.remove();
18906                 return;
18907             }
18908             
18909             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18910             v.attr('colspan', function(i, val){
18911                 return parseInt(val) + 1;
18912             });
18913         });
18914                         
18915         
18916         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18917         
18918         this.setStartDate(this.startDate);
18919         this.setEndDate(this.endDate);
18920         
18921         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18922         
18923         this.fillDow();
18924         this.fillMonths();
18925         this.update();
18926         this.showMode();
18927         
18928         if(this.isInline) {
18929             this.showPopup();
18930         }
18931     },
18932     
18933     picker : function()
18934     {
18935         return this.pickerEl;
18936 //        return this.el.select('.datepicker', true).first();
18937     },
18938     
18939     fillDow: function()
18940     {
18941         var dowCnt = this.weekStart;
18942         
18943         var dow = {
18944             tag: 'tr',
18945             cn: [
18946                 
18947             ]
18948         };
18949         
18950         if(this.calendarWeeks){
18951             dow.cn.push({
18952                 tag: 'th',
18953                 cls: 'cw',
18954                 html: '&nbsp;'
18955             })
18956         }
18957         
18958         while (dowCnt < this.weekStart + 7) {
18959             dow.cn.push({
18960                 tag: 'th',
18961                 cls: 'dow',
18962                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18963             });
18964         }
18965         
18966         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18967     },
18968     
18969     fillMonths: function()
18970     {    
18971         var i = 0;
18972         var months = this.picker().select('>.datepicker-months td', true).first();
18973         
18974         months.dom.innerHTML = '';
18975         
18976         while (i < 12) {
18977             var month = {
18978                 tag: 'span',
18979                 cls: 'month',
18980                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18981             };
18982             
18983             months.createChild(month);
18984         }
18985         
18986     },
18987     
18988     update: function()
18989     {
18990         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;
18991         
18992         if (this.date < this.startDate) {
18993             this.viewDate = new Date(this.startDate);
18994         } else if (this.date > this.endDate) {
18995             this.viewDate = new Date(this.endDate);
18996         } else {
18997             this.viewDate = new Date(this.date);
18998         }
18999         
19000         this.fill();
19001     },
19002     
19003     fill: function() 
19004     {
19005         var d = new Date(this.viewDate),
19006                 year = d.getUTCFullYear(),
19007                 month = d.getUTCMonth(),
19008                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19009                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19010                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19011                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19012                 currentDate = this.date && this.date.valueOf(),
19013                 today = this.UTCToday();
19014         
19015         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19016         
19017 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19018         
19019 //        this.picker.select('>tfoot th.today').
19020 //                                              .text(dates[this.language].today)
19021 //                                              .toggle(this.todayBtn !== false);
19022     
19023         this.updateNavArrows();
19024         this.fillMonths();
19025                                                 
19026         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19027         
19028         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19029          
19030         prevMonth.setUTCDate(day);
19031         
19032         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19033         
19034         var nextMonth = new Date(prevMonth);
19035         
19036         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19037         
19038         nextMonth = nextMonth.valueOf();
19039         
19040         var fillMonths = false;
19041         
19042         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19043         
19044         while(prevMonth.valueOf() <= nextMonth) {
19045             var clsName = '';
19046             
19047             if (prevMonth.getUTCDay() === this.weekStart) {
19048                 if(fillMonths){
19049                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19050                 }
19051                     
19052                 fillMonths = {
19053                     tag: 'tr',
19054                     cn: []
19055                 };
19056                 
19057                 if(this.calendarWeeks){
19058                     // ISO 8601: First week contains first thursday.
19059                     // ISO also states week starts on Monday, but we can be more abstract here.
19060                     var
19061                     // Start of current week: based on weekstart/current date
19062                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19063                     // Thursday of this week
19064                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19065                     // First Thursday of year, year from thursday
19066                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19067                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19068                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19069                     
19070                     fillMonths.cn.push({
19071                         tag: 'td',
19072                         cls: 'cw',
19073                         html: calWeek
19074                     });
19075                 }
19076             }
19077             
19078             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19079                 clsName += ' old';
19080             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19081                 clsName += ' new';
19082             }
19083             if (this.todayHighlight &&
19084                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19085                 prevMonth.getUTCMonth() == today.getMonth() &&
19086                 prevMonth.getUTCDate() == today.getDate()) {
19087                 clsName += ' today';
19088             }
19089             
19090             if (currentDate && prevMonth.valueOf() === currentDate) {
19091                 clsName += ' active';
19092             }
19093             
19094             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19095                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19096                     clsName += ' disabled';
19097             }
19098             
19099             fillMonths.cn.push({
19100                 tag: 'td',
19101                 cls: 'day ' + clsName,
19102                 html: prevMonth.getDate()
19103             });
19104             
19105             prevMonth.setDate(prevMonth.getDate()+1);
19106         }
19107           
19108         var currentYear = this.date && this.date.getUTCFullYear();
19109         var currentMonth = this.date && this.date.getUTCMonth();
19110         
19111         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19112         
19113         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19114             v.removeClass('active');
19115             
19116             if(currentYear === year && k === currentMonth){
19117                 v.addClass('active');
19118             }
19119             
19120             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19121                 v.addClass('disabled');
19122             }
19123             
19124         });
19125         
19126         
19127         year = parseInt(year/10, 10) * 10;
19128         
19129         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19130         
19131         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19132         
19133         year -= 1;
19134         for (var i = -1; i < 11; i++) {
19135             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19136                 tag: 'span',
19137                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19138                 html: year
19139             });
19140             
19141             year += 1;
19142         }
19143     },
19144     
19145     showMode: function(dir) 
19146     {
19147         if (dir) {
19148             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19149         }
19150         
19151         Roo.each(this.picker().select('>div',true).elements, function(v){
19152             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19153             v.hide();
19154         });
19155         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19156     },
19157     
19158     place: function()
19159     {
19160         if(this.isInline) {
19161             return;
19162         }
19163         
19164         this.picker().removeClass(['bottom', 'top']);
19165         
19166         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19167             /*
19168              * place to the top of element!
19169              *
19170              */
19171             
19172             this.picker().addClass('top');
19173             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19174             
19175             return;
19176         }
19177         
19178         this.picker().addClass('bottom');
19179         
19180         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19181     },
19182     
19183     parseDate : function(value)
19184     {
19185         if(!value || value instanceof Date){
19186             return value;
19187         }
19188         var v = Date.parseDate(value, this.format);
19189         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19190             v = Date.parseDate(value, 'Y-m-d');
19191         }
19192         if(!v && this.altFormats){
19193             if(!this.altFormatsArray){
19194                 this.altFormatsArray = this.altFormats.split("|");
19195             }
19196             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19197                 v = Date.parseDate(value, this.altFormatsArray[i]);
19198             }
19199         }
19200         return v;
19201     },
19202     
19203     formatDate : function(date, fmt)
19204     {   
19205         return (!date || !(date instanceof Date)) ?
19206         date : date.dateFormat(fmt || this.format);
19207     },
19208     
19209     onFocus : function()
19210     {
19211         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19212         this.showPopup();
19213     },
19214     
19215     onBlur : function()
19216     {
19217         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19218         
19219         var d = this.inputEl().getValue();
19220         
19221         this.setValue(d);
19222                 
19223         this.hidePopup();
19224     },
19225     
19226     showPopup : function()
19227     {
19228         this.picker().show();
19229         this.update();
19230         this.place();
19231         
19232         this.fireEvent('showpopup', this, this.date);
19233     },
19234     
19235     hidePopup : function()
19236     {
19237         if(this.isInline) {
19238             return;
19239         }
19240         this.picker().hide();
19241         this.viewMode = this.startViewMode;
19242         this.showMode();
19243         
19244         this.fireEvent('hidepopup', this, this.date);
19245         
19246     },
19247     
19248     onMousedown: function(e)
19249     {
19250         e.stopPropagation();
19251         e.preventDefault();
19252     },
19253     
19254     keyup: function(e)
19255     {
19256         Roo.bootstrap.DateField.superclass.keyup.call(this);
19257         this.update();
19258     },
19259
19260     setValue: function(v)
19261     {
19262         if(this.fireEvent('beforeselect', this, v) !== false){
19263             var d = new Date(this.parseDate(v) ).clearTime();
19264         
19265             if(isNaN(d.getTime())){
19266                 this.date = this.viewDate = '';
19267                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19268                 return;
19269             }
19270
19271             v = this.formatDate(d);
19272
19273             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19274
19275             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19276
19277             this.update();
19278
19279             this.fireEvent('select', this, this.date);
19280         }
19281     },
19282     
19283     getValue: function()
19284     {
19285         return this.formatDate(this.date);
19286     },
19287     
19288     fireKey: function(e)
19289     {
19290         if (!this.picker().isVisible()){
19291             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19292                 this.showPopup();
19293             }
19294             return;
19295         }
19296         
19297         var dateChanged = false,
19298         dir, day, month,
19299         newDate, newViewDate;
19300         
19301         switch(e.keyCode){
19302             case 27: // escape
19303                 this.hidePopup();
19304                 e.preventDefault();
19305                 break;
19306             case 37: // left
19307             case 39: // right
19308                 if (!this.keyboardNavigation) {
19309                     break;
19310                 }
19311                 dir = e.keyCode == 37 ? -1 : 1;
19312                 
19313                 if (e.ctrlKey){
19314                     newDate = this.moveYear(this.date, dir);
19315                     newViewDate = this.moveYear(this.viewDate, dir);
19316                 } else if (e.shiftKey){
19317                     newDate = this.moveMonth(this.date, dir);
19318                     newViewDate = this.moveMonth(this.viewDate, dir);
19319                 } else {
19320                     newDate = new Date(this.date);
19321                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19322                     newViewDate = new Date(this.viewDate);
19323                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19324                 }
19325                 if (this.dateWithinRange(newDate)){
19326                     this.date = newDate;
19327                     this.viewDate = newViewDate;
19328                     this.setValue(this.formatDate(this.date));
19329 //                    this.update();
19330                     e.preventDefault();
19331                     dateChanged = true;
19332                 }
19333                 break;
19334             case 38: // up
19335             case 40: // down
19336                 if (!this.keyboardNavigation) {
19337                     break;
19338                 }
19339                 dir = e.keyCode == 38 ? -1 : 1;
19340                 if (e.ctrlKey){
19341                     newDate = this.moveYear(this.date, dir);
19342                     newViewDate = this.moveYear(this.viewDate, dir);
19343                 } else if (e.shiftKey){
19344                     newDate = this.moveMonth(this.date, dir);
19345                     newViewDate = this.moveMonth(this.viewDate, dir);
19346                 } else {
19347                     newDate = new Date(this.date);
19348                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19349                     newViewDate = new Date(this.viewDate);
19350                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19351                 }
19352                 if (this.dateWithinRange(newDate)){
19353                     this.date = newDate;
19354                     this.viewDate = newViewDate;
19355                     this.setValue(this.formatDate(this.date));
19356 //                    this.update();
19357                     e.preventDefault();
19358                     dateChanged = true;
19359                 }
19360                 break;
19361             case 13: // enter
19362                 this.setValue(this.formatDate(this.date));
19363                 this.hidePopup();
19364                 e.preventDefault();
19365                 break;
19366             case 9: // tab
19367                 this.setValue(this.formatDate(this.date));
19368                 this.hidePopup();
19369                 break;
19370             case 16: // shift
19371             case 17: // ctrl
19372             case 18: // alt
19373                 break;
19374             default :
19375                 this.hidePopup();
19376                 
19377         }
19378     },
19379     
19380     
19381     onClick: function(e) 
19382     {
19383         e.stopPropagation();
19384         e.preventDefault();
19385         
19386         var target = e.getTarget();
19387         
19388         if(target.nodeName.toLowerCase() === 'i'){
19389             target = Roo.get(target).dom.parentNode;
19390         }
19391         
19392         var nodeName = target.nodeName;
19393         var className = target.className;
19394         var html = target.innerHTML;
19395         //Roo.log(nodeName);
19396         
19397         switch(nodeName.toLowerCase()) {
19398             case 'th':
19399                 switch(className) {
19400                     case 'switch':
19401                         this.showMode(1);
19402                         break;
19403                     case 'prev':
19404                     case 'next':
19405                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19406                         switch(this.viewMode){
19407                                 case 0:
19408                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19409                                         break;
19410                                 case 1:
19411                                 case 2:
19412                                         this.viewDate = this.moveYear(this.viewDate, dir);
19413                                         break;
19414                         }
19415                         this.fill();
19416                         break;
19417                     case 'today':
19418                         var date = new Date();
19419                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19420 //                        this.fill()
19421                         this.setValue(this.formatDate(this.date));
19422                         
19423                         this.hidePopup();
19424                         break;
19425                 }
19426                 break;
19427             case 'span':
19428                 if (className.indexOf('disabled') < 0) {
19429                     this.viewDate.setUTCDate(1);
19430                     if (className.indexOf('month') > -1) {
19431                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19432                     } else {
19433                         var year = parseInt(html, 10) || 0;
19434                         this.viewDate.setUTCFullYear(year);
19435                         
19436                     }
19437                     
19438                     if(this.singleMode){
19439                         this.setValue(this.formatDate(this.viewDate));
19440                         this.hidePopup();
19441                         return;
19442                     }
19443                     
19444                     this.showMode(-1);
19445                     this.fill();
19446                 }
19447                 break;
19448                 
19449             case 'td':
19450                 //Roo.log(className);
19451                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19452                     var day = parseInt(html, 10) || 1;
19453                     var year = this.viewDate.getUTCFullYear(),
19454                         month = this.viewDate.getUTCMonth();
19455
19456                     if (className.indexOf('old') > -1) {
19457                         if(month === 0 ){
19458                             month = 11;
19459                             year -= 1;
19460                         }else{
19461                             month -= 1;
19462                         }
19463                     } else if (className.indexOf('new') > -1) {
19464                         if (month == 11) {
19465                             month = 0;
19466                             year += 1;
19467                         } else {
19468                             month += 1;
19469                         }
19470                     }
19471                     //Roo.log([year,month,day]);
19472                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19473                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19474 //                    this.fill();
19475                     //Roo.log(this.formatDate(this.date));
19476                     this.setValue(this.formatDate(this.date));
19477                     this.hidePopup();
19478                 }
19479                 break;
19480         }
19481     },
19482     
19483     setStartDate: function(startDate)
19484     {
19485         this.startDate = startDate || -Infinity;
19486         if (this.startDate !== -Infinity) {
19487             this.startDate = this.parseDate(this.startDate);
19488         }
19489         this.update();
19490         this.updateNavArrows();
19491     },
19492
19493     setEndDate: function(endDate)
19494     {
19495         this.endDate = endDate || Infinity;
19496         if (this.endDate !== Infinity) {
19497             this.endDate = this.parseDate(this.endDate);
19498         }
19499         this.update();
19500         this.updateNavArrows();
19501     },
19502     
19503     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19504     {
19505         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19506         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19507             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19508         }
19509         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19510             return parseInt(d, 10);
19511         });
19512         this.update();
19513         this.updateNavArrows();
19514     },
19515     
19516     updateNavArrows: function() 
19517     {
19518         if(this.singleMode){
19519             return;
19520         }
19521         
19522         var d = new Date(this.viewDate),
19523         year = d.getUTCFullYear(),
19524         month = d.getUTCMonth();
19525         
19526         Roo.each(this.picker().select('.prev', true).elements, function(v){
19527             v.show();
19528             switch (this.viewMode) {
19529                 case 0:
19530
19531                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19532                         v.hide();
19533                     }
19534                     break;
19535                 case 1:
19536                 case 2:
19537                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19538                         v.hide();
19539                     }
19540                     break;
19541             }
19542         });
19543         
19544         Roo.each(this.picker().select('.next', true).elements, function(v){
19545             v.show();
19546             switch (this.viewMode) {
19547                 case 0:
19548
19549                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19550                         v.hide();
19551                     }
19552                     break;
19553                 case 1:
19554                 case 2:
19555                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19556                         v.hide();
19557                     }
19558                     break;
19559             }
19560         })
19561     },
19562     
19563     moveMonth: function(date, dir)
19564     {
19565         if (!dir) {
19566             return date;
19567         }
19568         var new_date = new Date(date.valueOf()),
19569         day = new_date.getUTCDate(),
19570         month = new_date.getUTCMonth(),
19571         mag = Math.abs(dir),
19572         new_month, test;
19573         dir = dir > 0 ? 1 : -1;
19574         if (mag == 1){
19575             test = dir == -1
19576             // If going back one month, make sure month is not current month
19577             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19578             ? function(){
19579                 return new_date.getUTCMonth() == month;
19580             }
19581             // If going forward one month, make sure month is as expected
19582             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19583             : function(){
19584                 return new_date.getUTCMonth() != new_month;
19585             };
19586             new_month = month + dir;
19587             new_date.setUTCMonth(new_month);
19588             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19589             if (new_month < 0 || new_month > 11) {
19590                 new_month = (new_month + 12) % 12;
19591             }
19592         } else {
19593             // For magnitudes >1, move one month at a time...
19594             for (var i=0; i<mag; i++) {
19595                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19596                 new_date = this.moveMonth(new_date, dir);
19597             }
19598             // ...then reset the day, keeping it in the new month
19599             new_month = new_date.getUTCMonth();
19600             new_date.setUTCDate(day);
19601             test = function(){
19602                 return new_month != new_date.getUTCMonth();
19603             };
19604         }
19605         // Common date-resetting loop -- if date is beyond end of month, make it
19606         // end of month
19607         while (test()){
19608             new_date.setUTCDate(--day);
19609             new_date.setUTCMonth(new_month);
19610         }
19611         return new_date;
19612     },
19613
19614     moveYear: function(date, dir)
19615     {
19616         return this.moveMonth(date, dir*12);
19617     },
19618
19619     dateWithinRange: function(date)
19620     {
19621         return date >= this.startDate && date <= this.endDate;
19622     },
19623
19624     
19625     remove: function() 
19626     {
19627         this.picker().remove();
19628     },
19629     
19630     validateValue : function(value)
19631     {
19632         if(this.getVisibilityEl().hasClass('hidden')){
19633             return true;
19634         }
19635         
19636         if(value.length < 1)  {
19637             if(this.allowBlank){
19638                 return true;
19639             }
19640             return false;
19641         }
19642         
19643         if(value.length < this.minLength){
19644             return false;
19645         }
19646         if(value.length > this.maxLength){
19647             return false;
19648         }
19649         if(this.vtype){
19650             var vt = Roo.form.VTypes;
19651             if(!vt[this.vtype](value, this)){
19652                 return false;
19653             }
19654         }
19655         if(typeof this.validator == "function"){
19656             var msg = this.validator(value);
19657             if(msg !== true){
19658                 return false;
19659             }
19660         }
19661         
19662         if(this.regex && !this.regex.test(value)){
19663             return false;
19664         }
19665         
19666         if(typeof(this.parseDate(value)) == 'undefined'){
19667             return false;
19668         }
19669         
19670         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19671             return false;
19672         }      
19673         
19674         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19675             return false;
19676         } 
19677         
19678         
19679         return true;
19680     },
19681     
19682     reset : function()
19683     {
19684         this.date = this.viewDate = '';
19685         
19686         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19687     }
19688    
19689 });
19690
19691 Roo.apply(Roo.bootstrap.DateField,  {
19692     
19693     head : {
19694         tag: 'thead',
19695         cn: [
19696         {
19697             tag: 'tr',
19698             cn: [
19699             {
19700                 tag: 'th',
19701                 cls: 'prev',
19702                 html: '<i class="fa fa-arrow-left"/>'
19703             },
19704             {
19705                 tag: 'th',
19706                 cls: 'switch',
19707                 colspan: '5'
19708             },
19709             {
19710                 tag: 'th',
19711                 cls: 'next',
19712                 html: '<i class="fa fa-arrow-right"/>'
19713             }
19714
19715             ]
19716         }
19717         ]
19718     },
19719     
19720     content : {
19721         tag: 'tbody',
19722         cn: [
19723         {
19724             tag: 'tr',
19725             cn: [
19726             {
19727                 tag: 'td',
19728                 colspan: '7'
19729             }
19730             ]
19731         }
19732         ]
19733     },
19734     
19735     footer : {
19736         tag: 'tfoot',
19737         cn: [
19738         {
19739             tag: 'tr',
19740             cn: [
19741             {
19742                 tag: 'th',
19743                 colspan: '7',
19744                 cls: 'today'
19745             }
19746                     
19747             ]
19748         }
19749         ]
19750     },
19751     
19752     dates:{
19753         en: {
19754             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19755             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19756             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19757             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19758             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19759             today: "Today"
19760         }
19761     },
19762     
19763     modes: [
19764     {
19765         clsName: 'days',
19766         navFnc: 'Month',
19767         navStep: 1
19768     },
19769     {
19770         clsName: 'months',
19771         navFnc: 'FullYear',
19772         navStep: 1
19773     },
19774     {
19775         clsName: 'years',
19776         navFnc: 'FullYear',
19777         navStep: 10
19778     }]
19779 });
19780
19781 Roo.apply(Roo.bootstrap.DateField,  {
19782   
19783     template : {
19784         tag: 'div',
19785         cls: 'datepicker dropdown-menu roo-dynamic',
19786         cn: [
19787         {
19788             tag: 'div',
19789             cls: 'datepicker-days',
19790             cn: [
19791             {
19792                 tag: 'table',
19793                 cls: 'table-condensed',
19794                 cn:[
19795                 Roo.bootstrap.DateField.head,
19796                 {
19797                     tag: 'tbody'
19798                 },
19799                 Roo.bootstrap.DateField.footer
19800                 ]
19801             }
19802             ]
19803         },
19804         {
19805             tag: 'div',
19806             cls: 'datepicker-months',
19807             cn: [
19808             {
19809                 tag: 'table',
19810                 cls: 'table-condensed',
19811                 cn:[
19812                 Roo.bootstrap.DateField.head,
19813                 Roo.bootstrap.DateField.content,
19814                 Roo.bootstrap.DateField.footer
19815                 ]
19816             }
19817             ]
19818         },
19819         {
19820             tag: 'div',
19821             cls: 'datepicker-years',
19822             cn: [
19823             {
19824                 tag: 'table',
19825                 cls: 'table-condensed',
19826                 cn:[
19827                 Roo.bootstrap.DateField.head,
19828                 Roo.bootstrap.DateField.content,
19829                 Roo.bootstrap.DateField.footer
19830                 ]
19831             }
19832             ]
19833         }
19834         ]
19835     }
19836 });
19837
19838  
19839
19840  /*
19841  * - LGPL
19842  *
19843  * TimeField
19844  * 
19845  */
19846
19847 /**
19848  * @class Roo.bootstrap.TimeField
19849  * @extends Roo.bootstrap.Input
19850  * Bootstrap DateField class
19851  * 
19852  * 
19853  * @constructor
19854  * Create a new TimeField
19855  * @param {Object} config The config object
19856  */
19857
19858 Roo.bootstrap.TimeField = function(config){
19859     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19860     this.addEvents({
19861             /**
19862              * @event show
19863              * Fires when this field show.
19864              * @param {Roo.bootstrap.DateField} thisthis
19865              * @param {Mixed} date The date value
19866              */
19867             show : true,
19868             /**
19869              * @event show
19870              * Fires when this field hide.
19871              * @param {Roo.bootstrap.DateField} this
19872              * @param {Mixed} date The date value
19873              */
19874             hide : true,
19875             /**
19876              * @event select
19877              * Fires when select a date.
19878              * @param {Roo.bootstrap.DateField} this
19879              * @param {Mixed} date The date value
19880              */
19881             select : true
19882         });
19883 };
19884
19885 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19886     
19887     /**
19888      * @cfg {String} format
19889      * The default time format string which can be overriden for localization support.  The format must be
19890      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19891      */
19892     format : "H:i",
19893        
19894     onRender: function(ct, position)
19895     {
19896         
19897         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19898                 
19899         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19900         
19901         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19902         
19903         this.pop = this.picker().select('>.datepicker-time',true).first();
19904         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19905         
19906         this.picker().on('mousedown', this.onMousedown, this);
19907         this.picker().on('click', this.onClick, this);
19908         
19909         this.picker().addClass('datepicker-dropdown');
19910     
19911         this.fillTime();
19912         this.update();
19913             
19914         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19915         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19916         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19917         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19918         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19919         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19920
19921     },
19922     
19923     fireKey: function(e){
19924         if (!this.picker().isVisible()){
19925             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19926                 this.show();
19927             }
19928             return;
19929         }
19930
19931         e.preventDefault();
19932         
19933         switch(e.keyCode){
19934             case 27: // escape
19935                 this.hide();
19936                 break;
19937             case 37: // left
19938             case 39: // right
19939                 this.onTogglePeriod();
19940                 break;
19941             case 38: // up
19942                 this.onIncrementMinutes();
19943                 break;
19944             case 40: // down
19945                 this.onDecrementMinutes();
19946                 break;
19947             case 13: // enter
19948             case 9: // tab
19949                 this.setTime();
19950                 break;
19951         }
19952     },
19953     
19954     onClick: function(e) {
19955         e.stopPropagation();
19956         e.preventDefault();
19957     },
19958     
19959     picker : function()
19960     {
19961         return this.el.select('.datepicker', true).first();
19962     },
19963     
19964     fillTime: function()
19965     {    
19966         var time = this.pop.select('tbody', true).first();
19967         
19968         time.dom.innerHTML = '';
19969         
19970         time.createChild({
19971             tag: 'tr',
19972             cn: [
19973                 {
19974                     tag: 'td',
19975                     cn: [
19976                         {
19977                             tag: 'a',
19978                             href: '#',
19979                             cls: 'btn',
19980                             cn: [
19981                                 {
19982                                     tag: 'span',
19983                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19984                                 }
19985                             ]
19986                         } 
19987                     ]
19988                 },
19989                 {
19990                     tag: 'td',
19991                     cls: 'separator'
19992                 },
19993                 {
19994                     tag: 'td',
19995                     cn: [
19996                         {
19997                             tag: 'a',
19998                             href: '#',
19999                             cls: 'btn',
20000                             cn: [
20001                                 {
20002                                     tag: 'span',
20003                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
20004                                 }
20005                             ]
20006                         }
20007                     ]
20008                 },
20009                 {
20010                     tag: 'td',
20011                     cls: 'separator'
20012                 }
20013             ]
20014         });
20015         
20016         time.createChild({
20017             tag: 'tr',
20018             cn: [
20019                 {
20020                     tag: 'td',
20021                     cn: [
20022                         {
20023                             tag: 'span',
20024                             cls: 'timepicker-hour',
20025                             html: '00'
20026                         }  
20027                     ]
20028                 },
20029                 {
20030                     tag: 'td',
20031                     cls: 'separator',
20032                     html: ':'
20033                 },
20034                 {
20035                     tag: 'td',
20036                     cn: [
20037                         {
20038                             tag: 'span',
20039                             cls: 'timepicker-minute',
20040                             html: '00'
20041                         }  
20042                     ]
20043                 },
20044                 {
20045                     tag: 'td',
20046                     cls: 'separator'
20047                 },
20048                 {
20049                     tag: 'td',
20050                     cn: [
20051                         {
20052                             tag: 'button',
20053                             type: 'button',
20054                             cls: 'btn btn-primary period',
20055                             html: 'AM'
20056                             
20057                         }
20058                     ]
20059                 }
20060             ]
20061         });
20062         
20063         time.createChild({
20064             tag: 'tr',
20065             cn: [
20066                 {
20067                     tag: 'td',
20068                     cn: [
20069                         {
20070                             tag: 'a',
20071                             href: '#',
20072                             cls: 'btn',
20073                             cn: [
20074                                 {
20075                                     tag: 'span',
20076                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20077                                 }
20078                             ]
20079                         }
20080                     ]
20081                 },
20082                 {
20083                     tag: 'td',
20084                     cls: 'separator'
20085                 },
20086                 {
20087                     tag: 'td',
20088                     cn: [
20089                         {
20090                             tag: 'a',
20091                             href: '#',
20092                             cls: 'btn',
20093                             cn: [
20094                                 {
20095                                     tag: 'span',
20096                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20097                                 }
20098                             ]
20099                         }
20100                     ]
20101                 },
20102                 {
20103                     tag: 'td',
20104                     cls: 'separator'
20105                 }
20106             ]
20107         });
20108         
20109     },
20110     
20111     update: function()
20112     {
20113         
20114         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20115         
20116         this.fill();
20117     },
20118     
20119     fill: function() 
20120     {
20121         var hours = this.time.getHours();
20122         var minutes = this.time.getMinutes();
20123         var period = 'AM';
20124         
20125         if(hours > 11){
20126             period = 'PM';
20127         }
20128         
20129         if(hours == 0){
20130             hours = 12;
20131         }
20132         
20133         
20134         if(hours > 12){
20135             hours = hours - 12;
20136         }
20137         
20138         if(hours < 10){
20139             hours = '0' + hours;
20140         }
20141         
20142         if(minutes < 10){
20143             minutes = '0' + minutes;
20144         }
20145         
20146         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20147         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20148         this.pop.select('button', true).first().dom.innerHTML = period;
20149         
20150     },
20151     
20152     place: function()
20153     {   
20154         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20155         
20156         var cls = ['bottom'];
20157         
20158         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20159             cls.pop();
20160             cls.push('top');
20161         }
20162         
20163         cls.push('right');
20164         
20165         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20166             cls.pop();
20167             cls.push('left');
20168         }
20169         
20170         this.picker().addClass(cls.join('-'));
20171         
20172         var _this = this;
20173         
20174         Roo.each(cls, function(c){
20175             if(c == 'bottom'){
20176                 _this.picker().setTop(_this.inputEl().getHeight());
20177                 return;
20178             }
20179             if(c == 'top'){
20180                 _this.picker().setTop(0 - _this.picker().getHeight());
20181                 return;
20182             }
20183             
20184             if(c == 'left'){
20185                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20186                 return;
20187             }
20188             if(c == 'right'){
20189                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20190                 return;
20191             }
20192         });
20193         
20194     },
20195   
20196     onFocus : function()
20197     {
20198         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20199         this.show();
20200     },
20201     
20202     onBlur : function()
20203     {
20204         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20205         this.hide();
20206     },
20207     
20208     show : function()
20209     {
20210         this.picker().show();
20211         this.pop.show();
20212         this.update();
20213         this.place();
20214         
20215         this.fireEvent('show', this, this.date);
20216     },
20217     
20218     hide : function()
20219     {
20220         this.picker().hide();
20221         this.pop.hide();
20222         
20223         this.fireEvent('hide', this, this.date);
20224     },
20225     
20226     setTime : function()
20227     {
20228         this.hide();
20229         this.setValue(this.time.format(this.format));
20230         
20231         this.fireEvent('select', this, this.date);
20232         
20233         
20234     },
20235     
20236     onMousedown: function(e){
20237         e.stopPropagation();
20238         e.preventDefault();
20239     },
20240     
20241     onIncrementHours: function()
20242     {
20243         Roo.log('onIncrementHours');
20244         this.time = this.time.add(Date.HOUR, 1);
20245         this.update();
20246         
20247     },
20248     
20249     onDecrementHours: function()
20250     {
20251         Roo.log('onDecrementHours');
20252         this.time = this.time.add(Date.HOUR, -1);
20253         this.update();
20254     },
20255     
20256     onIncrementMinutes: function()
20257     {
20258         Roo.log('onIncrementMinutes');
20259         this.time = this.time.add(Date.MINUTE, 1);
20260         this.update();
20261     },
20262     
20263     onDecrementMinutes: function()
20264     {
20265         Roo.log('onDecrementMinutes');
20266         this.time = this.time.add(Date.MINUTE, -1);
20267         this.update();
20268     },
20269     
20270     onTogglePeriod: function()
20271     {
20272         Roo.log('onTogglePeriod');
20273         this.time = this.time.add(Date.HOUR, 12);
20274         this.update();
20275     }
20276     
20277    
20278 });
20279
20280 Roo.apply(Roo.bootstrap.TimeField,  {
20281     
20282     content : {
20283         tag: 'tbody',
20284         cn: [
20285             {
20286                 tag: 'tr',
20287                 cn: [
20288                 {
20289                     tag: 'td',
20290                     colspan: '7'
20291                 }
20292                 ]
20293             }
20294         ]
20295     },
20296     
20297     footer : {
20298         tag: 'tfoot',
20299         cn: [
20300             {
20301                 tag: 'tr',
20302                 cn: [
20303                 {
20304                     tag: 'th',
20305                     colspan: '7',
20306                     cls: '',
20307                     cn: [
20308                         {
20309                             tag: 'button',
20310                             cls: 'btn btn-info ok',
20311                             html: 'OK'
20312                         }
20313                     ]
20314                 }
20315
20316                 ]
20317             }
20318         ]
20319     }
20320 });
20321
20322 Roo.apply(Roo.bootstrap.TimeField,  {
20323   
20324     template : {
20325         tag: 'div',
20326         cls: 'datepicker dropdown-menu',
20327         cn: [
20328             {
20329                 tag: 'div',
20330                 cls: 'datepicker-time',
20331                 cn: [
20332                 {
20333                     tag: 'table',
20334                     cls: 'table-condensed',
20335                     cn:[
20336                     Roo.bootstrap.TimeField.content,
20337                     Roo.bootstrap.TimeField.footer
20338                     ]
20339                 }
20340                 ]
20341             }
20342         ]
20343     }
20344 });
20345
20346  
20347
20348  /*
20349  * - LGPL
20350  *
20351  * MonthField
20352  * 
20353  */
20354
20355 /**
20356  * @class Roo.bootstrap.MonthField
20357  * @extends Roo.bootstrap.Input
20358  * Bootstrap MonthField class
20359  * 
20360  * @cfg {String} language default en
20361  * 
20362  * @constructor
20363  * Create a new MonthField
20364  * @param {Object} config The config object
20365  */
20366
20367 Roo.bootstrap.MonthField = function(config){
20368     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20369     
20370     this.addEvents({
20371         /**
20372          * @event show
20373          * Fires when this field show.
20374          * @param {Roo.bootstrap.MonthField} this
20375          * @param {Mixed} date The date value
20376          */
20377         show : true,
20378         /**
20379          * @event show
20380          * Fires when this field hide.
20381          * @param {Roo.bootstrap.MonthField} this
20382          * @param {Mixed} date The date value
20383          */
20384         hide : true,
20385         /**
20386          * @event select
20387          * Fires when select a date.
20388          * @param {Roo.bootstrap.MonthField} this
20389          * @param {String} oldvalue The old value
20390          * @param {String} newvalue The new value
20391          */
20392         select : true
20393     });
20394 };
20395
20396 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20397     
20398     onRender: function(ct, position)
20399     {
20400         
20401         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20402         
20403         this.language = this.language || 'en';
20404         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20405         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20406         
20407         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20408         this.isInline = false;
20409         this.isInput = true;
20410         this.component = this.el.select('.add-on', true).first() || false;
20411         this.component = (this.component && this.component.length === 0) ? false : this.component;
20412         this.hasInput = this.component && this.inputEL().length;
20413         
20414         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20415         
20416         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20417         
20418         this.picker().on('mousedown', this.onMousedown, this);
20419         this.picker().on('click', this.onClick, this);
20420         
20421         this.picker().addClass('datepicker-dropdown');
20422         
20423         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20424             v.setStyle('width', '189px');
20425         });
20426         
20427         this.fillMonths();
20428         
20429         this.update();
20430         
20431         if(this.isInline) {
20432             this.show();
20433         }
20434         
20435     },
20436     
20437     setValue: function(v, suppressEvent)
20438     {   
20439         var o = this.getValue();
20440         
20441         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20442         
20443         this.update();
20444
20445         if(suppressEvent !== true){
20446             this.fireEvent('select', this, o, v);
20447         }
20448         
20449     },
20450     
20451     getValue: function()
20452     {
20453         return this.value;
20454     },
20455     
20456     onClick: function(e) 
20457     {
20458         e.stopPropagation();
20459         e.preventDefault();
20460         
20461         var target = e.getTarget();
20462         
20463         if(target.nodeName.toLowerCase() === 'i'){
20464             target = Roo.get(target).dom.parentNode;
20465         }
20466         
20467         var nodeName = target.nodeName;
20468         var className = target.className;
20469         var html = target.innerHTML;
20470         
20471         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20472             return;
20473         }
20474         
20475         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20476         
20477         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20478         
20479         this.hide();
20480                         
20481     },
20482     
20483     picker : function()
20484     {
20485         return this.pickerEl;
20486     },
20487     
20488     fillMonths: function()
20489     {    
20490         var i = 0;
20491         var months = this.picker().select('>.datepicker-months td', true).first();
20492         
20493         months.dom.innerHTML = '';
20494         
20495         while (i < 12) {
20496             var month = {
20497                 tag: 'span',
20498                 cls: 'month',
20499                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20500             };
20501             
20502             months.createChild(month);
20503         }
20504         
20505     },
20506     
20507     update: function()
20508     {
20509         var _this = this;
20510         
20511         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20512             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20513         }
20514         
20515         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20516             e.removeClass('active');
20517             
20518             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20519                 e.addClass('active');
20520             }
20521         })
20522     },
20523     
20524     place: function()
20525     {
20526         if(this.isInline) {
20527             return;
20528         }
20529         
20530         this.picker().removeClass(['bottom', 'top']);
20531         
20532         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20533             /*
20534              * place to the top of element!
20535              *
20536              */
20537             
20538             this.picker().addClass('top');
20539             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20540             
20541             return;
20542         }
20543         
20544         this.picker().addClass('bottom');
20545         
20546         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20547     },
20548     
20549     onFocus : function()
20550     {
20551         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20552         this.show();
20553     },
20554     
20555     onBlur : function()
20556     {
20557         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20558         
20559         var d = this.inputEl().getValue();
20560         
20561         this.setValue(d);
20562                 
20563         this.hide();
20564     },
20565     
20566     show : function()
20567     {
20568         this.picker().show();
20569         this.picker().select('>.datepicker-months', true).first().show();
20570         this.update();
20571         this.place();
20572         
20573         this.fireEvent('show', this, this.date);
20574     },
20575     
20576     hide : function()
20577     {
20578         if(this.isInline) {
20579             return;
20580         }
20581         this.picker().hide();
20582         this.fireEvent('hide', this, this.date);
20583         
20584     },
20585     
20586     onMousedown: function(e)
20587     {
20588         e.stopPropagation();
20589         e.preventDefault();
20590     },
20591     
20592     keyup: function(e)
20593     {
20594         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20595         this.update();
20596     },
20597
20598     fireKey: function(e)
20599     {
20600         if (!this.picker().isVisible()){
20601             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20602                 this.show();
20603             }
20604             return;
20605         }
20606         
20607         var dir;
20608         
20609         switch(e.keyCode){
20610             case 27: // escape
20611                 this.hide();
20612                 e.preventDefault();
20613                 break;
20614             case 37: // left
20615             case 39: // right
20616                 dir = e.keyCode == 37 ? -1 : 1;
20617                 
20618                 this.vIndex = this.vIndex + dir;
20619                 
20620                 if(this.vIndex < 0){
20621                     this.vIndex = 0;
20622                 }
20623                 
20624                 if(this.vIndex > 11){
20625                     this.vIndex = 11;
20626                 }
20627                 
20628                 if(isNaN(this.vIndex)){
20629                     this.vIndex = 0;
20630                 }
20631                 
20632                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20633                 
20634                 break;
20635             case 38: // up
20636             case 40: // down
20637                 
20638                 dir = e.keyCode == 38 ? -1 : 1;
20639                 
20640                 this.vIndex = this.vIndex + dir * 4;
20641                 
20642                 if(this.vIndex < 0){
20643                     this.vIndex = 0;
20644                 }
20645                 
20646                 if(this.vIndex > 11){
20647                     this.vIndex = 11;
20648                 }
20649                 
20650                 if(isNaN(this.vIndex)){
20651                     this.vIndex = 0;
20652                 }
20653                 
20654                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20655                 break;
20656                 
20657             case 13: // enter
20658                 
20659                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20660                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20661                 }
20662                 
20663                 this.hide();
20664                 e.preventDefault();
20665                 break;
20666             case 9: // tab
20667                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20668                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20669                 }
20670                 this.hide();
20671                 break;
20672             case 16: // shift
20673             case 17: // ctrl
20674             case 18: // alt
20675                 break;
20676             default :
20677                 this.hide();
20678                 
20679         }
20680     },
20681     
20682     remove: function() 
20683     {
20684         this.picker().remove();
20685     }
20686    
20687 });
20688
20689 Roo.apply(Roo.bootstrap.MonthField,  {
20690     
20691     content : {
20692         tag: 'tbody',
20693         cn: [
20694         {
20695             tag: 'tr',
20696             cn: [
20697             {
20698                 tag: 'td',
20699                 colspan: '7'
20700             }
20701             ]
20702         }
20703         ]
20704     },
20705     
20706     dates:{
20707         en: {
20708             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20709             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20710         }
20711     }
20712 });
20713
20714 Roo.apply(Roo.bootstrap.MonthField,  {
20715   
20716     template : {
20717         tag: 'div',
20718         cls: 'datepicker dropdown-menu roo-dynamic',
20719         cn: [
20720             {
20721                 tag: 'div',
20722                 cls: 'datepicker-months',
20723                 cn: [
20724                 {
20725                     tag: 'table',
20726                     cls: 'table-condensed',
20727                     cn:[
20728                         Roo.bootstrap.DateField.content
20729                     ]
20730                 }
20731                 ]
20732             }
20733         ]
20734     }
20735 });
20736
20737  
20738
20739  
20740  /*
20741  * - LGPL
20742  *
20743  * CheckBox
20744  * 
20745  */
20746
20747 /**
20748  * @class Roo.bootstrap.CheckBox
20749  * @extends Roo.bootstrap.Input
20750  * Bootstrap CheckBox class
20751  * 
20752  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20753  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20754  * @cfg {String} boxLabel The text that appears beside the checkbox
20755  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20756  * @cfg {Boolean} checked initnal the element
20757  * @cfg {Boolean} inline inline the element (default false)
20758  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20759  * @cfg {String} tooltip label tooltip
20760  * 
20761  * @constructor
20762  * Create a new CheckBox
20763  * @param {Object} config The config object
20764  */
20765
20766 Roo.bootstrap.CheckBox = function(config){
20767     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20768    
20769     this.addEvents({
20770         /**
20771         * @event check
20772         * Fires when the element is checked or unchecked.
20773         * @param {Roo.bootstrap.CheckBox} this This input
20774         * @param {Boolean} checked The new checked value
20775         */
20776        check : true,
20777        /**
20778         * @event click
20779         * Fires when the element is click.
20780         * @param {Roo.bootstrap.CheckBox} this This input
20781         */
20782        click : true
20783     });
20784     
20785 };
20786
20787 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20788   
20789     inputType: 'checkbox',
20790     inputValue: 1,
20791     valueOff: 0,
20792     boxLabel: false,
20793     checked: false,
20794     weight : false,
20795     inline: false,
20796     tooltip : '',
20797     
20798     getAutoCreate : function()
20799     {
20800         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20801         
20802         var id = Roo.id();
20803         
20804         var cfg = {};
20805         
20806         cfg.cls = 'form-group ' + this.inputType; //input-group
20807         
20808         if(this.inline){
20809             cfg.cls += ' ' + this.inputType + '-inline';
20810         }
20811         
20812         var input =  {
20813             tag: 'input',
20814             id : id,
20815             type : this.inputType,
20816             value : this.inputValue,
20817             cls : 'roo-' + this.inputType, //'form-box',
20818             placeholder : this.placeholder || ''
20819             
20820         };
20821         
20822         if(this.inputType != 'radio'){
20823             var hidden =  {
20824                 tag: 'input',
20825                 type : 'hidden',
20826                 cls : 'roo-hidden-value',
20827                 value : this.checked ? this.inputValue : this.valueOff
20828             };
20829         }
20830         
20831             
20832         if (this.weight) { // Validity check?
20833             cfg.cls += " " + this.inputType + "-" + this.weight;
20834         }
20835         
20836         if (this.disabled) {
20837             input.disabled=true;
20838         }
20839         
20840         if(this.checked){
20841             input.checked = this.checked;
20842         }
20843         
20844         if (this.name) {
20845             
20846             input.name = this.name;
20847             
20848             if(this.inputType != 'radio'){
20849                 hidden.name = this.name;
20850                 input.name = '_hidden_' + this.name;
20851             }
20852         }
20853         
20854         if (this.size) {
20855             input.cls += ' input-' + this.size;
20856         }
20857         
20858         var settings=this;
20859         
20860         ['xs','sm','md','lg'].map(function(size){
20861             if (settings[size]) {
20862                 cfg.cls += ' col-' + size + '-' + settings[size];
20863             }
20864         });
20865         
20866         var inputblock = input;
20867          
20868         if (this.before || this.after) {
20869             
20870             inputblock = {
20871                 cls : 'input-group',
20872                 cn :  [] 
20873             };
20874             
20875             if (this.before) {
20876                 inputblock.cn.push({
20877                     tag :'span',
20878                     cls : 'input-group-addon',
20879                     html : this.before
20880                 });
20881             }
20882             
20883             inputblock.cn.push(input);
20884             
20885             if(this.inputType != 'radio'){
20886                 inputblock.cn.push(hidden);
20887             }
20888             
20889             if (this.after) {
20890                 inputblock.cn.push({
20891                     tag :'span',
20892                     cls : 'input-group-addon',
20893                     html : this.after
20894                 });
20895             }
20896             
20897         }
20898         
20899         if (align ==='left' && this.fieldLabel.length) {
20900 //                Roo.log("left and has label");
20901             cfg.cn = [
20902                 {
20903                     tag: 'label',
20904                     'for' :  id,
20905                     cls : 'control-label',
20906                     html : this.fieldLabel
20907                 },
20908                 {
20909                     cls : "", 
20910                     cn: [
20911                         inputblock
20912                     ]
20913                 }
20914             ];
20915             
20916             if(this.labelWidth > 12){
20917                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20918             }
20919             
20920             if(this.labelWidth < 13 && this.labelmd == 0){
20921                 this.labelmd = this.labelWidth;
20922             }
20923             
20924             if(this.labellg > 0){
20925                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20926                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20927             }
20928             
20929             if(this.labelmd > 0){
20930                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20931                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20932             }
20933             
20934             if(this.labelsm > 0){
20935                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20936                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20937             }
20938             
20939             if(this.labelxs > 0){
20940                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20941                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20942             }
20943             
20944         } else if ( this.fieldLabel.length) {
20945 //                Roo.log(" label");
20946                 cfg.cn = [
20947                    
20948                     {
20949                         tag: this.boxLabel ? 'span' : 'label',
20950                         'for': id,
20951                         cls: 'control-label box-input-label',
20952                         //cls : 'input-group-addon',
20953                         html : this.fieldLabel
20954                     },
20955                     
20956                     inputblock
20957                     
20958                 ];
20959
20960         } else {
20961             
20962 //                Roo.log(" no label && no align");
20963                 cfg.cn = [  inputblock ] ;
20964                 
20965                 
20966         }
20967         
20968         if(this.boxLabel){
20969              var boxLabelCfg = {
20970                 tag: 'label',
20971                 //'for': id, // box label is handled by onclick - so no for...
20972                 cls: 'box-label',
20973                 html: this.boxLabel
20974             };
20975             
20976             if(this.tooltip){
20977                 boxLabelCfg.tooltip = this.tooltip;
20978             }
20979              
20980             cfg.cn.push(boxLabelCfg);
20981         }
20982         
20983         if(this.inputType != 'radio'){
20984             cfg.cn.push(hidden);
20985         }
20986         
20987         return cfg;
20988         
20989     },
20990     
20991     /**
20992      * return the real input element.
20993      */
20994     inputEl: function ()
20995     {
20996         return this.el.select('input.roo-' + this.inputType,true).first();
20997     },
20998     hiddenEl: function ()
20999     {
21000         return this.el.select('input.roo-hidden-value',true).first();
21001     },
21002     
21003     labelEl: function()
21004     {
21005         return this.el.select('label.control-label',true).first();
21006     },
21007     /* depricated... */
21008     
21009     label: function()
21010     {
21011         return this.labelEl();
21012     },
21013     
21014     boxLabelEl: function()
21015     {
21016         return this.el.select('label.box-label',true).first();
21017     },
21018     
21019     initEvents : function()
21020     {
21021 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21022         
21023         this.inputEl().on('click', this.onClick,  this);
21024         
21025         if (this.boxLabel) { 
21026             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
21027         }
21028         
21029         this.startValue = this.getValue();
21030         
21031         if(this.groupId){
21032             Roo.bootstrap.CheckBox.register(this);
21033         }
21034     },
21035     
21036     onClick : function(e)
21037     {   
21038         if(this.fireEvent('click', this, e) !== false){
21039             this.setChecked(!this.checked);
21040         }
21041         
21042     },
21043     
21044     setChecked : function(state,suppressEvent)
21045     {
21046         this.startValue = this.getValue();
21047
21048         if(this.inputType == 'radio'){
21049             
21050             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21051                 e.dom.checked = false;
21052             });
21053             
21054             this.inputEl().dom.checked = true;
21055             
21056             this.inputEl().dom.value = this.inputValue;
21057             
21058             if(suppressEvent !== true){
21059                 this.fireEvent('check', this, true);
21060             }
21061             
21062             this.validate();
21063             
21064             return;
21065         }
21066         
21067         this.checked = state;
21068         
21069         this.inputEl().dom.checked = state;
21070         
21071         
21072         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21073         
21074         if(suppressEvent !== true){
21075             this.fireEvent('check', this, state);
21076         }
21077         
21078         this.validate();
21079     },
21080     
21081     getValue : function()
21082     {
21083         if(this.inputType == 'radio'){
21084             return this.getGroupValue();
21085         }
21086         
21087         return this.hiddenEl().dom.value;
21088         
21089     },
21090     
21091     getGroupValue : function()
21092     {
21093         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21094             return '';
21095         }
21096         
21097         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21098     },
21099     
21100     setValue : function(v,suppressEvent)
21101     {
21102         if(this.inputType == 'radio'){
21103             this.setGroupValue(v, suppressEvent);
21104             return;
21105         }
21106         
21107         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21108         
21109         this.validate();
21110     },
21111     
21112     setGroupValue : function(v, suppressEvent)
21113     {
21114         this.startValue = this.getValue();
21115         
21116         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21117             e.dom.checked = false;
21118             
21119             if(e.dom.value == v){
21120                 e.dom.checked = true;
21121             }
21122         });
21123         
21124         if(suppressEvent !== true){
21125             this.fireEvent('check', this, true);
21126         }
21127
21128         this.validate();
21129         
21130         return;
21131     },
21132     
21133     validate : function()
21134     {
21135         if(this.getVisibilityEl().hasClass('hidden')){
21136             return true;
21137         }
21138         
21139         if(
21140                 this.disabled || 
21141                 (this.inputType == 'radio' && this.validateRadio()) ||
21142                 (this.inputType == 'checkbox' && this.validateCheckbox())
21143         ){
21144             this.markValid();
21145             return true;
21146         }
21147         
21148         this.markInvalid();
21149         return false;
21150     },
21151     
21152     validateRadio : function()
21153     {
21154         if(this.getVisibilityEl().hasClass('hidden')){
21155             return true;
21156         }
21157         
21158         if(this.allowBlank){
21159             return true;
21160         }
21161         
21162         var valid = false;
21163         
21164         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21165             if(!e.dom.checked){
21166                 return;
21167             }
21168             
21169             valid = true;
21170             
21171             return false;
21172         });
21173         
21174         return valid;
21175     },
21176     
21177     validateCheckbox : function()
21178     {
21179         if(!this.groupId){
21180             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21181             //return (this.getValue() == this.inputValue) ? true : false;
21182         }
21183         
21184         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21185         
21186         if(!group){
21187             return false;
21188         }
21189         
21190         var r = false;
21191         
21192         for(var i in group){
21193             if(group[i].el.isVisible(true)){
21194                 r = false;
21195                 break;
21196             }
21197             
21198             r = true;
21199         }
21200         
21201         for(var i in group){
21202             if(r){
21203                 break;
21204             }
21205             
21206             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21207         }
21208         
21209         return r;
21210     },
21211     
21212     /**
21213      * Mark this field as valid
21214      */
21215     markValid : function()
21216     {
21217         var _this = this;
21218         
21219         this.fireEvent('valid', this);
21220         
21221         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21222         
21223         if(this.groupId){
21224             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21225         }
21226         
21227         if(label){
21228             label.markValid();
21229         }
21230
21231         if(this.inputType == 'radio'){
21232             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21233                 var fg = e.findParent('.form-group', false, true);
21234                 if (Roo.bootstrap.version == 3) {
21235                     fg.removeClass([_this.invalidClass, _this.validClass]);
21236                     fg.addClass(_this.validClass);
21237                 } else {
21238                     fg.removeClass(['is-valid', 'is-invalid']);
21239                     fg.addClass('is-valid');
21240                 }
21241             });
21242             
21243             return;
21244         }
21245
21246         if(!this.groupId){
21247             var fg = this.el.findParent('.form-group', false, true);
21248             if (Roo.bootstrap.version == 3) {
21249                 fg.removeClass([this.invalidClass, this.validClass]);
21250                 fg.addClass(this.validClass);
21251             } else {
21252                 fg.removeClass(['is-valid', 'is-invalid']);
21253                 fg.addClass('is-valid');
21254             }
21255             return;
21256         }
21257         
21258         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21259         
21260         if(!group){
21261             return;
21262         }
21263         
21264         for(var i in group){
21265             var fg = group[i].el.findParent('.form-group', false, true);
21266             if (Roo.bootstrap.version == 3) {
21267                 fg.removeClass([this.invalidClass, this.validClass]);
21268                 fg.addClass(this.validClass);
21269             } else {
21270                 fg.removeClass(['is-valid', 'is-invalid']);
21271                 fg.addClass('is-valid');
21272             }
21273         }
21274     },
21275     
21276      /**
21277      * Mark this field as invalid
21278      * @param {String} msg The validation message
21279      */
21280     markInvalid : function(msg)
21281     {
21282         if(this.allowBlank){
21283             return;
21284         }
21285         
21286         var _this = this;
21287         
21288         this.fireEvent('invalid', this, msg);
21289         
21290         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21291         
21292         if(this.groupId){
21293             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21294         }
21295         
21296         if(label){
21297             label.markInvalid();
21298         }
21299             
21300         if(this.inputType == 'radio'){
21301             
21302             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21303                 var fg = e.findParent('.form-group', false, true);
21304                 if (Roo.bootstrap.version == 3) {
21305                     fg.removeClass([_this.invalidClass, _this.validClass]);
21306                     fg.addClass(_this.invalidClass);
21307                 } else {
21308                     fg.removeClass(['is-invalid', 'is-valid']);
21309                     fg.addClass('is-invalid');
21310                 }
21311             });
21312             
21313             return;
21314         }
21315         
21316         if(!this.groupId){
21317             var fg = this.el.findParent('.form-group', false, true);
21318             if (Roo.bootstrap.version == 3) {
21319                 fg.removeClass([_this.invalidClass, _this.validClass]);
21320                 fg.addClass(_this.invalidClass);
21321             } else {
21322                 fg.removeClass(['is-invalid', 'is-valid']);
21323                 fg.addClass('is-invalid');
21324             }
21325             return;
21326         }
21327         
21328         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21329         
21330         if(!group){
21331             return;
21332         }
21333         
21334         for(var i in group){
21335             var fg = group[i].el.findParent('.form-group', false, true);
21336             if (Roo.bootstrap.version == 3) {
21337                 fg.removeClass([_this.invalidClass, _this.validClass]);
21338                 fg.addClass(_this.invalidClass);
21339             } else {
21340                 fg.removeClass(['is-invalid', 'is-valid']);
21341                 fg.addClass('is-invalid');
21342             }
21343         }
21344         
21345     },
21346     
21347     clearInvalid : function()
21348     {
21349         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21350         
21351         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21352         
21353         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21354         
21355         if (label && label.iconEl) {
21356             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21357             label.iconEl.removeClass(['is-invalid', 'is-valid']);
21358         }
21359     },
21360     
21361     disable : function()
21362     {
21363         if(this.inputType != 'radio'){
21364             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21365             return;
21366         }
21367         
21368         var _this = this;
21369         
21370         if(this.rendered){
21371             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21372                 _this.getActionEl().addClass(this.disabledClass);
21373                 e.dom.disabled = true;
21374             });
21375         }
21376         
21377         this.disabled = true;
21378         this.fireEvent("disable", this);
21379         return this;
21380     },
21381
21382     enable : function()
21383     {
21384         if(this.inputType != 'radio'){
21385             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21386             return;
21387         }
21388         
21389         var _this = this;
21390         
21391         if(this.rendered){
21392             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21393                 _this.getActionEl().removeClass(this.disabledClass);
21394                 e.dom.disabled = false;
21395             });
21396         }
21397         
21398         this.disabled = false;
21399         this.fireEvent("enable", this);
21400         return this;
21401     },
21402     
21403     setBoxLabel : function(v)
21404     {
21405         this.boxLabel = v;
21406         
21407         if(this.rendered){
21408             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21409         }
21410     }
21411
21412 });
21413
21414 Roo.apply(Roo.bootstrap.CheckBox, {
21415     
21416     groups: {},
21417     
21418      /**
21419     * register a CheckBox Group
21420     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21421     */
21422     register : function(checkbox)
21423     {
21424         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21425             this.groups[checkbox.groupId] = {};
21426         }
21427         
21428         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21429             return;
21430         }
21431         
21432         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21433         
21434     },
21435     /**
21436     * fetch a CheckBox Group based on the group ID
21437     * @param {string} the group ID
21438     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21439     */
21440     get: function(groupId) {
21441         if (typeof(this.groups[groupId]) == 'undefined') {
21442             return false;
21443         }
21444         
21445         return this.groups[groupId] ;
21446     }
21447     
21448     
21449 });
21450 /*
21451  * - LGPL
21452  *
21453  * RadioItem
21454  * 
21455  */
21456
21457 /**
21458  * @class Roo.bootstrap.Radio
21459  * @extends Roo.bootstrap.Component
21460  * Bootstrap Radio class
21461  * @cfg {String} boxLabel - the label associated
21462  * @cfg {String} value - the value of radio
21463  * 
21464  * @constructor
21465  * Create a new Radio
21466  * @param {Object} config The config object
21467  */
21468 Roo.bootstrap.Radio = function(config){
21469     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21470     
21471 };
21472
21473 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21474     
21475     boxLabel : '',
21476     
21477     value : '',
21478     
21479     getAutoCreate : function()
21480     {
21481         var cfg = {
21482             tag : 'div',
21483             cls : 'form-group radio',
21484             cn : [
21485                 {
21486                     tag : 'label',
21487                     cls : 'box-label',
21488                     html : this.boxLabel
21489                 }
21490             ]
21491         };
21492         
21493         return cfg;
21494     },
21495     
21496     initEvents : function() 
21497     {
21498         this.parent().register(this);
21499         
21500         this.el.on('click', this.onClick, this);
21501         
21502     },
21503     
21504     onClick : function(e)
21505     {
21506         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21507             this.setChecked(true);
21508         }
21509     },
21510     
21511     setChecked : function(state, suppressEvent)
21512     {
21513         this.parent().setValue(this.value, suppressEvent);
21514         
21515     },
21516     
21517     setBoxLabel : function(v)
21518     {
21519         this.boxLabel = v;
21520         
21521         if(this.rendered){
21522             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21523         }
21524     }
21525     
21526 });
21527  
21528
21529  /*
21530  * - LGPL
21531  *
21532  * Input
21533  * 
21534  */
21535
21536 /**
21537  * @class Roo.bootstrap.SecurePass
21538  * @extends Roo.bootstrap.Input
21539  * Bootstrap SecurePass class
21540  *
21541  * 
21542  * @constructor
21543  * Create a new SecurePass
21544  * @param {Object} config The config object
21545  */
21546  
21547 Roo.bootstrap.SecurePass = function (config) {
21548     // these go here, so the translation tool can replace them..
21549     this.errors = {
21550         PwdEmpty: "Please type a password, and then retype it to confirm.",
21551         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21552         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21553         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21554         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21555         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21556         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21557         TooWeak: "Your password is Too Weak."
21558     },
21559     this.meterLabel = "Password strength:";
21560     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21561     this.meterClass = [
21562         "roo-password-meter-tooweak", 
21563         "roo-password-meter-weak", 
21564         "roo-password-meter-medium", 
21565         "roo-password-meter-strong", 
21566         "roo-password-meter-grey"
21567     ];
21568     
21569     this.errors = {};
21570     
21571     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21572 }
21573
21574 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21575     /**
21576      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21577      * {
21578      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21579      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21580      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21581      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21582      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21583      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21584      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21585      * })
21586      */
21587     // private
21588     
21589     meterWidth: 300,
21590     errorMsg :'',    
21591     errors: false,
21592     imageRoot: '/',
21593     /**
21594      * @cfg {String/Object} Label for the strength meter (defaults to
21595      * 'Password strength:')
21596      */
21597     // private
21598     meterLabel: '',
21599     /**
21600      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21601      * ['Weak', 'Medium', 'Strong'])
21602      */
21603     // private    
21604     pwdStrengths: false,    
21605     // private
21606     strength: 0,
21607     // private
21608     _lastPwd: null,
21609     // private
21610     kCapitalLetter: 0,
21611     kSmallLetter: 1,
21612     kDigit: 2,
21613     kPunctuation: 3,
21614     
21615     insecure: false,
21616     // private
21617     initEvents: function ()
21618     {
21619         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21620
21621         if (this.el.is('input[type=password]') && Roo.isSafari) {
21622             this.el.on('keydown', this.SafariOnKeyDown, this);
21623         }
21624
21625         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21626     },
21627     // private
21628     onRender: function (ct, position)
21629     {
21630         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21631         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21632         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21633
21634         this.trigger.createChild({
21635                    cn: [
21636                     {
21637                     //id: 'PwdMeter',
21638                     tag: 'div',
21639                     cls: 'roo-password-meter-grey col-xs-12',
21640                     style: {
21641                         //width: 0,
21642                         //width: this.meterWidth + 'px'                                                
21643                         }
21644                     },
21645                     {                            
21646                          cls: 'roo-password-meter-text'                          
21647                     }
21648                 ]            
21649         });
21650
21651          
21652         if (this.hideTrigger) {
21653             this.trigger.setDisplayed(false);
21654         }
21655         this.setSize(this.width || '', this.height || '');
21656     },
21657     // private
21658     onDestroy: function ()
21659     {
21660         if (this.trigger) {
21661             this.trigger.removeAllListeners();
21662             this.trigger.remove();
21663         }
21664         if (this.wrap) {
21665             this.wrap.remove();
21666         }
21667         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21668     },
21669     // private
21670     checkStrength: function ()
21671     {
21672         var pwd = this.inputEl().getValue();
21673         if (pwd == this._lastPwd) {
21674             return;
21675         }
21676
21677         var strength;
21678         if (this.ClientSideStrongPassword(pwd)) {
21679             strength = 3;
21680         } else if (this.ClientSideMediumPassword(pwd)) {
21681             strength = 2;
21682         } else if (this.ClientSideWeakPassword(pwd)) {
21683             strength = 1;
21684         } else {
21685             strength = 0;
21686         }
21687         
21688         Roo.log('strength1: ' + strength);
21689         
21690         //var pm = this.trigger.child('div/div/div').dom;
21691         var pm = this.trigger.child('div/div');
21692         pm.removeClass(this.meterClass);
21693         pm.addClass(this.meterClass[strength]);
21694                 
21695         
21696         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21697                 
21698         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21699         
21700         this._lastPwd = pwd;
21701     },
21702     reset: function ()
21703     {
21704         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21705         
21706         this._lastPwd = '';
21707         
21708         var pm = this.trigger.child('div/div');
21709         pm.removeClass(this.meterClass);
21710         pm.addClass('roo-password-meter-grey');        
21711         
21712         
21713         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21714         
21715         pt.innerHTML = '';
21716         this.inputEl().dom.type='password';
21717     },
21718     // private
21719     validateValue: function (value)
21720     {
21721         
21722         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21723             return false;
21724         }
21725         if (value.length == 0) {
21726             if (this.allowBlank) {
21727                 this.clearInvalid();
21728                 return true;
21729             }
21730
21731             this.markInvalid(this.errors.PwdEmpty);
21732             this.errorMsg = this.errors.PwdEmpty;
21733             return false;
21734         }
21735         
21736         if(this.insecure){
21737             return true;
21738         }
21739         
21740         if ('[\x21-\x7e]*'.match(value)) {
21741             this.markInvalid(this.errors.PwdBadChar);
21742             this.errorMsg = this.errors.PwdBadChar;
21743             return false;
21744         }
21745         if (value.length < 6) {
21746             this.markInvalid(this.errors.PwdShort);
21747             this.errorMsg = this.errors.PwdShort;
21748             return false;
21749         }
21750         if (value.length > 16) {
21751             this.markInvalid(this.errors.PwdLong);
21752             this.errorMsg = this.errors.PwdLong;
21753             return false;
21754         }
21755         var strength;
21756         if (this.ClientSideStrongPassword(value)) {
21757             strength = 3;
21758         } else if (this.ClientSideMediumPassword(value)) {
21759             strength = 2;
21760         } else if (this.ClientSideWeakPassword(value)) {
21761             strength = 1;
21762         } else {
21763             strength = 0;
21764         }
21765
21766         
21767         if (strength < 2) {
21768             //this.markInvalid(this.errors.TooWeak);
21769             this.errorMsg = this.errors.TooWeak;
21770             //return false;
21771         }
21772         
21773         
21774         console.log('strength2: ' + strength);
21775         
21776         //var pm = this.trigger.child('div/div/div').dom;
21777         
21778         var pm = this.trigger.child('div/div');
21779         pm.removeClass(this.meterClass);
21780         pm.addClass(this.meterClass[strength]);
21781                 
21782         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21783                 
21784         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21785         
21786         this.errorMsg = ''; 
21787         return true;
21788     },
21789     // private
21790     CharacterSetChecks: function (type)
21791     {
21792         this.type = type;
21793         this.fResult = false;
21794     },
21795     // private
21796     isctype: function (character, type)
21797     {
21798         switch (type) {  
21799             case this.kCapitalLetter:
21800                 if (character >= 'A' && character <= 'Z') {
21801                     return true;
21802                 }
21803                 break;
21804             
21805             case this.kSmallLetter:
21806                 if (character >= 'a' && character <= 'z') {
21807                     return true;
21808                 }
21809                 break;
21810             
21811             case this.kDigit:
21812                 if (character >= '0' && character <= '9') {
21813                     return true;
21814                 }
21815                 break;
21816             
21817             case this.kPunctuation:
21818                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21819                     return true;
21820                 }
21821                 break;
21822             
21823             default:
21824                 return false;
21825         }
21826
21827     },
21828     // private
21829     IsLongEnough: function (pwd, size)
21830     {
21831         return !(pwd == null || isNaN(size) || pwd.length < size);
21832     },
21833     // private
21834     SpansEnoughCharacterSets: function (word, nb)
21835     {
21836         if (!this.IsLongEnough(word, nb))
21837         {
21838             return false;
21839         }
21840
21841         var characterSetChecks = new Array(
21842             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21843             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21844         );
21845         
21846         for (var index = 0; index < word.length; ++index) {
21847             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21848                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21849                     characterSetChecks[nCharSet].fResult = true;
21850                     break;
21851                 }
21852             }
21853         }
21854
21855         var nCharSets = 0;
21856         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21857             if (characterSetChecks[nCharSet].fResult) {
21858                 ++nCharSets;
21859             }
21860         }
21861
21862         if (nCharSets < nb) {
21863             return false;
21864         }
21865         return true;
21866     },
21867     // private
21868     ClientSideStrongPassword: function (pwd)
21869     {
21870         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21871     },
21872     // private
21873     ClientSideMediumPassword: function (pwd)
21874     {
21875         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21876     },
21877     // private
21878     ClientSideWeakPassword: function (pwd)
21879     {
21880         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21881     }
21882           
21883 })//<script type="text/javascript">
21884
21885 /*
21886  * Based  Ext JS Library 1.1.1
21887  * Copyright(c) 2006-2007, Ext JS, LLC.
21888  * LGPL
21889  *
21890  */
21891  
21892 /**
21893  * @class Roo.HtmlEditorCore
21894  * @extends Roo.Component
21895  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21896  *
21897  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21898  */
21899
21900 Roo.HtmlEditorCore = function(config){
21901     
21902     
21903     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21904     
21905     
21906     this.addEvents({
21907         /**
21908          * @event initialize
21909          * Fires when the editor is fully initialized (including the iframe)
21910          * @param {Roo.HtmlEditorCore} this
21911          */
21912         initialize: true,
21913         /**
21914          * @event activate
21915          * Fires when the editor is first receives the focus. Any insertion must wait
21916          * until after this event.
21917          * @param {Roo.HtmlEditorCore} this
21918          */
21919         activate: true,
21920          /**
21921          * @event beforesync
21922          * Fires before the textarea is updated with content from the editor iframe. Return false
21923          * to cancel the sync.
21924          * @param {Roo.HtmlEditorCore} this
21925          * @param {String} html
21926          */
21927         beforesync: true,
21928          /**
21929          * @event beforepush
21930          * Fires before the iframe editor is updated with content from the textarea. Return false
21931          * to cancel the push.
21932          * @param {Roo.HtmlEditorCore} this
21933          * @param {String} html
21934          */
21935         beforepush: true,
21936          /**
21937          * @event sync
21938          * Fires when the textarea is updated with content from the editor iframe.
21939          * @param {Roo.HtmlEditorCore} this
21940          * @param {String} html
21941          */
21942         sync: true,
21943          /**
21944          * @event push
21945          * Fires when the iframe editor is updated with content from the textarea.
21946          * @param {Roo.HtmlEditorCore} this
21947          * @param {String} html
21948          */
21949         push: true,
21950         
21951         /**
21952          * @event editorevent
21953          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21954          * @param {Roo.HtmlEditorCore} this
21955          */
21956         editorevent: true
21957         
21958     });
21959     
21960     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21961     
21962     // defaults : white / black...
21963     this.applyBlacklists();
21964     
21965     
21966     
21967 };
21968
21969
21970 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21971
21972
21973      /**
21974      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21975      */
21976     
21977     owner : false,
21978     
21979      /**
21980      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21981      *                        Roo.resizable.
21982      */
21983     resizable : false,
21984      /**
21985      * @cfg {Number} height (in pixels)
21986      */   
21987     height: 300,
21988    /**
21989      * @cfg {Number} width (in pixels)
21990      */   
21991     width: 500,
21992     
21993     /**
21994      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21995      * 
21996      */
21997     stylesheets: false,
21998     
21999     // id of frame..
22000     frameId: false,
22001     
22002     // private properties
22003     validationEvent : false,
22004     deferHeight: true,
22005     initialized : false,
22006     activated : false,
22007     sourceEditMode : false,
22008     onFocus : Roo.emptyFn,
22009     iframePad:3,
22010     hideMode:'offsets',
22011     
22012     clearUp: true,
22013     
22014     // blacklist + whitelisted elements..
22015     black: false,
22016     white: false,
22017      
22018     bodyCls : '',
22019
22020     /**
22021      * Protected method that will not generally be called directly. It
22022      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22023      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22024      */
22025     getDocMarkup : function(){
22026         // body styles..
22027         var st = '';
22028         
22029         // inherit styels from page...?? 
22030         if (this.stylesheets === false) {
22031             
22032             Roo.get(document.head).select('style').each(function(node) {
22033                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22034             });
22035             
22036             Roo.get(document.head).select('link').each(function(node) { 
22037                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22038             });
22039             
22040         } else if (!this.stylesheets.length) {
22041                 // simple..
22042                 st = '<style type="text/css">' +
22043                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22044                    '</style>';
22045         } else { 
22046             st = '<style type="text/css">' +
22047                     this.stylesheets +
22048                 '</style>';
22049         }
22050         
22051         st +=  '<style type="text/css">' +
22052             'IMG { cursor: pointer } ' +
22053         '</style>';
22054
22055         var cls = 'roo-htmleditor-body';
22056         
22057         if(this.bodyCls.length){
22058             cls += ' ' + this.bodyCls;
22059         }
22060         
22061         return '<html><head>' + st  +
22062             //<style type="text/css">' +
22063             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22064             //'</style>' +
22065             ' </head><body class="' +  cls + '"></body></html>';
22066     },
22067
22068     // private
22069     onRender : function(ct, position)
22070     {
22071         var _t = this;
22072         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22073         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22074         
22075         
22076         this.el.dom.style.border = '0 none';
22077         this.el.dom.setAttribute('tabIndex', -1);
22078         this.el.addClass('x-hidden hide');
22079         
22080         
22081         
22082         if(Roo.isIE){ // fix IE 1px bogus margin
22083             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22084         }
22085        
22086         
22087         this.frameId = Roo.id();
22088         
22089          
22090         
22091         var iframe = this.owner.wrap.createChild({
22092             tag: 'iframe',
22093             cls: 'form-control', // bootstrap..
22094             id: this.frameId,
22095             name: this.frameId,
22096             frameBorder : 'no',
22097             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22098         }, this.el
22099         );
22100         
22101         
22102         this.iframe = iframe.dom;
22103
22104          this.assignDocWin();
22105         
22106         this.doc.designMode = 'on';
22107        
22108         this.doc.open();
22109         this.doc.write(this.getDocMarkup());
22110         this.doc.close();
22111
22112         
22113         var task = { // must defer to wait for browser to be ready
22114             run : function(){
22115                 //console.log("run task?" + this.doc.readyState);
22116                 this.assignDocWin();
22117                 if(this.doc.body || this.doc.readyState == 'complete'){
22118                     try {
22119                         this.doc.designMode="on";
22120                     } catch (e) {
22121                         return;
22122                     }
22123                     Roo.TaskMgr.stop(task);
22124                     this.initEditor.defer(10, this);
22125                 }
22126             },
22127             interval : 10,
22128             duration: 10000,
22129             scope: this
22130         };
22131         Roo.TaskMgr.start(task);
22132
22133     },
22134
22135     // private
22136     onResize : function(w, h)
22137     {
22138          Roo.log('resize: ' +w + ',' + h );
22139         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22140         if(!this.iframe){
22141             return;
22142         }
22143         if(typeof w == 'number'){
22144             
22145             this.iframe.style.width = w + 'px';
22146         }
22147         if(typeof h == 'number'){
22148             
22149             this.iframe.style.height = h + 'px';
22150             if(this.doc){
22151                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22152             }
22153         }
22154         
22155     },
22156
22157     /**
22158      * Toggles the editor between standard and source edit mode.
22159      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22160      */
22161     toggleSourceEdit : function(sourceEditMode){
22162         
22163         this.sourceEditMode = sourceEditMode === true;
22164         
22165         if(this.sourceEditMode){
22166  
22167             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22168             
22169         }else{
22170             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22171             //this.iframe.className = '';
22172             this.deferFocus();
22173         }
22174         //this.setSize(this.owner.wrap.getSize());
22175         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22176     },
22177
22178     
22179   
22180
22181     /**
22182      * Protected method that will not generally be called directly. If you need/want
22183      * custom HTML cleanup, this is the method you should override.
22184      * @param {String} html The HTML to be cleaned
22185      * return {String} The cleaned HTML
22186      */
22187     cleanHtml : function(html){
22188         html = String(html);
22189         if(html.length > 5){
22190             if(Roo.isSafari){ // strip safari nonsense
22191                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22192             }
22193         }
22194         if(html == '&nbsp;'){
22195             html = '';
22196         }
22197         return html;
22198     },
22199
22200     /**
22201      * HTML Editor -> Textarea
22202      * Protected method that will not generally be called directly. Syncs the contents
22203      * of the editor iframe with the textarea.
22204      */
22205     syncValue : function(){
22206         if(this.initialized){
22207             var bd = (this.doc.body || this.doc.documentElement);
22208             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22209             var html = bd.innerHTML;
22210             if(Roo.isSafari){
22211                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22212                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22213                 if(m && m[1]){
22214                     html = '<div style="'+m[0]+'">' + html + '</div>';
22215                 }
22216             }
22217             html = this.cleanHtml(html);
22218             // fix up the special chars.. normaly like back quotes in word...
22219             // however we do not want to do this with chinese..
22220             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22221                 var cc = b.charCodeAt();
22222                 if (
22223                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22224                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22225                     (cc >= 0xf900 && cc < 0xfb00 )
22226                 ) {
22227                         return b;
22228                 }
22229                 return "&#"+cc+";" 
22230             });
22231             if(this.owner.fireEvent('beforesync', this, html) !== false){
22232                 this.el.dom.value = html;
22233                 this.owner.fireEvent('sync', this, html);
22234             }
22235         }
22236     },
22237
22238     /**
22239      * Protected method that will not generally be called directly. Pushes the value of the textarea
22240      * into the iframe editor.
22241      */
22242     pushValue : function(){
22243         if(this.initialized){
22244             var v = this.el.dom.value.trim();
22245             
22246 //            if(v.length < 1){
22247 //                v = '&#160;';
22248 //            }
22249             
22250             if(this.owner.fireEvent('beforepush', this, v) !== false){
22251                 var d = (this.doc.body || this.doc.documentElement);
22252                 d.innerHTML = v;
22253                 this.cleanUpPaste();
22254                 this.el.dom.value = d.innerHTML;
22255                 this.owner.fireEvent('push', this, v);
22256             }
22257         }
22258     },
22259
22260     // private
22261     deferFocus : function(){
22262         this.focus.defer(10, this);
22263     },
22264
22265     // doc'ed in Field
22266     focus : function(){
22267         if(this.win && !this.sourceEditMode){
22268             this.win.focus();
22269         }else{
22270             this.el.focus();
22271         }
22272     },
22273     
22274     assignDocWin: function()
22275     {
22276         var iframe = this.iframe;
22277         
22278          if(Roo.isIE){
22279             this.doc = iframe.contentWindow.document;
22280             this.win = iframe.contentWindow;
22281         } else {
22282 //            if (!Roo.get(this.frameId)) {
22283 //                return;
22284 //            }
22285 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22286 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22287             
22288             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22289                 return;
22290             }
22291             
22292             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22293             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22294         }
22295     },
22296     
22297     // private
22298     initEditor : function(){
22299         //console.log("INIT EDITOR");
22300         this.assignDocWin();
22301         
22302         
22303         
22304         this.doc.designMode="on";
22305         this.doc.open();
22306         this.doc.write(this.getDocMarkup());
22307         this.doc.close();
22308         
22309         var dbody = (this.doc.body || this.doc.documentElement);
22310         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22311         // this copies styles from the containing element into thsi one..
22312         // not sure why we need all of this..
22313         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22314         
22315         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22316         //ss['background-attachment'] = 'fixed'; // w3c
22317         dbody.bgProperties = 'fixed'; // ie
22318         //Roo.DomHelper.applyStyles(dbody, ss);
22319         Roo.EventManager.on(this.doc, {
22320             //'mousedown': this.onEditorEvent,
22321             'mouseup': this.onEditorEvent,
22322             'dblclick': this.onEditorEvent,
22323             'click': this.onEditorEvent,
22324             'keyup': this.onEditorEvent,
22325             buffer:100,
22326             scope: this
22327         });
22328         if(Roo.isGecko){
22329             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22330         }
22331         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22332             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22333         }
22334         this.initialized = true;
22335
22336         this.owner.fireEvent('initialize', this);
22337         this.pushValue();
22338     },
22339
22340     // private
22341     onDestroy : function(){
22342         
22343         
22344         
22345         if(this.rendered){
22346             
22347             //for (var i =0; i < this.toolbars.length;i++) {
22348             //    // fixme - ask toolbars for heights?
22349             //    this.toolbars[i].onDestroy();
22350            // }
22351             
22352             //this.wrap.dom.innerHTML = '';
22353             //this.wrap.remove();
22354         }
22355     },
22356
22357     // private
22358     onFirstFocus : function(){
22359         
22360         this.assignDocWin();
22361         
22362         
22363         this.activated = true;
22364          
22365     
22366         if(Roo.isGecko){ // prevent silly gecko errors
22367             this.win.focus();
22368             var s = this.win.getSelection();
22369             if(!s.focusNode || s.focusNode.nodeType != 3){
22370                 var r = s.getRangeAt(0);
22371                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22372                 r.collapse(true);
22373                 this.deferFocus();
22374             }
22375             try{
22376                 this.execCmd('useCSS', true);
22377                 this.execCmd('styleWithCSS', false);
22378             }catch(e){}
22379         }
22380         this.owner.fireEvent('activate', this);
22381     },
22382
22383     // private
22384     adjustFont: function(btn){
22385         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22386         //if(Roo.isSafari){ // safari
22387         //    adjust *= 2;
22388        // }
22389         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22390         if(Roo.isSafari){ // safari
22391             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22392             v =  (v < 10) ? 10 : v;
22393             v =  (v > 48) ? 48 : v;
22394             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22395             
22396         }
22397         
22398         
22399         v = Math.max(1, v+adjust);
22400         
22401         this.execCmd('FontSize', v  );
22402     },
22403
22404     onEditorEvent : function(e)
22405     {
22406         this.owner.fireEvent('editorevent', this, e);
22407       //  this.updateToolbar();
22408         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22409     },
22410
22411     insertTag : function(tg)
22412     {
22413         // could be a bit smarter... -> wrap the current selected tRoo..
22414         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22415             
22416             range = this.createRange(this.getSelection());
22417             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22418             wrappingNode.appendChild(range.extractContents());
22419             range.insertNode(wrappingNode);
22420
22421             return;
22422             
22423             
22424             
22425         }
22426         this.execCmd("formatblock",   tg);
22427         
22428     },
22429     
22430     insertText : function(txt)
22431     {
22432         
22433         
22434         var range = this.createRange();
22435         range.deleteContents();
22436                //alert(Sender.getAttribute('label'));
22437                
22438         range.insertNode(this.doc.createTextNode(txt));
22439     } ,
22440     
22441      
22442
22443     /**
22444      * Executes a Midas editor command on the editor document and performs necessary focus and
22445      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22446      * @param {String} cmd The Midas command
22447      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22448      */
22449     relayCmd : function(cmd, value){
22450         this.win.focus();
22451         this.execCmd(cmd, value);
22452         this.owner.fireEvent('editorevent', this);
22453         //this.updateToolbar();
22454         this.owner.deferFocus();
22455     },
22456
22457     /**
22458      * Executes a Midas editor command directly on the editor document.
22459      * For visual commands, you should use {@link #relayCmd} instead.
22460      * <b>This should only be called after the editor is initialized.</b>
22461      * @param {String} cmd The Midas command
22462      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22463      */
22464     execCmd : function(cmd, value){
22465         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22466         this.syncValue();
22467     },
22468  
22469  
22470    
22471     /**
22472      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22473      * to insert tRoo.
22474      * @param {String} text | dom node.. 
22475      */
22476     insertAtCursor : function(text)
22477     {
22478         
22479         if(!this.activated){
22480             return;
22481         }
22482         /*
22483         if(Roo.isIE){
22484             this.win.focus();
22485             var r = this.doc.selection.createRange();
22486             if(r){
22487                 r.collapse(true);
22488                 r.pasteHTML(text);
22489                 this.syncValue();
22490                 this.deferFocus();
22491             
22492             }
22493             return;
22494         }
22495         */
22496         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22497             this.win.focus();
22498             
22499             
22500             // from jquery ui (MIT licenced)
22501             var range, node;
22502             var win = this.win;
22503             
22504             if (win.getSelection && win.getSelection().getRangeAt) {
22505                 range = win.getSelection().getRangeAt(0);
22506                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22507                 range.insertNode(node);
22508             } else if (win.document.selection && win.document.selection.createRange) {
22509                 // no firefox support
22510                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22511                 win.document.selection.createRange().pasteHTML(txt);
22512             } else {
22513                 // no firefox support
22514                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22515                 this.execCmd('InsertHTML', txt);
22516             } 
22517             
22518             this.syncValue();
22519             
22520             this.deferFocus();
22521         }
22522     },
22523  // private
22524     mozKeyPress : function(e){
22525         if(e.ctrlKey){
22526             var c = e.getCharCode(), cmd;
22527           
22528             if(c > 0){
22529                 c = String.fromCharCode(c).toLowerCase();
22530                 switch(c){
22531                     case 'b':
22532                         cmd = 'bold';
22533                         break;
22534                     case 'i':
22535                         cmd = 'italic';
22536                         break;
22537                     
22538                     case 'u':
22539                         cmd = 'underline';
22540                         break;
22541                     
22542                     case 'v':
22543                         this.cleanUpPaste.defer(100, this);
22544                         return;
22545                         
22546                 }
22547                 if(cmd){
22548                     this.win.focus();
22549                     this.execCmd(cmd);
22550                     this.deferFocus();
22551                     e.preventDefault();
22552                 }
22553                 
22554             }
22555         }
22556     },
22557
22558     // private
22559     fixKeys : function(){ // load time branching for fastest keydown performance
22560         if(Roo.isIE){
22561             return function(e){
22562                 var k = e.getKey(), r;
22563                 if(k == e.TAB){
22564                     e.stopEvent();
22565                     r = this.doc.selection.createRange();
22566                     if(r){
22567                         r.collapse(true);
22568                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22569                         this.deferFocus();
22570                     }
22571                     return;
22572                 }
22573                 
22574                 if(k == e.ENTER){
22575                     r = this.doc.selection.createRange();
22576                     if(r){
22577                         var target = r.parentElement();
22578                         if(!target || target.tagName.toLowerCase() != 'li'){
22579                             e.stopEvent();
22580                             r.pasteHTML('<br />');
22581                             r.collapse(false);
22582                             r.select();
22583                         }
22584                     }
22585                 }
22586                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22587                     this.cleanUpPaste.defer(100, this);
22588                     return;
22589                 }
22590                 
22591                 
22592             };
22593         }else if(Roo.isOpera){
22594             return function(e){
22595                 var k = e.getKey();
22596                 if(k == e.TAB){
22597                     e.stopEvent();
22598                     this.win.focus();
22599                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22600                     this.deferFocus();
22601                 }
22602                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22603                     this.cleanUpPaste.defer(100, this);
22604                     return;
22605                 }
22606                 
22607             };
22608         }else if(Roo.isSafari){
22609             return function(e){
22610                 var k = e.getKey();
22611                 
22612                 if(k == e.TAB){
22613                     e.stopEvent();
22614                     this.execCmd('InsertText','\t');
22615                     this.deferFocus();
22616                     return;
22617                 }
22618                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22619                     this.cleanUpPaste.defer(100, this);
22620                     return;
22621                 }
22622                 
22623              };
22624         }
22625     }(),
22626     
22627     getAllAncestors: function()
22628     {
22629         var p = this.getSelectedNode();
22630         var a = [];
22631         if (!p) {
22632             a.push(p); // push blank onto stack..
22633             p = this.getParentElement();
22634         }
22635         
22636         
22637         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22638             a.push(p);
22639             p = p.parentNode;
22640         }
22641         a.push(this.doc.body);
22642         return a;
22643     },
22644     lastSel : false,
22645     lastSelNode : false,
22646     
22647     
22648     getSelection : function() 
22649     {
22650         this.assignDocWin();
22651         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22652     },
22653     
22654     getSelectedNode: function() 
22655     {
22656         // this may only work on Gecko!!!
22657         
22658         // should we cache this!!!!
22659         
22660         
22661         
22662          
22663         var range = this.createRange(this.getSelection()).cloneRange();
22664         
22665         if (Roo.isIE) {
22666             var parent = range.parentElement();
22667             while (true) {
22668                 var testRange = range.duplicate();
22669                 testRange.moveToElementText(parent);
22670                 if (testRange.inRange(range)) {
22671                     break;
22672                 }
22673                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22674                     break;
22675                 }
22676                 parent = parent.parentElement;
22677             }
22678             return parent;
22679         }
22680         
22681         // is ancestor a text element.
22682         var ac =  range.commonAncestorContainer;
22683         if (ac.nodeType == 3) {
22684             ac = ac.parentNode;
22685         }
22686         
22687         var ar = ac.childNodes;
22688          
22689         var nodes = [];
22690         var other_nodes = [];
22691         var has_other_nodes = false;
22692         for (var i=0;i<ar.length;i++) {
22693             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22694                 continue;
22695             }
22696             // fullly contained node.
22697             
22698             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22699                 nodes.push(ar[i]);
22700                 continue;
22701             }
22702             
22703             // probably selected..
22704             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22705                 other_nodes.push(ar[i]);
22706                 continue;
22707             }
22708             // outer..
22709             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22710                 continue;
22711             }
22712             
22713             
22714             has_other_nodes = true;
22715         }
22716         if (!nodes.length && other_nodes.length) {
22717             nodes= other_nodes;
22718         }
22719         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22720             return false;
22721         }
22722         
22723         return nodes[0];
22724     },
22725     createRange: function(sel)
22726     {
22727         // this has strange effects when using with 
22728         // top toolbar - not sure if it's a great idea.
22729         //this.editor.contentWindow.focus();
22730         if (typeof sel != "undefined") {
22731             try {
22732                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22733             } catch(e) {
22734                 return this.doc.createRange();
22735             }
22736         } else {
22737             return this.doc.createRange();
22738         }
22739     },
22740     getParentElement: function()
22741     {
22742         
22743         this.assignDocWin();
22744         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22745         
22746         var range = this.createRange(sel);
22747          
22748         try {
22749             var p = range.commonAncestorContainer;
22750             while (p.nodeType == 3) { // text node
22751                 p = p.parentNode;
22752             }
22753             return p;
22754         } catch (e) {
22755             return null;
22756         }
22757     
22758     },
22759     /***
22760      *
22761      * Range intersection.. the hard stuff...
22762      *  '-1' = before
22763      *  '0' = hits..
22764      *  '1' = after.
22765      *         [ -- selected range --- ]
22766      *   [fail]                        [fail]
22767      *
22768      *    basically..
22769      *      if end is before start or  hits it. fail.
22770      *      if start is after end or hits it fail.
22771      *
22772      *   if either hits (but other is outside. - then it's not 
22773      *   
22774      *    
22775      **/
22776     
22777     
22778     // @see http://www.thismuchiknow.co.uk/?p=64.
22779     rangeIntersectsNode : function(range, node)
22780     {
22781         var nodeRange = node.ownerDocument.createRange();
22782         try {
22783             nodeRange.selectNode(node);
22784         } catch (e) {
22785             nodeRange.selectNodeContents(node);
22786         }
22787     
22788         var rangeStartRange = range.cloneRange();
22789         rangeStartRange.collapse(true);
22790     
22791         var rangeEndRange = range.cloneRange();
22792         rangeEndRange.collapse(false);
22793     
22794         var nodeStartRange = nodeRange.cloneRange();
22795         nodeStartRange.collapse(true);
22796     
22797         var nodeEndRange = nodeRange.cloneRange();
22798         nodeEndRange.collapse(false);
22799     
22800         return rangeStartRange.compareBoundaryPoints(
22801                  Range.START_TO_START, nodeEndRange) == -1 &&
22802                rangeEndRange.compareBoundaryPoints(
22803                  Range.START_TO_START, nodeStartRange) == 1;
22804         
22805          
22806     },
22807     rangeCompareNode : function(range, node)
22808     {
22809         var nodeRange = node.ownerDocument.createRange();
22810         try {
22811             nodeRange.selectNode(node);
22812         } catch (e) {
22813             nodeRange.selectNodeContents(node);
22814         }
22815         
22816         
22817         range.collapse(true);
22818     
22819         nodeRange.collapse(true);
22820      
22821         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22822         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22823          
22824         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22825         
22826         var nodeIsBefore   =  ss == 1;
22827         var nodeIsAfter    = ee == -1;
22828         
22829         if (nodeIsBefore && nodeIsAfter) {
22830             return 0; // outer
22831         }
22832         if (!nodeIsBefore && nodeIsAfter) {
22833             return 1; //right trailed.
22834         }
22835         
22836         if (nodeIsBefore && !nodeIsAfter) {
22837             return 2;  // left trailed.
22838         }
22839         // fully contined.
22840         return 3;
22841     },
22842
22843     // private? - in a new class?
22844     cleanUpPaste :  function()
22845     {
22846         // cleans up the whole document..
22847         Roo.log('cleanuppaste');
22848         
22849         this.cleanUpChildren(this.doc.body);
22850         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22851         if (clean != this.doc.body.innerHTML) {
22852             this.doc.body.innerHTML = clean;
22853         }
22854         
22855     },
22856     
22857     cleanWordChars : function(input) {// change the chars to hex code
22858         var he = Roo.HtmlEditorCore;
22859         
22860         var output = input;
22861         Roo.each(he.swapCodes, function(sw) { 
22862             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22863             
22864             output = output.replace(swapper, sw[1]);
22865         });
22866         
22867         return output;
22868     },
22869     
22870     
22871     cleanUpChildren : function (n)
22872     {
22873         if (!n.childNodes.length) {
22874             return;
22875         }
22876         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22877            this.cleanUpChild(n.childNodes[i]);
22878         }
22879     },
22880     
22881     
22882         
22883     
22884     cleanUpChild : function (node)
22885     {
22886         var ed = this;
22887         //console.log(node);
22888         if (node.nodeName == "#text") {
22889             // clean up silly Windows -- stuff?
22890             return; 
22891         }
22892         if (node.nodeName == "#comment") {
22893             node.parentNode.removeChild(node);
22894             // clean up silly Windows -- stuff?
22895             return; 
22896         }
22897         var lcname = node.tagName.toLowerCase();
22898         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22899         // whitelist of tags..
22900         
22901         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22902             // remove node.
22903             node.parentNode.removeChild(node);
22904             return;
22905             
22906         }
22907         
22908         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22909         
22910         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22911         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22912         
22913         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22914         //    remove_keep_children = true;
22915         //}
22916         
22917         if (remove_keep_children) {
22918             this.cleanUpChildren(node);
22919             // inserts everything just before this node...
22920             while (node.childNodes.length) {
22921                 var cn = node.childNodes[0];
22922                 node.removeChild(cn);
22923                 node.parentNode.insertBefore(cn, node);
22924             }
22925             node.parentNode.removeChild(node);
22926             return;
22927         }
22928         
22929         if (!node.attributes || !node.attributes.length) {
22930             this.cleanUpChildren(node);
22931             return;
22932         }
22933         
22934         function cleanAttr(n,v)
22935         {
22936             
22937             if (v.match(/^\./) || v.match(/^\//)) {
22938                 return;
22939             }
22940             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22941                 return;
22942             }
22943             if (v.match(/^#/)) {
22944                 return;
22945             }
22946 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22947             node.removeAttribute(n);
22948             
22949         }
22950         
22951         var cwhite = this.cwhite;
22952         var cblack = this.cblack;
22953             
22954         function cleanStyle(n,v)
22955         {
22956             if (v.match(/expression/)) { //XSS?? should we even bother..
22957                 node.removeAttribute(n);
22958                 return;
22959             }
22960             
22961             var parts = v.split(/;/);
22962             var clean = [];
22963             
22964             Roo.each(parts, function(p) {
22965                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22966                 if (!p.length) {
22967                     return true;
22968                 }
22969                 var l = p.split(':').shift().replace(/\s+/g,'');
22970                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22971                 
22972                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22973 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22974                     //node.removeAttribute(n);
22975                     return true;
22976                 }
22977                 //Roo.log()
22978                 // only allow 'c whitelisted system attributes'
22979                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22980 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22981                     //node.removeAttribute(n);
22982                     return true;
22983                 }
22984                 
22985                 
22986                  
22987                 
22988                 clean.push(p);
22989                 return true;
22990             });
22991             if (clean.length) { 
22992                 node.setAttribute(n, clean.join(';'));
22993             } else {
22994                 node.removeAttribute(n);
22995             }
22996             
22997         }
22998         
22999         
23000         for (var i = node.attributes.length-1; i > -1 ; i--) {
23001             var a = node.attributes[i];
23002             //console.log(a);
23003             
23004             if (a.name.toLowerCase().substr(0,2)=='on')  {
23005                 node.removeAttribute(a.name);
23006                 continue;
23007             }
23008             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23009                 node.removeAttribute(a.name);
23010                 continue;
23011             }
23012             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23013                 cleanAttr(a.name,a.value); // fixme..
23014                 continue;
23015             }
23016             if (a.name == 'style') {
23017                 cleanStyle(a.name,a.value);
23018                 continue;
23019             }
23020             /// clean up MS crap..
23021             // tecnically this should be a list of valid class'es..
23022             
23023             
23024             if (a.name == 'class') {
23025                 if (a.value.match(/^Mso/)) {
23026                     node.className = '';
23027                 }
23028                 
23029                 if (a.value.match(/^body$/)) {
23030                     node.className = '';
23031                 }
23032                 continue;
23033             }
23034             
23035             // style cleanup!?
23036             // class cleanup?
23037             
23038         }
23039         
23040         
23041         this.cleanUpChildren(node);
23042         
23043         
23044     },
23045     
23046     /**
23047      * Clean up MS wordisms...
23048      */
23049     cleanWord : function(node)
23050     {
23051         
23052         
23053         if (!node) {
23054             this.cleanWord(this.doc.body);
23055             return;
23056         }
23057         if (node.nodeName == "#text") {
23058             // clean up silly Windows -- stuff?
23059             return; 
23060         }
23061         if (node.nodeName == "#comment") {
23062             node.parentNode.removeChild(node);
23063             // clean up silly Windows -- stuff?
23064             return; 
23065         }
23066         
23067         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23068             node.parentNode.removeChild(node);
23069             return;
23070         }
23071         
23072         // remove - but keep children..
23073         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
23074             while (node.childNodes.length) {
23075                 var cn = node.childNodes[0];
23076                 node.removeChild(cn);
23077                 node.parentNode.insertBefore(cn, node);
23078             }
23079             node.parentNode.removeChild(node);
23080             this.iterateChildren(node, this.cleanWord);
23081             return;
23082         }
23083         // clean styles
23084         if (node.className.length) {
23085             
23086             var cn = node.className.split(/\W+/);
23087             var cna = [];
23088             Roo.each(cn, function(cls) {
23089                 if (cls.match(/Mso[a-zA-Z]+/)) {
23090                     return;
23091                 }
23092                 cna.push(cls);
23093             });
23094             node.className = cna.length ? cna.join(' ') : '';
23095             if (!cna.length) {
23096                 node.removeAttribute("class");
23097             }
23098         }
23099         
23100         if (node.hasAttribute("lang")) {
23101             node.removeAttribute("lang");
23102         }
23103         
23104         if (node.hasAttribute("style")) {
23105             
23106             var styles = node.getAttribute("style").split(";");
23107             var nstyle = [];
23108             Roo.each(styles, function(s) {
23109                 if (!s.match(/:/)) {
23110                     return;
23111                 }
23112                 var kv = s.split(":");
23113                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23114                     return;
23115                 }
23116                 // what ever is left... we allow.
23117                 nstyle.push(s);
23118             });
23119             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23120             if (!nstyle.length) {
23121                 node.removeAttribute('style');
23122             }
23123         }
23124         this.iterateChildren(node, this.cleanWord);
23125         
23126         
23127         
23128     },
23129     /**
23130      * iterateChildren of a Node, calling fn each time, using this as the scole..
23131      * @param {DomNode} node node to iterate children of.
23132      * @param {Function} fn method of this class to call on each item.
23133      */
23134     iterateChildren : function(node, fn)
23135     {
23136         if (!node.childNodes.length) {
23137                 return;
23138         }
23139         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23140            fn.call(this, node.childNodes[i])
23141         }
23142     },
23143     
23144     
23145     /**
23146      * cleanTableWidths.
23147      *
23148      * Quite often pasting from word etc.. results in tables with column and widths.
23149      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23150      *
23151      */
23152     cleanTableWidths : function(node)
23153     {
23154          
23155          
23156         if (!node) {
23157             this.cleanTableWidths(this.doc.body);
23158             return;
23159         }
23160         
23161         // ignore list...
23162         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23163             return; 
23164         }
23165         Roo.log(node.tagName);
23166         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23167             this.iterateChildren(node, this.cleanTableWidths);
23168             return;
23169         }
23170         if (node.hasAttribute('width')) {
23171             node.removeAttribute('width');
23172         }
23173         
23174          
23175         if (node.hasAttribute("style")) {
23176             // pretty basic...
23177             
23178             var styles = node.getAttribute("style").split(";");
23179             var nstyle = [];
23180             Roo.each(styles, function(s) {
23181                 if (!s.match(/:/)) {
23182                     return;
23183                 }
23184                 var kv = s.split(":");
23185                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23186                     return;
23187                 }
23188                 // what ever is left... we allow.
23189                 nstyle.push(s);
23190             });
23191             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23192             if (!nstyle.length) {
23193                 node.removeAttribute('style');
23194             }
23195         }
23196         
23197         this.iterateChildren(node, this.cleanTableWidths);
23198         
23199         
23200     },
23201     
23202     
23203     
23204     
23205     domToHTML : function(currentElement, depth, nopadtext) {
23206         
23207         depth = depth || 0;
23208         nopadtext = nopadtext || false;
23209     
23210         if (!currentElement) {
23211             return this.domToHTML(this.doc.body);
23212         }
23213         
23214         //Roo.log(currentElement);
23215         var j;
23216         var allText = false;
23217         var nodeName = currentElement.nodeName;
23218         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23219         
23220         if  (nodeName == '#text') {
23221             
23222             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23223         }
23224         
23225         
23226         var ret = '';
23227         if (nodeName != 'BODY') {
23228              
23229             var i = 0;
23230             // Prints the node tagName, such as <A>, <IMG>, etc
23231             if (tagName) {
23232                 var attr = [];
23233                 for(i = 0; i < currentElement.attributes.length;i++) {
23234                     // quoting?
23235                     var aname = currentElement.attributes.item(i).name;
23236                     if (!currentElement.attributes.item(i).value.length) {
23237                         continue;
23238                     }
23239                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23240                 }
23241                 
23242                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23243             } 
23244             else {
23245                 
23246                 // eack
23247             }
23248         } else {
23249             tagName = false;
23250         }
23251         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23252             return ret;
23253         }
23254         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23255             nopadtext = true;
23256         }
23257         
23258         
23259         // Traverse the tree
23260         i = 0;
23261         var currentElementChild = currentElement.childNodes.item(i);
23262         var allText = true;
23263         var innerHTML  = '';
23264         lastnode = '';
23265         while (currentElementChild) {
23266             // Formatting code (indent the tree so it looks nice on the screen)
23267             var nopad = nopadtext;
23268             if (lastnode == 'SPAN') {
23269                 nopad  = true;
23270             }
23271             // text
23272             if  (currentElementChild.nodeName == '#text') {
23273                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23274                 toadd = nopadtext ? toadd : toadd.trim();
23275                 if (!nopad && toadd.length > 80) {
23276                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23277                 }
23278                 innerHTML  += toadd;
23279                 
23280                 i++;
23281                 currentElementChild = currentElement.childNodes.item(i);
23282                 lastNode = '';
23283                 continue;
23284             }
23285             allText = false;
23286             
23287             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23288                 
23289             // Recursively traverse the tree structure of the child node
23290             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23291             lastnode = currentElementChild.nodeName;
23292             i++;
23293             currentElementChild=currentElement.childNodes.item(i);
23294         }
23295         
23296         ret += innerHTML;
23297         
23298         if (!allText) {
23299                 // The remaining code is mostly for formatting the tree
23300             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23301         }
23302         
23303         
23304         if (tagName) {
23305             ret+= "</"+tagName+">";
23306         }
23307         return ret;
23308         
23309     },
23310         
23311     applyBlacklists : function()
23312     {
23313         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23314         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23315         
23316         this.white = [];
23317         this.black = [];
23318         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23319             if (b.indexOf(tag) > -1) {
23320                 return;
23321             }
23322             this.white.push(tag);
23323             
23324         }, this);
23325         
23326         Roo.each(w, function(tag) {
23327             if (b.indexOf(tag) > -1) {
23328                 return;
23329             }
23330             if (this.white.indexOf(tag) > -1) {
23331                 return;
23332             }
23333             this.white.push(tag);
23334             
23335         }, this);
23336         
23337         
23338         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23339             if (w.indexOf(tag) > -1) {
23340                 return;
23341             }
23342             this.black.push(tag);
23343             
23344         }, this);
23345         
23346         Roo.each(b, function(tag) {
23347             if (w.indexOf(tag) > -1) {
23348                 return;
23349             }
23350             if (this.black.indexOf(tag) > -1) {
23351                 return;
23352             }
23353             this.black.push(tag);
23354             
23355         }, this);
23356         
23357         
23358         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23359         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23360         
23361         this.cwhite = [];
23362         this.cblack = [];
23363         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23364             if (b.indexOf(tag) > -1) {
23365                 return;
23366             }
23367             this.cwhite.push(tag);
23368             
23369         }, this);
23370         
23371         Roo.each(w, function(tag) {
23372             if (b.indexOf(tag) > -1) {
23373                 return;
23374             }
23375             if (this.cwhite.indexOf(tag) > -1) {
23376                 return;
23377             }
23378             this.cwhite.push(tag);
23379             
23380         }, this);
23381         
23382         
23383         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23384             if (w.indexOf(tag) > -1) {
23385                 return;
23386             }
23387             this.cblack.push(tag);
23388             
23389         }, this);
23390         
23391         Roo.each(b, function(tag) {
23392             if (w.indexOf(tag) > -1) {
23393                 return;
23394             }
23395             if (this.cblack.indexOf(tag) > -1) {
23396                 return;
23397             }
23398             this.cblack.push(tag);
23399             
23400         }, this);
23401     },
23402     
23403     setStylesheets : function(stylesheets)
23404     {
23405         if(typeof(stylesheets) == 'string'){
23406             Roo.get(this.iframe.contentDocument.head).createChild({
23407                 tag : 'link',
23408                 rel : 'stylesheet',
23409                 type : 'text/css',
23410                 href : stylesheets
23411             });
23412             
23413             return;
23414         }
23415         var _this = this;
23416      
23417         Roo.each(stylesheets, function(s) {
23418             if(!s.length){
23419                 return;
23420             }
23421             
23422             Roo.get(_this.iframe.contentDocument.head).createChild({
23423                 tag : 'link',
23424                 rel : 'stylesheet',
23425                 type : 'text/css',
23426                 href : s
23427             });
23428         });
23429
23430         
23431     },
23432     
23433     removeStylesheets : function()
23434     {
23435         var _this = this;
23436         
23437         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23438             s.remove();
23439         });
23440     },
23441     
23442     setStyle : function(style)
23443     {
23444         Roo.get(this.iframe.contentDocument.head).createChild({
23445             tag : 'style',
23446             type : 'text/css',
23447             html : style
23448         });
23449
23450         return;
23451     }
23452     
23453     // hide stuff that is not compatible
23454     /**
23455      * @event blur
23456      * @hide
23457      */
23458     /**
23459      * @event change
23460      * @hide
23461      */
23462     /**
23463      * @event focus
23464      * @hide
23465      */
23466     /**
23467      * @event specialkey
23468      * @hide
23469      */
23470     /**
23471      * @cfg {String} fieldClass @hide
23472      */
23473     /**
23474      * @cfg {String} focusClass @hide
23475      */
23476     /**
23477      * @cfg {String} autoCreate @hide
23478      */
23479     /**
23480      * @cfg {String} inputType @hide
23481      */
23482     /**
23483      * @cfg {String} invalidClass @hide
23484      */
23485     /**
23486      * @cfg {String} invalidText @hide
23487      */
23488     /**
23489      * @cfg {String} msgFx @hide
23490      */
23491     /**
23492      * @cfg {String} validateOnBlur @hide
23493      */
23494 });
23495
23496 Roo.HtmlEditorCore.white = [
23497         'area', 'br', 'img', 'input', 'hr', 'wbr',
23498         
23499        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23500        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23501        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23502        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23503        'table',   'ul',         'xmp', 
23504        
23505        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23506       'thead',   'tr', 
23507      
23508       'dir', 'menu', 'ol', 'ul', 'dl',
23509        
23510       'embed',  'object'
23511 ];
23512
23513
23514 Roo.HtmlEditorCore.black = [
23515     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23516         'applet', // 
23517         'base',   'basefont', 'bgsound', 'blink',  'body', 
23518         'frame',  'frameset', 'head',    'html',   'ilayer', 
23519         'iframe', 'layer',  'link',     'meta',    'object',   
23520         'script', 'style' ,'title',  'xml' // clean later..
23521 ];
23522 Roo.HtmlEditorCore.clean = [
23523     'script', 'style', 'title', 'xml'
23524 ];
23525 Roo.HtmlEditorCore.remove = [
23526     'font'
23527 ];
23528 // attributes..
23529
23530 Roo.HtmlEditorCore.ablack = [
23531     'on'
23532 ];
23533     
23534 Roo.HtmlEditorCore.aclean = [ 
23535     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23536 ];
23537
23538 // protocols..
23539 Roo.HtmlEditorCore.pwhite= [
23540         'http',  'https',  'mailto'
23541 ];
23542
23543 // white listed style attributes.
23544 Roo.HtmlEditorCore.cwhite= [
23545       //  'text-align', /// default is to allow most things..
23546       
23547          
23548 //        'font-size'//??
23549 ];
23550
23551 // black listed style attributes.
23552 Roo.HtmlEditorCore.cblack= [
23553       //  'font-size' -- this can be set by the project 
23554 ];
23555
23556
23557 Roo.HtmlEditorCore.swapCodes   =[ 
23558     [    8211, "--" ], 
23559     [    8212, "--" ], 
23560     [    8216,  "'" ],  
23561     [    8217, "'" ],  
23562     [    8220, '"' ],  
23563     [    8221, '"' ],  
23564     [    8226, "*" ],  
23565     [    8230, "..." ]
23566 ]; 
23567
23568     /*
23569  * - LGPL
23570  *
23571  * HtmlEditor
23572  * 
23573  */
23574
23575 /**
23576  * @class Roo.bootstrap.HtmlEditor
23577  * @extends Roo.bootstrap.TextArea
23578  * Bootstrap HtmlEditor class
23579
23580  * @constructor
23581  * Create a new HtmlEditor
23582  * @param {Object} config The config object
23583  */
23584
23585 Roo.bootstrap.HtmlEditor = function(config){
23586     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23587     if (!this.toolbars) {
23588         this.toolbars = [];
23589     }
23590     
23591     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23592     this.addEvents({
23593             /**
23594              * @event initialize
23595              * Fires when the editor is fully initialized (including the iframe)
23596              * @param {HtmlEditor} this
23597              */
23598             initialize: true,
23599             /**
23600              * @event activate
23601              * Fires when the editor is first receives the focus. Any insertion must wait
23602              * until after this event.
23603              * @param {HtmlEditor} this
23604              */
23605             activate: true,
23606              /**
23607              * @event beforesync
23608              * Fires before the textarea is updated with content from the editor iframe. Return false
23609              * to cancel the sync.
23610              * @param {HtmlEditor} this
23611              * @param {String} html
23612              */
23613             beforesync: true,
23614              /**
23615              * @event beforepush
23616              * Fires before the iframe editor is updated with content from the textarea. Return false
23617              * to cancel the push.
23618              * @param {HtmlEditor} this
23619              * @param {String} html
23620              */
23621             beforepush: true,
23622              /**
23623              * @event sync
23624              * Fires when the textarea is updated with content from the editor iframe.
23625              * @param {HtmlEditor} this
23626              * @param {String} html
23627              */
23628             sync: true,
23629              /**
23630              * @event push
23631              * Fires when the iframe editor is updated with content from the textarea.
23632              * @param {HtmlEditor} this
23633              * @param {String} html
23634              */
23635             push: true,
23636              /**
23637              * @event editmodechange
23638              * Fires when the editor switches edit modes
23639              * @param {HtmlEditor} this
23640              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23641              */
23642             editmodechange: true,
23643             /**
23644              * @event editorevent
23645              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23646              * @param {HtmlEditor} this
23647              */
23648             editorevent: true,
23649             /**
23650              * @event firstfocus
23651              * Fires when on first focus - needed by toolbars..
23652              * @param {HtmlEditor} this
23653              */
23654             firstfocus: true,
23655             /**
23656              * @event autosave
23657              * Auto save the htmlEditor value as a file into Events
23658              * @param {HtmlEditor} this
23659              */
23660             autosave: true,
23661             /**
23662              * @event savedpreview
23663              * preview the saved version of htmlEditor
23664              * @param {HtmlEditor} this
23665              */
23666             savedpreview: true
23667         });
23668 };
23669
23670
23671 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23672     
23673     
23674       /**
23675      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23676      */
23677     toolbars : false,
23678     
23679      /**
23680     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23681     */
23682     btns : [],
23683    
23684      /**
23685      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23686      *                        Roo.resizable.
23687      */
23688     resizable : false,
23689      /**
23690      * @cfg {Number} height (in pixels)
23691      */   
23692     height: 300,
23693    /**
23694      * @cfg {Number} width (in pixels)
23695      */   
23696     width: false,
23697     
23698     /**
23699      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23700      * 
23701      */
23702     stylesheets: false,
23703     
23704     // id of frame..
23705     frameId: false,
23706     
23707     // private properties
23708     validationEvent : false,
23709     deferHeight: true,
23710     initialized : false,
23711     activated : false,
23712     
23713     onFocus : Roo.emptyFn,
23714     iframePad:3,
23715     hideMode:'offsets',
23716     
23717     tbContainer : false,
23718     
23719     bodyCls : '',
23720     
23721     toolbarContainer :function() {
23722         return this.wrap.select('.x-html-editor-tb',true).first();
23723     },
23724
23725     /**
23726      * Protected method that will not generally be called directly. It
23727      * is called when the editor creates its toolbar. Override this method if you need to
23728      * add custom toolbar buttons.
23729      * @param {HtmlEditor} editor
23730      */
23731     createToolbar : function(){
23732         Roo.log('renewing');
23733         Roo.log("create toolbars");
23734         
23735         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23736         this.toolbars[0].render(this.toolbarContainer());
23737         
23738         return;
23739         
23740 //        if (!editor.toolbars || !editor.toolbars.length) {
23741 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23742 //        }
23743 //        
23744 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23745 //            editor.toolbars[i] = Roo.factory(
23746 //                    typeof(editor.toolbars[i]) == 'string' ?
23747 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23748 //                Roo.bootstrap.HtmlEditor);
23749 //            editor.toolbars[i].init(editor);
23750 //        }
23751     },
23752
23753      
23754     // private
23755     onRender : function(ct, position)
23756     {
23757        // Roo.log("Call onRender: " + this.xtype);
23758         var _t = this;
23759         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23760       
23761         this.wrap = this.inputEl().wrap({
23762             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23763         });
23764         
23765         this.editorcore.onRender(ct, position);
23766          
23767         if (this.resizable) {
23768             this.resizeEl = new Roo.Resizable(this.wrap, {
23769                 pinned : true,
23770                 wrap: true,
23771                 dynamic : true,
23772                 minHeight : this.height,
23773                 height: this.height,
23774                 handles : this.resizable,
23775                 width: this.width,
23776                 listeners : {
23777                     resize : function(r, w, h) {
23778                         _t.onResize(w,h); // -something
23779                     }
23780                 }
23781             });
23782             
23783         }
23784         this.createToolbar(this);
23785        
23786         
23787         if(!this.width && this.resizable){
23788             this.setSize(this.wrap.getSize());
23789         }
23790         if (this.resizeEl) {
23791             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23792             // should trigger onReize..
23793         }
23794         
23795     },
23796
23797     // private
23798     onResize : function(w, h)
23799     {
23800         Roo.log('resize: ' +w + ',' + h );
23801         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23802         var ew = false;
23803         var eh = false;
23804         
23805         if(this.inputEl() ){
23806             if(typeof w == 'number'){
23807                 var aw = w - this.wrap.getFrameWidth('lr');
23808                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23809                 ew = aw;
23810             }
23811             if(typeof h == 'number'){
23812                  var tbh = -11;  // fixme it needs to tool bar size!
23813                 for (var i =0; i < this.toolbars.length;i++) {
23814                     // fixme - ask toolbars for heights?
23815                     tbh += this.toolbars[i].el.getHeight();
23816                     //if (this.toolbars[i].footer) {
23817                     //    tbh += this.toolbars[i].footer.el.getHeight();
23818                     //}
23819                 }
23820               
23821                 
23822                 
23823                 
23824                 
23825                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23826                 ah -= 5; // knock a few pixes off for look..
23827                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23828                 var eh = ah;
23829             }
23830         }
23831         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23832         this.editorcore.onResize(ew,eh);
23833         
23834     },
23835
23836     /**
23837      * Toggles the editor between standard and source edit mode.
23838      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23839      */
23840     toggleSourceEdit : function(sourceEditMode)
23841     {
23842         this.editorcore.toggleSourceEdit(sourceEditMode);
23843         
23844         if(this.editorcore.sourceEditMode){
23845             Roo.log('editor - showing textarea');
23846             
23847 //            Roo.log('in');
23848 //            Roo.log(this.syncValue());
23849             this.syncValue();
23850             this.inputEl().removeClass(['hide', 'x-hidden']);
23851             this.inputEl().dom.removeAttribute('tabIndex');
23852             this.inputEl().focus();
23853         }else{
23854             Roo.log('editor - hiding textarea');
23855 //            Roo.log('out')
23856 //            Roo.log(this.pushValue()); 
23857             this.pushValue();
23858             
23859             this.inputEl().addClass(['hide', 'x-hidden']);
23860             this.inputEl().dom.setAttribute('tabIndex', -1);
23861             //this.deferFocus();
23862         }
23863          
23864         if(this.resizable){
23865             this.setSize(this.wrap.getSize());
23866         }
23867         
23868         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23869     },
23870  
23871     // private (for BoxComponent)
23872     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23873
23874     // private (for BoxComponent)
23875     getResizeEl : function(){
23876         return this.wrap;
23877     },
23878
23879     // private (for BoxComponent)
23880     getPositionEl : function(){
23881         return this.wrap;
23882     },
23883
23884     // private
23885     initEvents : function(){
23886         this.originalValue = this.getValue();
23887     },
23888
23889 //    /**
23890 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23891 //     * @method
23892 //     */
23893 //    markInvalid : Roo.emptyFn,
23894 //    /**
23895 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23896 //     * @method
23897 //     */
23898 //    clearInvalid : Roo.emptyFn,
23899
23900     setValue : function(v){
23901         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23902         this.editorcore.pushValue();
23903     },
23904
23905      
23906     // private
23907     deferFocus : function(){
23908         this.focus.defer(10, this);
23909     },
23910
23911     // doc'ed in Field
23912     focus : function(){
23913         this.editorcore.focus();
23914         
23915     },
23916       
23917
23918     // private
23919     onDestroy : function(){
23920         
23921         
23922         
23923         if(this.rendered){
23924             
23925             for (var i =0; i < this.toolbars.length;i++) {
23926                 // fixme - ask toolbars for heights?
23927                 this.toolbars[i].onDestroy();
23928             }
23929             
23930             this.wrap.dom.innerHTML = '';
23931             this.wrap.remove();
23932         }
23933     },
23934
23935     // private
23936     onFirstFocus : function(){
23937         //Roo.log("onFirstFocus");
23938         this.editorcore.onFirstFocus();
23939          for (var i =0; i < this.toolbars.length;i++) {
23940             this.toolbars[i].onFirstFocus();
23941         }
23942         
23943     },
23944     
23945     // private
23946     syncValue : function()
23947     {   
23948         this.editorcore.syncValue();
23949     },
23950     
23951     pushValue : function()
23952     {   
23953         this.editorcore.pushValue();
23954     }
23955      
23956     
23957     // hide stuff that is not compatible
23958     /**
23959      * @event blur
23960      * @hide
23961      */
23962     /**
23963      * @event change
23964      * @hide
23965      */
23966     /**
23967      * @event focus
23968      * @hide
23969      */
23970     /**
23971      * @event specialkey
23972      * @hide
23973      */
23974     /**
23975      * @cfg {String} fieldClass @hide
23976      */
23977     /**
23978      * @cfg {String} focusClass @hide
23979      */
23980     /**
23981      * @cfg {String} autoCreate @hide
23982      */
23983     /**
23984      * @cfg {String} inputType @hide
23985      */
23986      
23987     /**
23988      * @cfg {String} invalidText @hide
23989      */
23990     /**
23991      * @cfg {String} msgFx @hide
23992      */
23993     /**
23994      * @cfg {String} validateOnBlur @hide
23995      */
23996 });
23997  
23998     
23999    
24000    
24001    
24002       
24003 Roo.namespace('Roo.bootstrap.htmleditor');
24004 /**
24005  * @class Roo.bootstrap.HtmlEditorToolbar1
24006  * Basic Toolbar
24007  * 
24008  * Usage:
24009  *
24010  new Roo.bootstrap.HtmlEditor({
24011     ....
24012     toolbars : [
24013         new Roo.bootstrap.HtmlEditorToolbar1({
24014             disable : { fonts: 1 , format: 1, ..., ... , ...],
24015             btns : [ .... ]
24016         })
24017     }
24018      
24019  * 
24020  * @cfg {Object} disable List of elements to disable..
24021  * @cfg {Array} btns List of additional buttons.
24022  * 
24023  * 
24024  * NEEDS Extra CSS? 
24025  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24026  */
24027  
24028 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24029 {
24030     
24031     Roo.apply(this, config);
24032     
24033     // default disabled, based on 'good practice'..
24034     this.disable = this.disable || {};
24035     Roo.applyIf(this.disable, {
24036         fontSize : true,
24037         colors : true,
24038         specialElements : true
24039     });
24040     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24041     
24042     this.editor = config.editor;
24043     this.editorcore = config.editor.editorcore;
24044     
24045     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24046     
24047     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24048     // dont call parent... till later.
24049 }
24050 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
24051      
24052     bar : true,
24053     
24054     editor : false,
24055     editorcore : false,
24056     
24057     
24058     formats : [
24059         "p" ,  
24060         "h1","h2","h3","h4","h5","h6", 
24061         "pre", "code", 
24062         "abbr", "acronym", "address", "cite", "samp", "var",
24063         'div','span'
24064     ],
24065     
24066     onRender : function(ct, position)
24067     {
24068        // Roo.log("Call onRender: " + this.xtype);
24069         
24070        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24071        Roo.log(this.el);
24072        this.el.dom.style.marginBottom = '0';
24073        var _this = this;
24074        var editorcore = this.editorcore;
24075        var editor= this.editor;
24076        
24077        var children = [];
24078        var btn = function(id,cmd , toggle, handler, html){
24079        
24080             var  event = toggle ? 'toggle' : 'click';
24081        
24082             var a = {
24083                 size : 'sm',
24084                 xtype: 'Button',
24085                 xns: Roo.bootstrap,
24086                 //glyphicon : id,
24087                 fa: id,
24088                 cmd : id || cmd,
24089                 enableToggle:toggle !== false,
24090                 html : html || '',
24091                 pressed : toggle ? false : null,
24092                 listeners : {}
24093             };
24094             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24095                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24096             };
24097             children.push(a);
24098             return a;
24099        }
24100        
24101     //    var cb_box = function...
24102         
24103         var style = {
24104                 xtype: 'Button',
24105                 size : 'sm',
24106                 xns: Roo.bootstrap,
24107                 fa : 'font',
24108                 //html : 'submit'
24109                 menu : {
24110                     xtype: 'Menu',
24111                     xns: Roo.bootstrap,
24112                     items:  []
24113                 }
24114         };
24115         Roo.each(this.formats, function(f) {
24116             style.menu.items.push({
24117                 xtype :'MenuItem',
24118                 xns: Roo.bootstrap,
24119                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24120                 tagname : f,
24121                 listeners : {
24122                     click : function()
24123                     {
24124                         editorcore.insertTag(this.tagname);
24125                         editor.focus();
24126                     }
24127                 }
24128                 
24129             });
24130         });
24131         children.push(style);   
24132         
24133         btn('bold',false,true);
24134         btn('italic',false,true);
24135         btn('align-left', 'justifyleft',true);
24136         btn('align-center', 'justifycenter',true);
24137         btn('align-right' , 'justifyright',true);
24138         btn('link', false, false, function(btn) {
24139             //Roo.log("create link?");
24140             var url = prompt(this.createLinkText, this.defaultLinkValue);
24141             if(url && url != 'http:/'+'/'){
24142                 this.editorcore.relayCmd('createlink', url);
24143             }
24144         }),
24145         btn('list','insertunorderedlist',true);
24146         btn('pencil', false,true, function(btn){
24147                 Roo.log(this);
24148                 this.toggleSourceEdit(btn.pressed);
24149         });
24150         
24151         if (this.editor.btns.length > 0) {
24152             for (var i = 0; i<this.editor.btns.length; i++) {
24153                 children.push(this.editor.btns[i]);
24154             }
24155         }
24156         
24157         /*
24158         var cog = {
24159                 xtype: 'Button',
24160                 size : 'sm',
24161                 xns: Roo.bootstrap,
24162                 glyphicon : 'cog',
24163                 //html : 'submit'
24164                 menu : {
24165                     xtype: 'Menu',
24166                     xns: Roo.bootstrap,
24167                     items:  []
24168                 }
24169         };
24170         
24171         cog.menu.items.push({
24172             xtype :'MenuItem',
24173             xns: Roo.bootstrap,
24174             html : Clean styles,
24175             tagname : f,
24176             listeners : {
24177                 click : function()
24178                 {
24179                     editorcore.insertTag(this.tagname);
24180                     editor.focus();
24181                 }
24182             }
24183             
24184         });
24185        */
24186         
24187          
24188        this.xtype = 'NavSimplebar';
24189         
24190         for(var i=0;i< children.length;i++) {
24191             
24192             this.buttons.add(this.addxtypeChild(children[i]));
24193             
24194         }
24195         
24196         editor.on('editorevent', this.updateToolbar, this);
24197     },
24198     onBtnClick : function(id)
24199     {
24200        this.editorcore.relayCmd(id);
24201        this.editorcore.focus();
24202     },
24203     
24204     /**
24205      * Protected method that will not generally be called directly. It triggers
24206      * a toolbar update by reading the markup state of the current selection in the editor.
24207      */
24208     updateToolbar: function(){
24209
24210         if(!this.editorcore.activated){
24211             this.editor.onFirstFocus(); // is this neeed?
24212             return;
24213         }
24214
24215         var btns = this.buttons; 
24216         var doc = this.editorcore.doc;
24217         btns.get('bold').setActive(doc.queryCommandState('bold'));
24218         btns.get('italic').setActive(doc.queryCommandState('italic'));
24219         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24220         
24221         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24222         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24223         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24224         
24225         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24226         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24227          /*
24228         
24229         var ans = this.editorcore.getAllAncestors();
24230         if (this.formatCombo) {
24231             
24232             
24233             var store = this.formatCombo.store;
24234             this.formatCombo.setValue("");
24235             for (var i =0; i < ans.length;i++) {
24236                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24237                     // select it..
24238                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24239                     break;
24240                 }
24241             }
24242         }
24243         
24244         
24245         
24246         // hides menus... - so this cant be on a menu...
24247         Roo.bootstrap.MenuMgr.hideAll();
24248         */
24249         Roo.bootstrap.MenuMgr.hideAll();
24250         //this.editorsyncValue();
24251     },
24252     onFirstFocus: function() {
24253         this.buttons.each(function(item){
24254            item.enable();
24255         });
24256     },
24257     toggleSourceEdit : function(sourceEditMode){
24258         
24259           
24260         if(sourceEditMode){
24261             Roo.log("disabling buttons");
24262            this.buttons.each( function(item){
24263                 if(item.cmd != 'pencil'){
24264                     item.disable();
24265                 }
24266             });
24267           
24268         }else{
24269             Roo.log("enabling buttons");
24270             if(this.editorcore.initialized){
24271                 this.buttons.each( function(item){
24272                     item.enable();
24273                 });
24274             }
24275             
24276         }
24277         Roo.log("calling toggole on editor");
24278         // tell the editor that it's been pressed..
24279         this.editor.toggleSourceEdit(sourceEditMode);
24280        
24281     }
24282 });
24283
24284
24285
24286
24287
24288 /**
24289  * @class Roo.bootstrap.Table.AbstractSelectionModel
24290  * @extends Roo.util.Observable
24291  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24292  * implemented by descendant classes.  This class should not be directly instantiated.
24293  * @constructor
24294  */
24295 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24296     this.locked = false;
24297     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24298 };
24299
24300
24301 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24302     /** @ignore Called by the grid automatically. Do not call directly. */
24303     init : function(grid){
24304         this.grid = grid;
24305         this.initEvents();
24306     },
24307
24308     /**
24309      * Locks the selections.
24310      */
24311     lock : function(){
24312         this.locked = true;
24313     },
24314
24315     /**
24316      * Unlocks the selections.
24317      */
24318     unlock : function(){
24319         this.locked = false;
24320     },
24321
24322     /**
24323      * Returns true if the selections are locked.
24324      * @return {Boolean}
24325      */
24326     isLocked : function(){
24327         return this.locked;
24328     }
24329 });
24330 /**
24331  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24332  * @class Roo.bootstrap.Table.RowSelectionModel
24333  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24334  * It supports multiple selections and keyboard selection/navigation. 
24335  * @constructor
24336  * @param {Object} config
24337  */
24338
24339 Roo.bootstrap.Table.RowSelectionModel = function(config){
24340     Roo.apply(this, config);
24341     this.selections = new Roo.util.MixedCollection(false, function(o){
24342         return o.id;
24343     });
24344
24345     this.last = false;
24346     this.lastActive = false;
24347
24348     this.addEvents({
24349         /**
24350              * @event selectionchange
24351              * Fires when the selection changes
24352              * @param {SelectionModel} this
24353              */
24354             "selectionchange" : true,
24355         /**
24356              * @event afterselectionchange
24357              * Fires after the selection changes (eg. by key press or clicking)
24358              * @param {SelectionModel} this
24359              */
24360             "afterselectionchange" : true,
24361         /**
24362              * @event beforerowselect
24363              * Fires when a row is selected being selected, return false to cancel.
24364              * @param {SelectionModel} this
24365              * @param {Number} rowIndex The selected index
24366              * @param {Boolean} keepExisting False if other selections will be cleared
24367              */
24368             "beforerowselect" : true,
24369         /**
24370              * @event rowselect
24371              * Fires when a row is selected.
24372              * @param {SelectionModel} this
24373              * @param {Number} rowIndex The selected index
24374              * @param {Roo.data.Record} r The record
24375              */
24376             "rowselect" : true,
24377         /**
24378              * @event rowdeselect
24379              * Fires when a row is deselected.
24380              * @param {SelectionModel} this
24381              * @param {Number} rowIndex The selected index
24382              */
24383         "rowdeselect" : true
24384     });
24385     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24386     this.locked = false;
24387  };
24388
24389 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24390     /**
24391      * @cfg {Boolean} singleSelect
24392      * True to allow selection of only one row at a time (defaults to false)
24393      */
24394     singleSelect : false,
24395
24396     // private
24397     initEvents : function()
24398     {
24399
24400         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24401         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24402         //}else{ // allow click to work like normal
24403          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24404         //}
24405         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24406         this.grid.on("rowclick", this.handleMouseDown, this);
24407         
24408         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24409             "up" : function(e){
24410                 if(!e.shiftKey){
24411                     this.selectPrevious(e.shiftKey);
24412                 }else if(this.last !== false && this.lastActive !== false){
24413                     var last = this.last;
24414                     this.selectRange(this.last,  this.lastActive-1);
24415                     this.grid.getView().focusRow(this.lastActive);
24416                     if(last !== false){
24417                         this.last = last;
24418                     }
24419                 }else{
24420                     this.selectFirstRow();
24421                 }
24422                 this.fireEvent("afterselectionchange", this);
24423             },
24424             "down" : function(e){
24425                 if(!e.shiftKey){
24426                     this.selectNext(e.shiftKey);
24427                 }else if(this.last !== false && this.lastActive !== false){
24428                     var last = this.last;
24429                     this.selectRange(this.last,  this.lastActive+1);
24430                     this.grid.getView().focusRow(this.lastActive);
24431                     if(last !== false){
24432                         this.last = last;
24433                     }
24434                 }else{
24435                     this.selectFirstRow();
24436                 }
24437                 this.fireEvent("afterselectionchange", this);
24438             },
24439             scope: this
24440         });
24441         this.grid.store.on('load', function(){
24442             this.selections.clear();
24443         },this);
24444         /*
24445         var view = this.grid.view;
24446         view.on("refresh", this.onRefresh, this);
24447         view.on("rowupdated", this.onRowUpdated, this);
24448         view.on("rowremoved", this.onRemove, this);
24449         */
24450     },
24451
24452     // private
24453     onRefresh : function()
24454     {
24455         var ds = this.grid.store, i, v = this.grid.view;
24456         var s = this.selections;
24457         s.each(function(r){
24458             if((i = ds.indexOfId(r.id)) != -1){
24459                 v.onRowSelect(i);
24460             }else{
24461                 s.remove(r);
24462             }
24463         });
24464     },
24465
24466     // private
24467     onRemove : function(v, index, r){
24468         this.selections.remove(r);
24469     },
24470
24471     // private
24472     onRowUpdated : function(v, index, r){
24473         if(this.isSelected(r)){
24474             v.onRowSelect(index);
24475         }
24476     },
24477
24478     /**
24479      * Select records.
24480      * @param {Array} records The records to select
24481      * @param {Boolean} keepExisting (optional) True to keep existing selections
24482      */
24483     selectRecords : function(records, keepExisting)
24484     {
24485         if(!keepExisting){
24486             this.clearSelections();
24487         }
24488             var ds = this.grid.store;
24489         for(var i = 0, len = records.length; i < len; i++){
24490             this.selectRow(ds.indexOf(records[i]), true);
24491         }
24492     },
24493
24494     /**
24495      * Gets the number of selected rows.
24496      * @return {Number}
24497      */
24498     getCount : function(){
24499         return this.selections.length;
24500     },
24501
24502     /**
24503      * Selects the first row in the grid.
24504      */
24505     selectFirstRow : function(){
24506         this.selectRow(0);
24507     },
24508
24509     /**
24510      * Select the last row.
24511      * @param {Boolean} keepExisting (optional) True to keep existing selections
24512      */
24513     selectLastRow : function(keepExisting){
24514         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24515         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24516     },
24517
24518     /**
24519      * Selects the row immediately following the last selected row.
24520      * @param {Boolean} keepExisting (optional) True to keep existing selections
24521      */
24522     selectNext : function(keepExisting)
24523     {
24524             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24525             this.selectRow(this.last+1, keepExisting);
24526             this.grid.getView().focusRow(this.last);
24527         }
24528     },
24529
24530     /**
24531      * Selects the row that precedes the last selected row.
24532      * @param {Boolean} keepExisting (optional) True to keep existing selections
24533      */
24534     selectPrevious : function(keepExisting){
24535         if(this.last){
24536             this.selectRow(this.last-1, keepExisting);
24537             this.grid.getView().focusRow(this.last);
24538         }
24539     },
24540
24541     /**
24542      * Returns the selected records
24543      * @return {Array} Array of selected records
24544      */
24545     getSelections : function(){
24546         return [].concat(this.selections.items);
24547     },
24548
24549     /**
24550      * Returns the first selected record.
24551      * @return {Record}
24552      */
24553     getSelected : function(){
24554         return this.selections.itemAt(0);
24555     },
24556
24557
24558     /**
24559      * Clears all selections.
24560      */
24561     clearSelections : function(fast)
24562     {
24563         if(this.locked) {
24564             return;
24565         }
24566         if(fast !== true){
24567                 var ds = this.grid.store;
24568             var s = this.selections;
24569             s.each(function(r){
24570                 this.deselectRow(ds.indexOfId(r.id));
24571             }, this);
24572             s.clear();
24573         }else{
24574             this.selections.clear();
24575         }
24576         this.last = false;
24577     },
24578
24579
24580     /**
24581      * Selects all rows.
24582      */
24583     selectAll : function(){
24584         if(this.locked) {
24585             return;
24586         }
24587         this.selections.clear();
24588         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24589             this.selectRow(i, true);
24590         }
24591     },
24592
24593     /**
24594      * Returns True if there is a selection.
24595      * @return {Boolean}
24596      */
24597     hasSelection : function(){
24598         return this.selections.length > 0;
24599     },
24600
24601     /**
24602      * Returns True if the specified row is selected.
24603      * @param {Number/Record} record The record or index of the record to check
24604      * @return {Boolean}
24605      */
24606     isSelected : function(index){
24607             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24608         return (r && this.selections.key(r.id) ? true : false);
24609     },
24610
24611     /**
24612      * Returns True if the specified record id is selected.
24613      * @param {String} id The id of record to check
24614      * @return {Boolean}
24615      */
24616     isIdSelected : function(id){
24617         return (this.selections.key(id) ? true : false);
24618     },
24619
24620
24621     // private
24622     handleMouseDBClick : function(e, t){
24623         
24624     },
24625     // private
24626     handleMouseDown : function(e, t)
24627     {
24628             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24629         if(this.isLocked() || rowIndex < 0 ){
24630             return;
24631         };
24632         if(e.shiftKey && this.last !== false){
24633             var last = this.last;
24634             this.selectRange(last, rowIndex, e.ctrlKey);
24635             this.last = last; // reset the last
24636             t.focus();
24637     
24638         }else{
24639             var isSelected = this.isSelected(rowIndex);
24640             //Roo.log("select row:" + rowIndex);
24641             if(isSelected){
24642                 this.deselectRow(rowIndex);
24643             } else {
24644                         this.selectRow(rowIndex, true);
24645             }
24646     
24647             /*
24648                 if(e.button !== 0 && isSelected){
24649                 alert('rowIndex 2: ' + rowIndex);
24650                     view.focusRow(rowIndex);
24651                 }else if(e.ctrlKey && isSelected){
24652                     this.deselectRow(rowIndex);
24653                 }else if(!isSelected){
24654                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24655                     view.focusRow(rowIndex);
24656                 }
24657             */
24658         }
24659         this.fireEvent("afterselectionchange", this);
24660     },
24661     // private
24662     handleDragableRowClick :  function(grid, rowIndex, e) 
24663     {
24664         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24665             this.selectRow(rowIndex, false);
24666             grid.view.focusRow(rowIndex);
24667              this.fireEvent("afterselectionchange", this);
24668         }
24669     },
24670     
24671     /**
24672      * Selects multiple rows.
24673      * @param {Array} rows Array of the indexes of the row to select
24674      * @param {Boolean} keepExisting (optional) True to keep existing selections
24675      */
24676     selectRows : function(rows, keepExisting){
24677         if(!keepExisting){
24678             this.clearSelections();
24679         }
24680         for(var i = 0, len = rows.length; i < len; i++){
24681             this.selectRow(rows[i], true);
24682         }
24683     },
24684
24685     /**
24686      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24687      * @param {Number} startRow The index of the first row in the range
24688      * @param {Number} endRow The index of the last row in the range
24689      * @param {Boolean} keepExisting (optional) True to retain existing selections
24690      */
24691     selectRange : function(startRow, endRow, keepExisting){
24692         if(this.locked) {
24693             return;
24694         }
24695         if(!keepExisting){
24696             this.clearSelections();
24697         }
24698         if(startRow <= endRow){
24699             for(var i = startRow; i <= endRow; i++){
24700                 this.selectRow(i, true);
24701             }
24702         }else{
24703             for(var i = startRow; i >= endRow; i--){
24704                 this.selectRow(i, true);
24705             }
24706         }
24707     },
24708
24709     /**
24710      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24711      * @param {Number} startRow The index of the first row in the range
24712      * @param {Number} endRow The index of the last row in the range
24713      */
24714     deselectRange : function(startRow, endRow, preventViewNotify){
24715         if(this.locked) {
24716             return;
24717         }
24718         for(var i = startRow; i <= endRow; i++){
24719             this.deselectRow(i, preventViewNotify);
24720         }
24721     },
24722
24723     /**
24724      * Selects a row.
24725      * @param {Number} row The index of the row to select
24726      * @param {Boolean} keepExisting (optional) True to keep existing selections
24727      */
24728     selectRow : function(index, keepExisting, preventViewNotify)
24729     {
24730             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24731             return;
24732         }
24733         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24734             if(!keepExisting || this.singleSelect){
24735                 this.clearSelections();
24736             }
24737             
24738             var r = this.grid.store.getAt(index);
24739             //console.log('selectRow - record id :' + r.id);
24740             
24741             this.selections.add(r);
24742             this.last = this.lastActive = index;
24743             if(!preventViewNotify){
24744                 var proxy = new Roo.Element(
24745                                 this.grid.getRowDom(index)
24746                 );
24747                 proxy.addClass('bg-info info');
24748             }
24749             this.fireEvent("rowselect", this, index, r);
24750             this.fireEvent("selectionchange", this);
24751         }
24752     },
24753
24754     /**
24755      * Deselects a row.
24756      * @param {Number} row The index of the row to deselect
24757      */
24758     deselectRow : function(index, preventViewNotify)
24759     {
24760         if(this.locked) {
24761             return;
24762         }
24763         if(this.last == index){
24764             this.last = false;
24765         }
24766         if(this.lastActive == index){
24767             this.lastActive = false;
24768         }
24769         
24770         var r = this.grid.store.getAt(index);
24771         if (!r) {
24772             return;
24773         }
24774         
24775         this.selections.remove(r);
24776         //.console.log('deselectRow - record id :' + r.id);
24777         if(!preventViewNotify){
24778         
24779             var proxy = new Roo.Element(
24780                 this.grid.getRowDom(index)
24781             );
24782             proxy.removeClass('bg-info info');
24783         }
24784         this.fireEvent("rowdeselect", this, index);
24785         this.fireEvent("selectionchange", this);
24786     },
24787
24788     // private
24789     restoreLast : function(){
24790         if(this._last){
24791             this.last = this._last;
24792         }
24793     },
24794
24795     // private
24796     acceptsNav : function(row, col, cm){
24797         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24798     },
24799
24800     // private
24801     onEditorKey : function(field, e){
24802         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24803         if(k == e.TAB){
24804             e.stopEvent();
24805             ed.completeEdit();
24806             if(e.shiftKey){
24807                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24808             }else{
24809                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24810             }
24811         }else if(k == e.ENTER && !e.ctrlKey){
24812             e.stopEvent();
24813             ed.completeEdit();
24814             if(e.shiftKey){
24815                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24816             }else{
24817                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24818             }
24819         }else if(k == e.ESC){
24820             ed.cancelEdit();
24821         }
24822         if(newCell){
24823             g.startEditing(newCell[0], newCell[1]);
24824         }
24825     }
24826 });
24827 /*
24828  * Based on:
24829  * Ext JS Library 1.1.1
24830  * Copyright(c) 2006-2007, Ext JS, LLC.
24831  *
24832  * Originally Released Under LGPL - original licence link has changed is not relivant.
24833  *
24834  * Fork - LGPL
24835  * <script type="text/javascript">
24836  */
24837  
24838 /**
24839  * @class Roo.bootstrap.PagingToolbar
24840  * @extends Roo.bootstrap.NavSimplebar
24841  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24842  * @constructor
24843  * Create a new PagingToolbar
24844  * @param {Object} config The config object
24845  * @param {Roo.data.Store} store
24846  */
24847 Roo.bootstrap.PagingToolbar = function(config)
24848 {
24849     // old args format still supported... - xtype is prefered..
24850         // created from xtype...
24851     
24852     this.ds = config.dataSource;
24853     
24854     if (config.store && !this.ds) {
24855         this.store= Roo.factory(config.store, Roo.data);
24856         this.ds = this.store;
24857         this.ds.xmodule = this.xmodule || false;
24858     }
24859     
24860     this.toolbarItems = [];
24861     if (config.items) {
24862         this.toolbarItems = config.items;
24863     }
24864     
24865     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24866     
24867     this.cursor = 0;
24868     
24869     if (this.ds) { 
24870         this.bind(this.ds);
24871     }
24872     
24873     if (Roo.bootstrap.version == 4) {
24874         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24875     } else {
24876         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24877     }
24878     
24879 };
24880
24881 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24882     /**
24883      * @cfg {Roo.data.Store} dataSource
24884      * The underlying data store providing the paged data
24885      */
24886     /**
24887      * @cfg {String/HTMLElement/Element} container
24888      * container The id or element that will contain the toolbar
24889      */
24890     /**
24891      * @cfg {Boolean} displayInfo
24892      * True to display the displayMsg (defaults to false)
24893      */
24894     /**
24895      * @cfg {Number} pageSize
24896      * The number of records to display per page (defaults to 20)
24897      */
24898     pageSize: 20,
24899     /**
24900      * @cfg {String} displayMsg
24901      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24902      */
24903     displayMsg : 'Displaying {0} - {1} of {2}',
24904     /**
24905      * @cfg {String} emptyMsg
24906      * The message to display when no records are found (defaults to "No data to display")
24907      */
24908     emptyMsg : 'No data to display',
24909     /**
24910      * Customizable piece of the default paging text (defaults to "Page")
24911      * @type String
24912      */
24913     beforePageText : "Page",
24914     /**
24915      * Customizable piece of the default paging text (defaults to "of %0")
24916      * @type String
24917      */
24918     afterPageText : "of {0}",
24919     /**
24920      * Customizable piece of the default paging text (defaults to "First Page")
24921      * @type String
24922      */
24923     firstText : "First Page",
24924     /**
24925      * Customizable piece of the default paging text (defaults to "Previous Page")
24926      * @type String
24927      */
24928     prevText : "Previous Page",
24929     /**
24930      * Customizable piece of the default paging text (defaults to "Next Page")
24931      * @type String
24932      */
24933     nextText : "Next Page",
24934     /**
24935      * Customizable piece of the default paging text (defaults to "Last Page")
24936      * @type String
24937      */
24938     lastText : "Last Page",
24939     /**
24940      * Customizable piece of the default paging text (defaults to "Refresh")
24941      * @type String
24942      */
24943     refreshText : "Refresh",
24944
24945     buttons : false,
24946     // private
24947     onRender : function(ct, position) 
24948     {
24949         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24950         this.navgroup.parentId = this.id;
24951         this.navgroup.onRender(this.el, null);
24952         // add the buttons to the navgroup
24953         
24954         if(this.displayInfo){
24955             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24956             this.displayEl = this.el.select('.x-paging-info', true).first();
24957 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24958 //            this.displayEl = navel.el.select('span',true).first();
24959         }
24960         
24961         var _this = this;
24962         
24963         if(this.buttons){
24964             Roo.each(_this.buttons, function(e){ // this might need to use render????
24965                Roo.factory(e).render(_this.el);
24966             });
24967         }
24968             
24969         Roo.each(_this.toolbarItems, function(e) {
24970             _this.navgroup.addItem(e);
24971         });
24972         
24973         
24974         this.first = this.navgroup.addItem({
24975             tooltip: this.firstText,
24976             cls: "prev btn-outline-secondary",
24977             html : ' <i class="fa fa-step-backward"></i>',
24978             disabled: true,
24979             preventDefault: true,
24980             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24981         });
24982         
24983         this.prev =  this.navgroup.addItem({
24984             tooltip: this.prevText,
24985             cls: "prev btn-outline-secondary",
24986             html : ' <i class="fa fa-backward"></i>',
24987             disabled: true,
24988             preventDefault: true,
24989             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24990         });
24991     //this.addSeparator();
24992         
24993         
24994         var field = this.navgroup.addItem( {
24995             tagtype : 'span',
24996             cls : 'x-paging-position  btn-outline-secondary',
24997              disabled: true,
24998             html : this.beforePageText  +
24999                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25000                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
25001          } ); //?? escaped?
25002         
25003         this.field = field.el.select('input', true).first();
25004         this.field.on("keydown", this.onPagingKeydown, this);
25005         this.field.on("focus", function(){this.dom.select();});
25006     
25007     
25008         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
25009         //this.field.setHeight(18);
25010         //this.addSeparator();
25011         this.next = this.navgroup.addItem({
25012             tooltip: this.nextText,
25013             cls: "next btn-outline-secondary",
25014             html : ' <i class="fa fa-forward"></i>',
25015             disabled: true,
25016             preventDefault: true,
25017             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
25018         });
25019         this.last = this.navgroup.addItem({
25020             tooltip: this.lastText,
25021             html : ' <i class="fa fa-step-forward"></i>',
25022             cls: "next btn-outline-secondary",
25023             disabled: true,
25024             preventDefault: true,
25025             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
25026         });
25027     //this.addSeparator();
25028         this.loading = this.navgroup.addItem({
25029             tooltip: this.refreshText,
25030             cls: "btn-outline-secondary",
25031             html : ' <i class="fa fa-refresh"></i>',
25032             preventDefault: true,
25033             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25034         });
25035         
25036     },
25037
25038     // private
25039     updateInfo : function(){
25040         if(this.displayEl){
25041             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25042             var msg = count == 0 ?
25043                 this.emptyMsg :
25044                 String.format(
25045                     this.displayMsg,
25046                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
25047                 );
25048             this.displayEl.update(msg);
25049         }
25050     },
25051
25052     // private
25053     onLoad : function(ds, r, o)
25054     {
25055         this.cursor = o.params.start ? o.params.start : 0;
25056         
25057         var d = this.getPageData(),
25058             ap = d.activePage,
25059             ps = d.pages;
25060         
25061         
25062         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25063         this.field.dom.value = ap;
25064         this.first.setDisabled(ap == 1);
25065         this.prev.setDisabled(ap == 1);
25066         this.next.setDisabled(ap == ps);
25067         this.last.setDisabled(ap == ps);
25068         this.loading.enable();
25069         this.updateInfo();
25070     },
25071
25072     // private
25073     getPageData : function(){
25074         var total = this.ds.getTotalCount();
25075         return {
25076             total : total,
25077             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25078             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25079         };
25080     },
25081
25082     // private
25083     onLoadError : function(){
25084         this.loading.enable();
25085     },
25086
25087     // private
25088     onPagingKeydown : function(e){
25089         var k = e.getKey();
25090         var d = this.getPageData();
25091         if(k == e.RETURN){
25092             var v = this.field.dom.value, pageNum;
25093             if(!v || isNaN(pageNum = parseInt(v, 10))){
25094                 this.field.dom.value = d.activePage;
25095                 return;
25096             }
25097             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25098             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25099             e.stopEvent();
25100         }
25101         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))
25102         {
25103           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25104           this.field.dom.value = pageNum;
25105           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25106           e.stopEvent();
25107         }
25108         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25109         {
25110           var v = this.field.dom.value, pageNum; 
25111           var increment = (e.shiftKey) ? 10 : 1;
25112           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25113                 increment *= -1;
25114           }
25115           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25116             this.field.dom.value = d.activePage;
25117             return;
25118           }
25119           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25120           {
25121             this.field.dom.value = parseInt(v, 10) + increment;
25122             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25123             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25124           }
25125           e.stopEvent();
25126         }
25127     },
25128
25129     // private
25130     beforeLoad : function(){
25131         if(this.loading){
25132             this.loading.disable();
25133         }
25134     },
25135
25136     // private
25137     onClick : function(which){
25138         
25139         var ds = this.ds;
25140         if (!ds) {
25141             return;
25142         }
25143         
25144         switch(which){
25145             case "first":
25146                 ds.load({params:{start: 0, limit: this.pageSize}});
25147             break;
25148             case "prev":
25149                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25150             break;
25151             case "next":
25152                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25153             break;
25154             case "last":
25155                 var total = ds.getTotalCount();
25156                 var extra = total % this.pageSize;
25157                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25158                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25159             break;
25160             case "refresh":
25161                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25162             break;
25163         }
25164     },
25165
25166     /**
25167      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25168      * @param {Roo.data.Store} store The data store to unbind
25169      */
25170     unbind : function(ds){
25171         ds.un("beforeload", this.beforeLoad, this);
25172         ds.un("load", this.onLoad, this);
25173         ds.un("loadexception", this.onLoadError, this);
25174         ds.un("remove", this.updateInfo, this);
25175         ds.un("add", this.updateInfo, this);
25176         this.ds = undefined;
25177     },
25178
25179     /**
25180      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25181      * @param {Roo.data.Store} store The data store to bind
25182      */
25183     bind : function(ds){
25184         ds.on("beforeload", this.beforeLoad, this);
25185         ds.on("load", this.onLoad, this);
25186         ds.on("loadexception", this.onLoadError, this);
25187         ds.on("remove", this.updateInfo, this);
25188         ds.on("add", this.updateInfo, this);
25189         this.ds = ds;
25190     }
25191 });/*
25192  * - LGPL
25193  *
25194  * element
25195  * 
25196  */
25197
25198 /**
25199  * @class Roo.bootstrap.MessageBar
25200  * @extends Roo.bootstrap.Component
25201  * Bootstrap MessageBar class
25202  * @cfg {String} html contents of the MessageBar
25203  * @cfg {String} weight (info | success | warning | danger) default info
25204  * @cfg {String} beforeClass insert the bar before the given class
25205  * @cfg {Boolean} closable (true | false) default false
25206  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25207  * 
25208  * @constructor
25209  * Create a new Element
25210  * @param {Object} config The config object
25211  */
25212
25213 Roo.bootstrap.MessageBar = function(config){
25214     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25215 };
25216
25217 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25218     
25219     html: '',
25220     weight: 'info',
25221     closable: false,
25222     fixed: false,
25223     beforeClass: 'bootstrap-sticky-wrap',
25224     
25225     getAutoCreate : function(){
25226         
25227         var cfg = {
25228             tag: 'div',
25229             cls: 'alert alert-dismissable alert-' + this.weight,
25230             cn: [
25231                 {
25232                     tag: 'span',
25233                     cls: 'message',
25234                     html: this.html || ''
25235                 }
25236             ]
25237         };
25238         
25239         if(this.fixed){
25240             cfg.cls += ' alert-messages-fixed';
25241         }
25242         
25243         if(this.closable){
25244             cfg.cn.push({
25245                 tag: 'button',
25246                 cls: 'close',
25247                 html: 'x'
25248             });
25249         }
25250         
25251         return cfg;
25252     },
25253     
25254     onRender : function(ct, position)
25255     {
25256         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25257         
25258         if(!this.el){
25259             var cfg = Roo.apply({},  this.getAutoCreate());
25260             cfg.id = Roo.id();
25261             
25262             if (this.cls) {
25263                 cfg.cls += ' ' + this.cls;
25264             }
25265             if (this.style) {
25266                 cfg.style = this.style;
25267             }
25268             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25269             
25270             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25271         }
25272         
25273         this.el.select('>button.close').on('click', this.hide, this);
25274         
25275     },
25276     
25277     show : function()
25278     {
25279         if (!this.rendered) {
25280             this.render();
25281         }
25282         
25283         this.el.show();
25284         
25285         this.fireEvent('show', this);
25286         
25287     },
25288     
25289     hide : function()
25290     {
25291         if (!this.rendered) {
25292             this.render();
25293         }
25294         
25295         this.el.hide();
25296         
25297         this.fireEvent('hide', this);
25298     },
25299     
25300     update : function()
25301     {
25302 //        var e = this.el.dom.firstChild;
25303 //        
25304 //        if(this.closable){
25305 //            e = e.nextSibling;
25306 //        }
25307 //        
25308 //        e.data = this.html || '';
25309
25310         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25311     }
25312    
25313 });
25314
25315  
25316
25317      /*
25318  * - LGPL
25319  *
25320  * Graph
25321  * 
25322  */
25323
25324
25325 /**
25326  * @class Roo.bootstrap.Graph
25327  * @extends Roo.bootstrap.Component
25328  * Bootstrap Graph class
25329 > Prameters
25330  -sm {number} sm 4
25331  -md {number} md 5
25332  @cfg {String} graphtype  bar | vbar | pie
25333  @cfg {number} g_x coodinator | centre x (pie)
25334  @cfg {number} g_y coodinator | centre y (pie)
25335  @cfg {number} g_r radius (pie)
25336  @cfg {number} g_height height of the chart (respected by all elements in the set)
25337  @cfg {number} g_width width of the chart (respected by all elements in the set)
25338  @cfg {Object} title The title of the chart
25339     
25340  -{Array}  values
25341  -opts (object) options for the chart 
25342      o {
25343      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25344      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25345      o vgutter (number)
25346      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.
25347      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25348      o to
25349      o stretch (boolean)
25350      o }
25351  -opts (object) options for the pie
25352      o{
25353      o cut
25354      o startAngle (number)
25355      o endAngle (number)
25356      } 
25357  *
25358  * @constructor
25359  * Create a new Input
25360  * @param {Object} config The config object
25361  */
25362
25363 Roo.bootstrap.Graph = function(config){
25364     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25365     
25366     this.addEvents({
25367         // img events
25368         /**
25369          * @event click
25370          * The img click event for the img.
25371          * @param {Roo.EventObject} e
25372          */
25373         "click" : true
25374     });
25375 };
25376
25377 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25378     
25379     sm: 4,
25380     md: 5,
25381     graphtype: 'bar',
25382     g_height: 250,
25383     g_width: 400,
25384     g_x: 50,
25385     g_y: 50,
25386     g_r: 30,
25387     opts:{
25388         //g_colors: this.colors,
25389         g_type: 'soft',
25390         g_gutter: '20%'
25391
25392     },
25393     title : false,
25394
25395     getAutoCreate : function(){
25396         
25397         var cfg = {
25398             tag: 'div',
25399             html : null
25400         };
25401         
25402         
25403         return  cfg;
25404     },
25405
25406     onRender : function(ct,position){
25407         
25408         
25409         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25410         
25411         if (typeof(Raphael) == 'undefined') {
25412             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25413             return;
25414         }
25415         
25416         this.raphael = Raphael(this.el.dom);
25417         
25418                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25419                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25420                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25421                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25422                 /*
25423                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25424                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25425                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25426                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25427                 
25428                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25429                 r.barchart(330, 10, 300, 220, data1);
25430                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25431                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25432                 */
25433                 
25434                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25435                 // r.barchart(30, 30, 560, 250,  xdata, {
25436                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25437                 //     axis : "0 0 1 1",
25438                 //     axisxlabels :  xdata
25439                 //     //yvalues : cols,
25440                    
25441                 // });
25442 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25443 //        
25444 //        this.load(null,xdata,{
25445 //                axis : "0 0 1 1",
25446 //                axisxlabels :  xdata
25447 //                });
25448
25449     },
25450
25451     load : function(graphtype,xdata,opts)
25452     {
25453         this.raphael.clear();
25454         if(!graphtype) {
25455             graphtype = this.graphtype;
25456         }
25457         if(!opts){
25458             opts = this.opts;
25459         }
25460         var r = this.raphael,
25461             fin = function () {
25462                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25463             },
25464             fout = function () {
25465                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25466             },
25467             pfin = function() {
25468                 this.sector.stop();
25469                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25470
25471                 if (this.label) {
25472                     this.label[0].stop();
25473                     this.label[0].attr({ r: 7.5 });
25474                     this.label[1].attr({ "font-weight": 800 });
25475                 }
25476             },
25477             pfout = function() {
25478                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25479
25480                 if (this.label) {
25481                     this.label[0].animate({ r: 5 }, 500, "bounce");
25482                     this.label[1].attr({ "font-weight": 400 });
25483                 }
25484             };
25485
25486         switch(graphtype){
25487             case 'bar':
25488                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25489                 break;
25490             case 'hbar':
25491                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25492                 break;
25493             case 'pie':
25494 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25495 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25496 //            
25497                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25498                 
25499                 break;
25500
25501         }
25502         
25503         if(this.title){
25504             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25505         }
25506         
25507     },
25508     
25509     setTitle: function(o)
25510     {
25511         this.title = o;
25512     },
25513     
25514     initEvents: function() {
25515         
25516         if(!this.href){
25517             this.el.on('click', this.onClick, this);
25518         }
25519     },
25520     
25521     onClick : function(e)
25522     {
25523         Roo.log('img onclick');
25524         this.fireEvent('click', this, e);
25525     }
25526    
25527 });
25528
25529  
25530 /*
25531  * - LGPL
25532  *
25533  * numberBox
25534  * 
25535  */
25536 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25537
25538 /**
25539  * @class Roo.bootstrap.dash.NumberBox
25540  * @extends Roo.bootstrap.Component
25541  * Bootstrap NumberBox class
25542  * @cfg {String} headline Box headline
25543  * @cfg {String} content Box content
25544  * @cfg {String} icon Box icon
25545  * @cfg {String} footer Footer text
25546  * @cfg {String} fhref Footer href
25547  * 
25548  * @constructor
25549  * Create a new NumberBox
25550  * @param {Object} config The config object
25551  */
25552
25553
25554 Roo.bootstrap.dash.NumberBox = function(config){
25555     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25556     
25557 };
25558
25559 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25560     
25561     headline : '',
25562     content : '',
25563     icon : '',
25564     footer : '',
25565     fhref : '',
25566     ficon : '',
25567     
25568     getAutoCreate : function(){
25569         
25570         var cfg = {
25571             tag : 'div',
25572             cls : 'small-box ',
25573             cn : [
25574                 {
25575                     tag : 'div',
25576                     cls : 'inner',
25577                     cn :[
25578                         {
25579                             tag : 'h3',
25580                             cls : 'roo-headline',
25581                             html : this.headline
25582                         },
25583                         {
25584                             tag : 'p',
25585                             cls : 'roo-content',
25586                             html : this.content
25587                         }
25588                     ]
25589                 }
25590             ]
25591         };
25592         
25593         if(this.icon){
25594             cfg.cn.push({
25595                 tag : 'div',
25596                 cls : 'icon',
25597                 cn :[
25598                     {
25599                         tag : 'i',
25600                         cls : 'ion ' + this.icon
25601                     }
25602                 ]
25603             });
25604         }
25605         
25606         if(this.footer){
25607             var footer = {
25608                 tag : 'a',
25609                 cls : 'small-box-footer',
25610                 href : this.fhref || '#',
25611                 html : this.footer
25612             };
25613             
25614             cfg.cn.push(footer);
25615             
25616         }
25617         
25618         return  cfg;
25619     },
25620
25621     onRender : function(ct,position){
25622         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25623
25624
25625        
25626                 
25627     },
25628
25629     setHeadline: function (value)
25630     {
25631         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25632     },
25633     
25634     setFooter: function (value, href)
25635     {
25636         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25637         
25638         if(href){
25639             this.el.select('a.small-box-footer',true).first().attr('href', href);
25640         }
25641         
25642     },
25643
25644     setContent: function (value)
25645     {
25646         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25647     },
25648
25649     initEvents: function() 
25650     {   
25651         
25652     }
25653     
25654 });
25655
25656  
25657 /*
25658  * - LGPL
25659  *
25660  * TabBox
25661  * 
25662  */
25663 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25664
25665 /**
25666  * @class Roo.bootstrap.dash.TabBox
25667  * @extends Roo.bootstrap.Component
25668  * Bootstrap TabBox class
25669  * @cfg {String} title Title of the TabBox
25670  * @cfg {String} icon Icon of the TabBox
25671  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25672  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25673  * 
25674  * @constructor
25675  * Create a new TabBox
25676  * @param {Object} config The config object
25677  */
25678
25679
25680 Roo.bootstrap.dash.TabBox = function(config){
25681     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25682     this.addEvents({
25683         // raw events
25684         /**
25685          * @event addpane
25686          * When a pane is added
25687          * @param {Roo.bootstrap.dash.TabPane} pane
25688          */
25689         "addpane" : true,
25690         /**
25691          * @event activatepane
25692          * When a pane is activated
25693          * @param {Roo.bootstrap.dash.TabPane} pane
25694          */
25695         "activatepane" : true
25696         
25697          
25698     });
25699     
25700     this.panes = [];
25701 };
25702
25703 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25704
25705     title : '',
25706     icon : false,
25707     showtabs : true,
25708     tabScrollable : false,
25709     
25710     getChildContainer : function()
25711     {
25712         return this.el.select('.tab-content', true).first();
25713     },
25714     
25715     getAutoCreate : function(){
25716         
25717         var header = {
25718             tag: 'li',
25719             cls: 'pull-left header',
25720             html: this.title,
25721             cn : []
25722         };
25723         
25724         if(this.icon){
25725             header.cn.push({
25726                 tag: 'i',
25727                 cls: 'fa ' + this.icon
25728             });
25729         }
25730         
25731         var h = {
25732             tag: 'ul',
25733             cls: 'nav nav-tabs pull-right',
25734             cn: [
25735                 header
25736             ]
25737         };
25738         
25739         if(this.tabScrollable){
25740             h = {
25741                 tag: 'div',
25742                 cls: 'tab-header',
25743                 cn: [
25744                     {
25745                         tag: 'ul',
25746                         cls: 'nav nav-tabs pull-right',
25747                         cn: [
25748                             header
25749                         ]
25750                     }
25751                 ]
25752             };
25753         }
25754         
25755         var cfg = {
25756             tag: 'div',
25757             cls: 'nav-tabs-custom',
25758             cn: [
25759                 h,
25760                 {
25761                     tag: 'div',
25762                     cls: 'tab-content no-padding',
25763                     cn: []
25764                 }
25765             ]
25766         };
25767
25768         return  cfg;
25769     },
25770     initEvents : function()
25771     {
25772         //Roo.log('add add pane handler');
25773         this.on('addpane', this.onAddPane, this);
25774     },
25775      /**
25776      * Updates the box title
25777      * @param {String} html to set the title to.
25778      */
25779     setTitle : function(value)
25780     {
25781         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25782     },
25783     onAddPane : function(pane)
25784     {
25785         this.panes.push(pane);
25786         //Roo.log('addpane');
25787         //Roo.log(pane);
25788         // tabs are rendere left to right..
25789         if(!this.showtabs){
25790             return;
25791         }
25792         
25793         var ctr = this.el.select('.nav-tabs', true).first();
25794          
25795          
25796         var existing = ctr.select('.nav-tab',true);
25797         var qty = existing.getCount();;
25798         
25799         
25800         var tab = ctr.createChild({
25801             tag : 'li',
25802             cls : 'nav-tab' + (qty ? '' : ' active'),
25803             cn : [
25804                 {
25805                     tag : 'a',
25806                     href:'#',
25807                     html : pane.title
25808                 }
25809             ]
25810         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25811         pane.tab = tab;
25812         
25813         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25814         if (!qty) {
25815             pane.el.addClass('active');
25816         }
25817         
25818                 
25819     },
25820     onTabClick : function(ev,un,ob,pane)
25821     {
25822         //Roo.log('tab - prev default');
25823         ev.preventDefault();
25824         
25825         
25826         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25827         pane.tab.addClass('active');
25828         //Roo.log(pane.title);
25829         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25830         // technically we should have a deactivate event.. but maybe add later.
25831         // and it should not de-activate the selected tab...
25832         this.fireEvent('activatepane', pane);
25833         pane.el.addClass('active');
25834         pane.fireEvent('activate');
25835         
25836         
25837     },
25838     
25839     getActivePane : function()
25840     {
25841         var r = false;
25842         Roo.each(this.panes, function(p) {
25843             if(p.el.hasClass('active')){
25844                 r = p;
25845                 return false;
25846             }
25847             
25848             return;
25849         });
25850         
25851         return r;
25852     }
25853     
25854     
25855 });
25856
25857  
25858 /*
25859  * - LGPL
25860  *
25861  * Tab pane
25862  * 
25863  */
25864 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25865 /**
25866  * @class Roo.bootstrap.TabPane
25867  * @extends Roo.bootstrap.Component
25868  * Bootstrap TabPane class
25869  * @cfg {Boolean} active (false | true) Default false
25870  * @cfg {String} title title of panel
25871
25872  * 
25873  * @constructor
25874  * Create a new TabPane
25875  * @param {Object} config The config object
25876  */
25877
25878 Roo.bootstrap.dash.TabPane = function(config){
25879     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25880     
25881     this.addEvents({
25882         // raw events
25883         /**
25884          * @event activate
25885          * When a pane is activated
25886          * @param {Roo.bootstrap.dash.TabPane} pane
25887          */
25888         "activate" : true
25889          
25890     });
25891 };
25892
25893 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25894     
25895     active : false,
25896     title : '',
25897     
25898     // the tabBox that this is attached to.
25899     tab : false,
25900      
25901     getAutoCreate : function() 
25902     {
25903         var cfg = {
25904             tag: 'div',
25905             cls: 'tab-pane'
25906         };
25907         
25908         if(this.active){
25909             cfg.cls += ' active';
25910         }
25911         
25912         return cfg;
25913     },
25914     initEvents  : function()
25915     {
25916         //Roo.log('trigger add pane handler');
25917         this.parent().fireEvent('addpane', this)
25918     },
25919     
25920      /**
25921      * Updates the tab title 
25922      * @param {String} html to set the title to.
25923      */
25924     setTitle: function(str)
25925     {
25926         if (!this.tab) {
25927             return;
25928         }
25929         this.title = str;
25930         this.tab.select('a', true).first().dom.innerHTML = str;
25931         
25932     }
25933     
25934     
25935     
25936 });
25937
25938  
25939
25940
25941  /*
25942  * - LGPL
25943  *
25944  * menu
25945  * 
25946  */
25947 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25948
25949 /**
25950  * @class Roo.bootstrap.menu.Menu
25951  * @extends Roo.bootstrap.Component
25952  * Bootstrap Menu class - container for Menu
25953  * @cfg {String} html Text of the menu
25954  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25955  * @cfg {String} icon Font awesome icon
25956  * @cfg {String} pos Menu align to (top | bottom) default bottom
25957  * 
25958  * 
25959  * @constructor
25960  * Create a new Menu
25961  * @param {Object} config The config object
25962  */
25963
25964
25965 Roo.bootstrap.menu.Menu = function(config){
25966     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25967     
25968     this.addEvents({
25969         /**
25970          * @event beforeshow
25971          * Fires before this menu is displayed
25972          * @param {Roo.bootstrap.menu.Menu} this
25973          */
25974         beforeshow : true,
25975         /**
25976          * @event beforehide
25977          * Fires before this menu is hidden
25978          * @param {Roo.bootstrap.menu.Menu} this
25979          */
25980         beforehide : true,
25981         /**
25982          * @event show
25983          * Fires after this menu is displayed
25984          * @param {Roo.bootstrap.menu.Menu} this
25985          */
25986         show : true,
25987         /**
25988          * @event hide
25989          * Fires after this menu is hidden
25990          * @param {Roo.bootstrap.menu.Menu} this
25991          */
25992         hide : true,
25993         /**
25994          * @event click
25995          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25996          * @param {Roo.bootstrap.menu.Menu} this
25997          * @param {Roo.EventObject} e
25998          */
25999         click : true
26000     });
26001     
26002 };
26003
26004 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
26005     
26006     submenu : false,
26007     html : '',
26008     weight : 'default',
26009     icon : false,
26010     pos : 'bottom',
26011     
26012     
26013     getChildContainer : function() {
26014         if(this.isSubMenu){
26015             return this.el;
26016         }
26017         
26018         return this.el.select('ul.dropdown-menu', true).first();  
26019     },
26020     
26021     getAutoCreate : function()
26022     {
26023         var text = [
26024             {
26025                 tag : 'span',
26026                 cls : 'roo-menu-text',
26027                 html : this.html
26028             }
26029         ];
26030         
26031         if(this.icon){
26032             text.unshift({
26033                 tag : 'i',
26034                 cls : 'fa ' + this.icon
26035             })
26036         }
26037         
26038         
26039         var cfg = {
26040             tag : 'div',
26041             cls : 'btn-group',
26042             cn : [
26043                 {
26044                     tag : 'button',
26045                     cls : 'dropdown-button btn btn-' + this.weight,
26046                     cn : text
26047                 },
26048                 {
26049                     tag : 'button',
26050                     cls : 'dropdown-toggle btn btn-' + this.weight,
26051                     cn : [
26052                         {
26053                             tag : 'span',
26054                             cls : 'caret'
26055                         }
26056                     ]
26057                 },
26058                 {
26059                     tag : 'ul',
26060                     cls : 'dropdown-menu'
26061                 }
26062             ]
26063             
26064         };
26065         
26066         if(this.pos == 'top'){
26067             cfg.cls += ' dropup';
26068         }
26069         
26070         if(this.isSubMenu){
26071             cfg = {
26072                 tag : 'ul',
26073                 cls : 'dropdown-menu'
26074             }
26075         }
26076         
26077         return cfg;
26078     },
26079     
26080     onRender : function(ct, position)
26081     {
26082         this.isSubMenu = ct.hasClass('dropdown-submenu');
26083         
26084         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26085     },
26086     
26087     initEvents : function() 
26088     {
26089         if(this.isSubMenu){
26090             return;
26091         }
26092         
26093         this.hidden = true;
26094         
26095         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26096         this.triggerEl.on('click', this.onTriggerPress, this);
26097         
26098         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26099         this.buttonEl.on('click', this.onClick, this);
26100         
26101     },
26102     
26103     list : function()
26104     {
26105         if(this.isSubMenu){
26106             return this.el;
26107         }
26108         
26109         return this.el.select('ul.dropdown-menu', true).first();
26110     },
26111     
26112     onClick : function(e)
26113     {
26114         this.fireEvent("click", this, e);
26115     },
26116     
26117     onTriggerPress  : function(e)
26118     {   
26119         if (this.isVisible()) {
26120             this.hide();
26121         } else {
26122             this.show();
26123         }
26124     },
26125     
26126     isVisible : function(){
26127         return !this.hidden;
26128     },
26129     
26130     show : function()
26131     {
26132         this.fireEvent("beforeshow", this);
26133         
26134         this.hidden = false;
26135         this.el.addClass('open');
26136         
26137         Roo.get(document).on("mouseup", this.onMouseUp, this);
26138         
26139         this.fireEvent("show", this);
26140         
26141         
26142     },
26143     
26144     hide : function()
26145     {
26146         this.fireEvent("beforehide", this);
26147         
26148         this.hidden = true;
26149         this.el.removeClass('open');
26150         
26151         Roo.get(document).un("mouseup", this.onMouseUp);
26152         
26153         this.fireEvent("hide", this);
26154     },
26155     
26156     onMouseUp : function()
26157     {
26158         this.hide();
26159     }
26160     
26161 });
26162
26163  
26164  /*
26165  * - LGPL
26166  *
26167  * menu item
26168  * 
26169  */
26170 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26171
26172 /**
26173  * @class Roo.bootstrap.menu.Item
26174  * @extends Roo.bootstrap.Component
26175  * Bootstrap MenuItem class
26176  * @cfg {Boolean} submenu (true | false) default false
26177  * @cfg {String} html text of the item
26178  * @cfg {String} href the link
26179  * @cfg {Boolean} disable (true | false) default false
26180  * @cfg {Boolean} preventDefault (true | false) default true
26181  * @cfg {String} icon Font awesome icon
26182  * @cfg {String} pos Submenu align to (left | right) default right 
26183  * 
26184  * 
26185  * @constructor
26186  * Create a new Item
26187  * @param {Object} config The config object
26188  */
26189
26190
26191 Roo.bootstrap.menu.Item = function(config){
26192     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26193     this.addEvents({
26194         /**
26195          * @event mouseover
26196          * Fires when the mouse is hovering over this menu
26197          * @param {Roo.bootstrap.menu.Item} this
26198          * @param {Roo.EventObject} e
26199          */
26200         mouseover : true,
26201         /**
26202          * @event mouseout
26203          * Fires when the mouse exits this menu
26204          * @param {Roo.bootstrap.menu.Item} this
26205          * @param {Roo.EventObject} e
26206          */
26207         mouseout : true,
26208         // raw events
26209         /**
26210          * @event click
26211          * The raw click event for the entire grid.
26212          * @param {Roo.EventObject} e
26213          */
26214         click : true
26215     });
26216 };
26217
26218 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26219     
26220     submenu : false,
26221     href : '',
26222     html : '',
26223     preventDefault: true,
26224     disable : false,
26225     icon : false,
26226     pos : 'right',
26227     
26228     getAutoCreate : function()
26229     {
26230         var text = [
26231             {
26232                 tag : 'span',
26233                 cls : 'roo-menu-item-text',
26234                 html : this.html
26235             }
26236         ];
26237         
26238         if(this.icon){
26239             text.unshift({
26240                 tag : 'i',
26241                 cls : 'fa ' + this.icon
26242             })
26243         }
26244         
26245         var cfg = {
26246             tag : 'li',
26247             cn : [
26248                 {
26249                     tag : 'a',
26250                     href : this.href || '#',
26251                     cn : text
26252                 }
26253             ]
26254         };
26255         
26256         if(this.disable){
26257             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26258         }
26259         
26260         if(this.submenu){
26261             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26262             
26263             if(this.pos == 'left'){
26264                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26265             }
26266         }
26267         
26268         return cfg;
26269     },
26270     
26271     initEvents : function() 
26272     {
26273         this.el.on('mouseover', this.onMouseOver, this);
26274         this.el.on('mouseout', this.onMouseOut, this);
26275         
26276         this.el.select('a', true).first().on('click', this.onClick, this);
26277         
26278     },
26279     
26280     onClick : function(e)
26281     {
26282         if(this.preventDefault){
26283             e.preventDefault();
26284         }
26285         
26286         this.fireEvent("click", this, e);
26287     },
26288     
26289     onMouseOver : function(e)
26290     {
26291         if(this.submenu && this.pos == 'left'){
26292             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26293         }
26294         
26295         this.fireEvent("mouseover", this, e);
26296     },
26297     
26298     onMouseOut : function(e)
26299     {
26300         this.fireEvent("mouseout", this, e);
26301     }
26302 });
26303
26304  
26305
26306  /*
26307  * - LGPL
26308  *
26309  * menu separator
26310  * 
26311  */
26312 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26313
26314 /**
26315  * @class Roo.bootstrap.menu.Separator
26316  * @extends Roo.bootstrap.Component
26317  * Bootstrap Separator class
26318  * 
26319  * @constructor
26320  * Create a new Separator
26321  * @param {Object} config The config object
26322  */
26323
26324
26325 Roo.bootstrap.menu.Separator = function(config){
26326     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26327 };
26328
26329 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26330     
26331     getAutoCreate : function(){
26332         var cfg = {
26333             tag : 'li',
26334             cls: 'divider'
26335         };
26336         
26337         return cfg;
26338     }
26339    
26340 });
26341
26342  
26343
26344  /*
26345  * - LGPL
26346  *
26347  * Tooltip
26348  * 
26349  */
26350
26351 /**
26352  * @class Roo.bootstrap.Tooltip
26353  * Bootstrap Tooltip class
26354  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26355  * to determine which dom element triggers the tooltip.
26356  * 
26357  * It needs to add support for additional attributes like tooltip-position
26358  * 
26359  * @constructor
26360  * Create a new Toolti
26361  * @param {Object} config The config object
26362  */
26363
26364 Roo.bootstrap.Tooltip = function(config){
26365     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26366     
26367     this.alignment = Roo.bootstrap.Tooltip.alignment;
26368     
26369     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26370         this.alignment = config.alignment;
26371     }
26372     
26373 };
26374
26375 Roo.apply(Roo.bootstrap.Tooltip, {
26376     /**
26377      * @function init initialize tooltip monitoring.
26378      * @static
26379      */
26380     currentEl : false,
26381     currentTip : false,
26382     currentRegion : false,
26383     
26384     //  init : delay?
26385     
26386     init : function()
26387     {
26388         Roo.get(document).on('mouseover', this.enter ,this);
26389         Roo.get(document).on('mouseout', this.leave, this);
26390          
26391         
26392         this.currentTip = new Roo.bootstrap.Tooltip();
26393     },
26394     
26395     enter : function(ev)
26396     {
26397         var dom = ev.getTarget();
26398         
26399         //Roo.log(['enter',dom]);
26400         var el = Roo.fly(dom);
26401         if (this.currentEl) {
26402             //Roo.log(dom);
26403             //Roo.log(this.currentEl);
26404             //Roo.log(this.currentEl.contains(dom));
26405             if (this.currentEl == el) {
26406                 return;
26407             }
26408             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26409                 return;
26410             }
26411
26412         }
26413         
26414         if (this.currentTip.el) {
26415             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26416         }    
26417         //Roo.log(ev);
26418         
26419         if(!el || el.dom == document){
26420             return;
26421         }
26422         
26423         var bindEl = el;
26424         
26425         // you can not look for children, as if el is the body.. then everythign is the child..
26426         if (!el.attr('tooltip')) { //
26427             if (!el.select("[tooltip]").elements.length) {
26428                 return;
26429             }
26430             // is the mouse over this child...?
26431             bindEl = el.select("[tooltip]").first();
26432             var xy = ev.getXY();
26433             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26434                 //Roo.log("not in region.");
26435                 return;
26436             }
26437             //Roo.log("child element over..");
26438             
26439         }
26440         this.currentEl = bindEl;
26441         this.currentTip.bind(bindEl);
26442         this.currentRegion = Roo.lib.Region.getRegion(dom);
26443         this.currentTip.enter();
26444         
26445     },
26446     leave : function(ev)
26447     {
26448         var dom = ev.getTarget();
26449         //Roo.log(['leave',dom]);
26450         if (!this.currentEl) {
26451             return;
26452         }
26453         
26454         
26455         if (dom != this.currentEl.dom) {
26456             return;
26457         }
26458         var xy = ev.getXY();
26459         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26460             return;
26461         }
26462         // only activate leave if mouse cursor is outside... bounding box..
26463         
26464         
26465         
26466         
26467         if (this.currentTip) {
26468             this.currentTip.leave();
26469         }
26470         //Roo.log('clear currentEl');
26471         this.currentEl = false;
26472         
26473         
26474     },
26475     alignment : {
26476         'left' : ['r-l', [-2,0], 'right'],
26477         'right' : ['l-r', [2,0], 'left'],
26478         'bottom' : ['t-b', [0,2], 'top'],
26479         'top' : [ 'b-t', [0,-2], 'bottom']
26480     }
26481     
26482 });
26483
26484
26485 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26486     
26487     
26488     bindEl : false,
26489     
26490     delay : null, // can be { show : 300 , hide: 500}
26491     
26492     timeout : null,
26493     
26494     hoverState : null, //???
26495     
26496     placement : 'bottom', 
26497     
26498     alignment : false,
26499     
26500     getAutoCreate : function(){
26501     
26502         var cfg = {
26503            cls : 'tooltip',
26504            role : 'tooltip',
26505            cn : [
26506                 {
26507                     cls : 'tooltip-arrow'
26508                 },
26509                 {
26510                     cls : 'tooltip-inner'
26511                 }
26512            ]
26513         };
26514         
26515         return cfg;
26516     },
26517     bind : function(el)
26518     {
26519         this.bindEl = el;
26520     },
26521       
26522     
26523     enter : function () {
26524        
26525         if (this.timeout != null) {
26526             clearTimeout(this.timeout);
26527         }
26528         
26529         this.hoverState = 'in';
26530          //Roo.log("enter - show");
26531         if (!this.delay || !this.delay.show) {
26532             this.show();
26533             return;
26534         }
26535         var _t = this;
26536         this.timeout = setTimeout(function () {
26537             if (_t.hoverState == 'in') {
26538                 _t.show();
26539             }
26540         }, this.delay.show);
26541     },
26542     leave : function()
26543     {
26544         clearTimeout(this.timeout);
26545     
26546         this.hoverState = 'out';
26547          if (!this.delay || !this.delay.hide) {
26548             this.hide();
26549             return;
26550         }
26551        
26552         var _t = this;
26553         this.timeout = setTimeout(function () {
26554             //Roo.log("leave - timeout");
26555             
26556             if (_t.hoverState == 'out') {
26557                 _t.hide();
26558                 Roo.bootstrap.Tooltip.currentEl = false;
26559             }
26560         }, delay);
26561     },
26562     
26563     show : function (msg)
26564     {
26565         if (!this.el) {
26566             this.render(document.body);
26567         }
26568         // set content.
26569         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26570         
26571         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26572         
26573         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26574         
26575         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26576         
26577         var placement = typeof this.placement == 'function' ?
26578             this.placement.call(this, this.el, on_el) :
26579             this.placement;
26580             
26581         var autoToken = /\s?auto?\s?/i;
26582         var autoPlace = autoToken.test(placement);
26583         if (autoPlace) {
26584             placement = placement.replace(autoToken, '') || 'top';
26585         }
26586         
26587         //this.el.detach()
26588         //this.el.setXY([0,0]);
26589         this.el.show();
26590         //this.el.dom.style.display='block';
26591         
26592         //this.el.appendTo(on_el);
26593         
26594         var p = this.getPosition();
26595         var box = this.el.getBox();
26596         
26597         if (autoPlace) {
26598             // fixme..
26599         }
26600         
26601         var align = this.alignment[placement];
26602         
26603         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26604         
26605         if(placement == 'top' || placement == 'bottom'){
26606             if(xy[0] < 0){
26607                 placement = 'right';
26608             }
26609             
26610             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26611                 placement = 'left';
26612             }
26613             
26614             var scroll = Roo.select('body', true).first().getScroll();
26615             
26616             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26617                 placement = 'top';
26618             }
26619             
26620             align = this.alignment[placement];
26621         }
26622         
26623         this.el.alignTo(this.bindEl, align[0],align[1]);
26624         //var arrow = this.el.select('.arrow',true).first();
26625         //arrow.set(align[2], 
26626         
26627         this.el.addClass(placement);
26628         
26629         this.el.addClass('in fade');
26630         
26631         this.hoverState = null;
26632         
26633         if (this.el.hasClass('fade')) {
26634             // fade it?
26635         }
26636         
26637     },
26638     hide : function()
26639     {
26640          
26641         if (!this.el) {
26642             return;
26643         }
26644         //this.el.setXY([0,0]);
26645         this.el.removeClass('in');
26646         //this.el.hide();
26647         
26648     }
26649     
26650 });
26651  
26652
26653  /*
26654  * - LGPL
26655  *
26656  * Location Picker
26657  * 
26658  */
26659
26660 /**
26661  * @class Roo.bootstrap.LocationPicker
26662  * @extends Roo.bootstrap.Component
26663  * Bootstrap LocationPicker class
26664  * @cfg {Number} latitude Position when init default 0
26665  * @cfg {Number} longitude Position when init default 0
26666  * @cfg {Number} zoom default 15
26667  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26668  * @cfg {Boolean} mapTypeControl default false
26669  * @cfg {Boolean} disableDoubleClickZoom default false
26670  * @cfg {Boolean} scrollwheel default true
26671  * @cfg {Boolean} streetViewControl default false
26672  * @cfg {Number} radius default 0
26673  * @cfg {String} locationName
26674  * @cfg {Boolean} draggable default true
26675  * @cfg {Boolean} enableAutocomplete default false
26676  * @cfg {Boolean} enableReverseGeocode default true
26677  * @cfg {String} markerTitle
26678  * 
26679  * @constructor
26680  * Create a new LocationPicker
26681  * @param {Object} config The config object
26682  */
26683
26684
26685 Roo.bootstrap.LocationPicker = function(config){
26686     
26687     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26688     
26689     this.addEvents({
26690         /**
26691          * @event initial
26692          * Fires when the picker initialized.
26693          * @param {Roo.bootstrap.LocationPicker} this
26694          * @param {Google Location} location
26695          */
26696         initial : true,
26697         /**
26698          * @event positionchanged
26699          * Fires when the picker position changed.
26700          * @param {Roo.bootstrap.LocationPicker} this
26701          * @param {Google Location} location
26702          */
26703         positionchanged : true,
26704         /**
26705          * @event resize
26706          * Fires when the map resize.
26707          * @param {Roo.bootstrap.LocationPicker} this
26708          */
26709         resize : true,
26710         /**
26711          * @event show
26712          * Fires when the map show.
26713          * @param {Roo.bootstrap.LocationPicker} this
26714          */
26715         show : true,
26716         /**
26717          * @event hide
26718          * Fires when the map hide.
26719          * @param {Roo.bootstrap.LocationPicker} this
26720          */
26721         hide : true,
26722         /**
26723          * @event mapClick
26724          * Fires when click the map.
26725          * @param {Roo.bootstrap.LocationPicker} this
26726          * @param {Map event} e
26727          */
26728         mapClick : true,
26729         /**
26730          * @event mapRightClick
26731          * Fires when right click the map.
26732          * @param {Roo.bootstrap.LocationPicker} this
26733          * @param {Map event} e
26734          */
26735         mapRightClick : true,
26736         /**
26737          * @event markerClick
26738          * Fires when click the marker.
26739          * @param {Roo.bootstrap.LocationPicker} this
26740          * @param {Map event} e
26741          */
26742         markerClick : true,
26743         /**
26744          * @event markerRightClick
26745          * Fires when right click the marker.
26746          * @param {Roo.bootstrap.LocationPicker} this
26747          * @param {Map event} e
26748          */
26749         markerRightClick : true,
26750         /**
26751          * @event OverlayViewDraw
26752          * Fires when OverlayView Draw
26753          * @param {Roo.bootstrap.LocationPicker} this
26754          */
26755         OverlayViewDraw : true,
26756         /**
26757          * @event OverlayViewOnAdd
26758          * Fires when OverlayView Draw
26759          * @param {Roo.bootstrap.LocationPicker} this
26760          */
26761         OverlayViewOnAdd : true,
26762         /**
26763          * @event OverlayViewOnRemove
26764          * Fires when OverlayView Draw
26765          * @param {Roo.bootstrap.LocationPicker} this
26766          */
26767         OverlayViewOnRemove : true,
26768         /**
26769          * @event OverlayViewShow
26770          * Fires when OverlayView Draw
26771          * @param {Roo.bootstrap.LocationPicker} this
26772          * @param {Pixel} cpx
26773          */
26774         OverlayViewShow : true,
26775         /**
26776          * @event OverlayViewHide
26777          * Fires when OverlayView Draw
26778          * @param {Roo.bootstrap.LocationPicker} this
26779          */
26780         OverlayViewHide : true,
26781         /**
26782          * @event loadexception
26783          * Fires when load google lib failed.
26784          * @param {Roo.bootstrap.LocationPicker} this
26785          */
26786         loadexception : true
26787     });
26788         
26789 };
26790
26791 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26792     
26793     gMapContext: false,
26794     
26795     latitude: 0,
26796     longitude: 0,
26797     zoom: 15,
26798     mapTypeId: false,
26799     mapTypeControl: false,
26800     disableDoubleClickZoom: false,
26801     scrollwheel: true,
26802     streetViewControl: false,
26803     radius: 0,
26804     locationName: '',
26805     draggable: true,
26806     enableAutocomplete: false,
26807     enableReverseGeocode: true,
26808     markerTitle: '',
26809     
26810     getAutoCreate: function()
26811     {
26812
26813         var cfg = {
26814             tag: 'div',
26815             cls: 'roo-location-picker'
26816         };
26817         
26818         return cfg
26819     },
26820     
26821     initEvents: function(ct, position)
26822     {       
26823         if(!this.el.getWidth() || this.isApplied()){
26824             return;
26825         }
26826         
26827         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26828         
26829         this.initial();
26830     },
26831     
26832     initial: function()
26833     {
26834         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26835             this.fireEvent('loadexception', this);
26836             return;
26837         }
26838         
26839         if(!this.mapTypeId){
26840             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26841         }
26842         
26843         this.gMapContext = this.GMapContext();
26844         
26845         this.initOverlayView();
26846         
26847         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26848         
26849         var _this = this;
26850                 
26851         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26852             _this.setPosition(_this.gMapContext.marker.position);
26853         });
26854         
26855         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26856             _this.fireEvent('mapClick', this, event);
26857             
26858         });
26859
26860         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26861             _this.fireEvent('mapRightClick', this, event);
26862             
26863         });
26864         
26865         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26866             _this.fireEvent('markerClick', this, event);
26867             
26868         });
26869
26870         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26871             _this.fireEvent('markerRightClick', this, event);
26872             
26873         });
26874         
26875         this.setPosition(this.gMapContext.location);
26876         
26877         this.fireEvent('initial', this, this.gMapContext.location);
26878     },
26879     
26880     initOverlayView: function()
26881     {
26882         var _this = this;
26883         
26884         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26885             
26886             draw: function()
26887             {
26888                 _this.fireEvent('OverlayViewDraw', _this);
26889             },
26890             
26891             onAdd: function()
26892             {
26893                 _this.fireEvent('OverlayViewOnAdd', _this);
26894             },
26895             
26896             onRemove: function()
26897             {
26898                 _this.fireEvent('OverlayViewOnRemove', _this);
26899             },
26900             
26901             show: function(cpx)
26902             {
26903                 _this.fireEvent('OverlayViewShow', _this, cpx);
26904             },
26905             
26906             hide: function()
26907             {
26908                 _this.fireEvent('OverlayViewHide', _this);
26909             }
26910             
26911         });
26912     },
26913     
26914     fromLatLngToContainerPixel: function(event)
26915     {
26916         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26917     },
26918     
26919     isApplied: function() 
26920     {
26921         return this.getGmapContext() == false ? false : true;
26922     },
26923     
26924     getGmapContext: function() 
26925     {
26926         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26927     },
26928     
26929     GMapContext: function() 
26930     {
26931         var position = new google.maps.LatLng(this.latitude, this.longitude);
26932         
26933         var _map = new google.maps.Map(this.el.dom, {
26934             center: position,
26935             zoom: this.zoom,
26936             mapTypeId: this.mapTypeId,
26937             mapTypeControl: this.mapTypeControl,
26938             disableDoubleClickZoom: this.disableDoubleClickZoom,
26939             scrollwheel: this.scrollwheel,
26940             streetViewControl: this.streetViewControl,
26941             locationName: this.locationName,
26942             draggable: this.draggable,
26943             enableAutocomplete: this.enableAutocomplete,
26944             enableReverseGeocode: this.enableReverseGeocode
26945         });
26946         
26947         var _marker = new google.maps.Marker({
26948             position: position,
26949             map: _map,
26950             title: this.markerTitle,
26951             draggable: this.draggable
26952         });
26953         
26954         return {
26955             map: _map,
26956             marker: _marker,
26957             circle: null,
26958             location: position,
26959             radius: this.radius,
26960             locationName: this.locationName,
26961             addressComponents: {
26962                 formatted_address: null,
26963                 addressLine1: null,
26964                 addressLine2: null,
26965                 streetName: null,
26966                 streetNumber: null,
26967                 city: null,
26968                 district: null,
26969                 state: null,
26970                 stateOrProvince: null
26971             },
26972             settings: this,
26973             domContainer: this.el.dom,
26974             geodecoder: new google.maps.Geocoder()
26975         };
26976     },
26977     
26978     drawCircle: function(center, radius, options) 
26979     {
26980         if (this.gMapContext.circle != null) {
26981             this.gMapContext.circle.setMap(null);
26982         }
26983         if (radius > 0) {
26984             radius *= 1;
26985             options = Roo.apply({}, options, {
26986                 strokeColor: "#0000FF",
26987                 strokeOpacity: .35,
26988                 strokeWeight: 2,
26989                 fillColor: "#0000FF",
26990                 fillOpacity: .2
26991             });
26992             
26993             options.map = this.gMapContext.map;
26994             options.radius = radius;
26995             options.center = center;
26996             this.gMapContext.circle = new google.maps.Circle(options);
26997             return this.gMapContext.circle;
26998         }
26999         
27000         return null;
27001     },
27002     
27003     setPosition: function(location) 
27004     {
27005         this.gMapContext.location = location;
27006         this.gMapContext.marker.setPosition(location);
27007         this.gMapContext.map.panTo(location);
27008         this.drawCircle(location, this.gMapContext.radius, {});
27009         
27010         var _this = this;
27011         
27012         if (this.gMapContext.settings.enableReverseGeocode) {
27013             this.gMapContext.geodecoder.geocode({
27014                 latLng: this.gMapContext.location
27015             }, function(results, status) {
27016                 
27017                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27018                     _this.gMapContext.locationName = results[0].formatted_address;
27019                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27020                     
27021                     _this.fireEvent('positionchanged', this, location);
27022                 }
27023             });
27024             
27025             return;
27026         }
27027         
27028         this.fireEvent('positionchanged', this, location);
27029     },
27030     
27031     resize: function()
27032     {
27033         google.maps.event.trigger(this.gMapContext.map, "resize");
27034         
27035         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27036         
27037         this.fireEvent('resize', this);
27038     },
27039     
27040     setPositionByLatLng: function(latitude, longitude)
27041     {
27042         this.setPosition(new google.maps.LatLng(latitude, longitude));
27043     },
27044     
27045     getCurrentPosition: function() 
27046     {
27047         return {
27048             latitude: this.gMapContext.location.lat(),
27049             longitude: this.gMapContext.location.lng()
27050         };
27051     },
27052     
27053     getAddressName: function() 
27054     {
27055         return this.gMapContext.locationName;
27056     },
27057     
27058     getAddressComponents: function() 
27059     {
27060         return this.gMapContext.addressComponents;
27061     },
27062     
27063     address_component_from_google_geocode: function(address_components) 
27064     {
27065         var result = {};
27066         
27067         for (var i = 0; i < address_components.length; i++) {
27068             var component = address_components[i];
27069             if (component.types.indexOf("postal_code") >= 0) {
27070                 result.postalCode = component.short_name;
27071             } else if (component.types.indexOf("street_number") >= 0) {
27072                 result.streetNumber = component.short_name;
27073             } else if (component.types.indexOf("route") >= 0) {
27074                 result.streetName = component.short_name;
27075             } else if (component.types.indexOf("neighborhood") >= 0) {
27076                 result.city = component.short_name;
27077             } else if (component.types.indexOf("locality") >= 0) {
27078                 result.city = component.short_name;
27079             } else if (component.types.indexOf("sublocality") >= 0) {
27080                 result.district = component.short_name;
27081             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27082                 result.stateOrProvince = component.short_name;
27083             } else if (component.types.indexOf("country") >= 0) {
27084                 result.country = component.short_name;
27085             }
27086         }
27087         
27088         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27089         result.addressLine2 = "";
27090         return result;
27091     },
27092     
27093     setZoomLevel: function(zoom)
27094     {
27095         this.gMapContext.map.setZoom(zoom);
27096     },
27097     
27098     show: function()
27099     {
27100         if(!this.el){
27101             return;
27102         }
27103         
27104         this.el.show();
27105         
27106         this.resize();
27107         
27108         this.fireEvent('show', this);
27109     },
27110     
27111     hide: function()
27112     {
27113         if(!this.el){
27114             return;
27115         }
27116         
27117         this.el.hide();
27118         
27119         this.fireEvent('hide', this);
27120     }
27121     
27122 });
27123
27124 Roo.apply(Roo.bootstrap.LocationPicker, {
27125     
27126     OverlayView : function(map, options)
27127     {
27128         options = options || {};
27129         
27130         this.setMap(map);
27131     }
27132     
27133     
27134 });/*
27135  * - LGPL
27136  *
27137  * Alert
27138  * 
27139  */
27140
27141 /**
27142  * @class Roo.bootstrap.Alert
27143  * @extends Roo.bootstrap.Component
27144  * Bootstrap Alert class
27145  * @cfg {String} title The title of alert
27146  * @cfg {String} html The content of alert
27147  * @cfg {String} weight (  success | info | warning | danger )
27148  * @cfg {String} faicon font-awesomeicon
27149  * 
27150  * @constructor
27151  * Create a new alert
27152  * @param {Object} config The config object
27153  */
27154
27155
27156 Roo.bootstrap.Alert = function(config){
27157     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27158     
27159 };
27160
27161 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27162     
27163     title: '',
27164     html: '',
27165     weight: false,
27166     faicon: false,
27167     
27168     getAutoCreate : function()
27169     {
27170         
27171         var cfg = {
27172             tag : 'div',
27173             cls : 'alert',
27174             cn : [
27175                 {
27176                     tag : 'i',
27177                     cls : 'roo-alert-icon'
27178                     
27179                 },
27180                 {
27181                     tag : 'b',
27182                     cls : 'roo-alert-title',
27183                     html : this.title
27184                 },
27185                 {
27186                     tag : 'span',
27187                     cls : 'roo-alert-text',
27188                     html : this.html
27189                 }
27190             ]
27191         };
27192         
27193         if(this.faicon){
27194             cfg.cn[0].cls += ' fa ' + this.faicon;
27195         }
27196         
27197         if(this.weight){
27198             cfg.cls += ' alert-' + this.weight;
27199         }
27200         
27201         return cfg;
27202     },
27203     
27204     initEvents: function() 
27205     {
27206         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27207     },
27208     
27209     setTitle : function(str)
27210     {
27211         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27212     },
27213     
27214     setText : function(str)
27215     {
27216         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27217     },
27218     
27219     setWeight : function(weight)
27220     {
27221         if(this.weight){
27222             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27223         }
27224         
27225         this.weight = weight;
27226         
27227         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27228     },
27229     
27230     setIcon : function(icon)
27231     {
27232         if(this.faicon){
27233             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27234         }
27235         
27236         this.faicon = icon;
27237         
27238         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27239     },
27240     
27241     hide: function() 
27242     {
27243         this.el.hide();   
27244     },
27245     
27246     show: function() 
27247     {  
27248         this.el.show();   
27249     }
27250     
27251 });
27252
27253  
27254 /*
27255 * Licence: LGPL
27256 */
27257
27258 /**
27259  * @class Roo.bootstrap.UploadCropbox
27260  * @extends Roo.bootstrap.Component
27261  * Bootstrap UploadCropbox class
27262  * @cfg {String} emptyText show when image has been loaded
27263  * @cfg {String} rotateNotify show when image too small to rotate
27264  * @cfg {Number} errorTimeout default 3000
27265  * @cfg {Number} minWidth default 300
27266  * @cfg {Number} minHeight default 300
27267  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27268  * @cfg {Boolean} isDocument (true|false) default false
27269  * @cfg {String} url action url
27270  * @cfg {String} paramName default 'imageUpload'
27271  * @cfg {String} method default POST
27272  * @cfg {Boolean} loadMask (true|false) default true
27273  * @cfg {Boolean} loadingText default 'Loading...'
27274  * 
27275  * @constructor
27276  * Create a new UploadCropbox
27277  * @param {Object} config The config object
27278  */
27279
27280 Roo.bootstrap.UploadCropbox = function(config){
27281     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27282     
27283     this.addEvents({
27284         /**
27285          * @event beforeselectfile
27286          * Fire before select file
27287          * @param {Roo.bootstrap.UploadCropbox} this
27288          */
27289         "beforeselectfile" : true,
27290         /**
27291          * @event initial
27292          * Fire after initEvent
27293          * @param {Roo.bootstrap.UploadCropbox} this
27294          */
27295         "initial" : true,
27296         /**
27297          * @event crop
27298          * Fire after initEvent
27299          * @param {Roo.bootstrap.UploadCropbox} this
27300          * @param {String} data
27301          */
27302         "crop" : true,
27303         /**
27304          * @event prepare
27305          * Fire when preparing the file data
27306          * @param {Roo.bootstrap.UploadCropbox} this
27307          * @param {Object} file
27308          */
27309         "prepare" : true,
27310         /**
27311          * @event exception
27312          * Fire when get exception
27313          * @param {Roo.bootstrap.UploadCropbox} this
27314          * @param {XMLHttpRequest} xhr
27315          */
27316         "exception" : true,
27317         /**
27318          * @event beforeloadcanvas
27319          * Fire before load the canvas
27320          * @param {Roo.bootstrap.UploadCropbox} this
27321          * @param {String} src
27322          */
27323         "beforeloadcanvas" : true,
27324         /**
27325          * @event trash
27326          * Fire when trash image
27327          * @param {Roo.bootstrap.UploadCropbox} this
27328          */
27329         "trash" : true,
27330         /**
27331          * @event download
27332          * Fire when download the image
27333          * @param {Roo.bootstrap.UploadCropbox} this
27334          */
27335         "download" : true,
27336         /**
27337          * @event footerbuttonclick
27338          * Fire when footerbuttonclick
27339          * @param {Roo.bootstrap.UploadCropbox} this
27340          * @param {String} type
27341          */
27342         "footerbuttonclick" : true,
27343         /**
27344          * @event resize
27345          * Fire when resize
27346          * @param {Roo.bootstrap.UploadCropbox} this
27347          */
27348         "resize" : true,
27349         /**
27350          * @event rotate
27351          * Fire when rotate the image
27352          * @param {Roo.bootstrap.UploadCropbox} this
27353          * @param {String} pos
27354          */
27355         "rotate" : true,
27356         /**
27357          * @event inspect
27358          * Fire when inspect the file
27359          * @param {Roo.bootstrap.UploadCropbox} this
27360          * @param {Object} file
27361          */
27362         "inspect" : true,
27363         /**
27364          * @event upload
27365          * Fire when xhr upload the file
27366          * @param {Roo.bootstrap.UploadCropbox} this
27367          * @param {Object} data
27368          */
27369         "upload" : true,
27370         /**
27371          * @event arrange
27372          * Fire when arrange the file data
27373          * @param {Roo.bootstrap.UploadCropbox} this
27374          * @param {Object} formData
27375          */
27376         "arrange" : true
27377     });
27378     
27379     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27380 };
27381
27382 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27383     
27384     emptyText : 'Click to upload image',
27385     rotateNotify : 'Image is too small to rotate',
27386     errorTimeout : 3000,
27387     scale : 0,
27388     baseScale : 1,
27389     rotate : 0,
27390     dragable : false,
27391     pinching : false,
27392     mouseX : 0,
27393     mouseY : 0,
27394     cropData : false,
27395     minWidth : 300,
27396     minHeight : 300,
27397     file : false,
27398     exif : {},
27399     baseRotate : 1,
27400     cropType : 'image/jpeg',
27401     buttons : false,
27402     canvasLoaded : false,
27403     isDocument : false,
27404     method : 'POST',
27405     paramName : 'imageUpload',
27406     loadMask : true,
27407     loadingText : 'Loading...',
27408     maskEl : false,
27409     
27410     getAutoCreate : function()
27411     {
27412         var cfg = {
27413             tag : 'div',
27414             cls : 'roo-upload-cropbox',
27415             cn : [
27416                 {
27417                     tag : 'input',
27418                     cls : 'roo-upload-cropbox-selector',
27419                     type : 'file'
27420                 },
27421                 {
27422                     tag : 'div',
27423                     cls : 'roo-upload-cropbox-body',
27424                     style : 'cursor:pointer',
27425                     cn : [
27426                         {
27427                             tag : 'div',
27428                             cls : 'roo-upload-cropbox-preview'
27429                         },
27430                         {
27431                             tag : 'div',
27432                             cls : 'roo-upload-cropbox-thumb'
27433                         },
27434                         {
27435                             tag : 'div',
27436                             cls : 'roo-upload-cropbox-empty-notify',
27437                             html : this.emptyText
27438                         },
27439                         {
27440                             tag : 'div',
27441                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27442                             html : this.rotateNotify
27443                         }
27444                     ]
27445                 },
27446                 {
27447                     tag : 'div',
27448                     cls : 'roo-upload-cropbox-footer',
27449                     cn : {
27450                         tag : 'div',
27451                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27452                         cn : []
27453                     }
27454                 }
27455             ]
27456         };
27457         
27458         return cfg;
27459     },
27460     
27461     onRender : function(ct, position)
27462     {
27463         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27464         
27465         if (this.buttons.length) {
27466             
27467             Roo.each(this.buttons, function(bb) {
27468                 
27469                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27470                 
27471                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27472                 
27473             }, this);
27474         }
27475         
27476         if(this.loadMask){
27477             this.maskEl = this.el;
27478         }
27479     },
27480     
27481     initEvents : function()
27482     {
27483         this.urlAPI = (window.createObjectURL && window) || 
27484                                 (window.URL && URL.revokeObjectURL && URL) || 
27485                                 (window.webkitURL && webkitURL);
27486                         
27487         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27488         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27489         
27490         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27491         this.selectorEl.hide();
27492         
27493         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27494         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27495         
27496         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27497         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27498         this.thumbEl.hide();
27499         
27500         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27501         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27502         
27503         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27504         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27505         this.errorEl.hide();
27506         
27507         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27508         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27509         this.footerEl.hide();
27510         
27511         this.setThumbBoxSize();
27512         
27513         this.bind();
27514         
27515         this.resize();
27516         
27517         this.fireEvent('initial', this);
27518     },
27519
27520     bind : function()
27521     {
27522         var _this = this;
27523         
27524         window.addEventListener("resize", function() { _this.resize(); } );
27525         
27526         this.bodyEl.on('click', this.beforeSelectFile, this);
27527         
27528         if(Roo.isTouch){
27529             this.bodyEl.on('touchstart', this.onTouchStart, this);
27530             this.bodyEl.on('touchmove', this.onTouchMove, this);
27531             this.bodyEl.on('touchend', this.onTouchEnd, this);
27532         }
27533         
27534         if(!Roo.isTouch){
27535             this.bodyEl.on('mousedown', this.onMouseDown, this);
27536             this.bodyEl.on('mousemove', this.onMouseMove, this);
27537             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27538             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27539             Roo.get(document).on('mouseup', this.onMouseUp, this);
27540         }
27541         
27542         this.selectorEl.on('change', this.onFileSelected, this);
27543     },
27544     
27545     reset : function()
27546     {    
27547         this.scale = 0;
27548         this.baseScale = 1;
27549         this.rotate = 0;
27550         this.baseRotate = 1;
27551         this.dragable = false;
27552         this.pinching = false;
27553         this.mouseX = 0;
27554         this.mouseY = 0;
27555         this.cropData = false;
27556         this.notifyEl.dom.innerHTML = this.emptyText;
27557         
27558         this.selectorEl.dom.value = '';
27559         
27560     },
27561     
27562     resize : function()
27563     {
27564         if(this.fireEvent('resize', this) != false){
27565             this.setThumbBoxPosition();
27566             this.setCanvasPosition();
27567         }
27568     },
27569     
27570     onFooterButtonClick : function(e, el, o, type)
27571     {
27572         switch (type) {
27573             case 'rotate-left' :
27574                 this.onRotateLeft(e);
27575                 break;
27576             case 'rotate-right' :
27577                 this.onRotateRight(e);
27578                 break;
27579             case 'picture' :
27580                 this.beforeSelectFile(e);
27581                 break;
27582             case 'trash' :
27583                 this.trash(e);
27584                 break;
27585             case 'crop' :
27586                 this.crop(e);
27587                 break;
27588             case 'download' :
27589                 this.download(e);
27590                 break;
27591             default :
27592                 break;
27593         }
27594         
27595         this.fireEvent('footerbuttonclick', this, type);
27596     },
27597     
27598     beforeSelectFile : function(e)
27599     {
27600         e.preventDefault();
27601         
27602         if(this.fireEvent('beforeselectfile', this) != false){
27603             this.selectorEl.dom.click();
27604         }
27605     },
27606     
27607     onFileSelected : function(e)
27608     {
27609         e.preventDefault();
27610         
27611         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27612             return;
27613         }
27614         
27615         var file = this.selectorEl.dom.files[0];
27616         
27617         if(this.fireEvent('inspect', this, file) != false){
27618             this.prepare(file);
27619         }
27620         
27621     },
27622     
27623     trash : function(e)
27624     {
27625         this.fireEvent('trash', this);
27626     },
27627     
27628     download : function(e)
27629     {
27630         this.fireEvent('download', this);
27631     },
27632     
27633     loadCanvas : function(src)
27634     {   
27635         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27636             
27637             this.reset();
27638             
27639             this.imageEl = document.createElement('img');
27640             
27641             var _this = this;
27642             
27643             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27644             
27645             this.imageEl.src = src;
27646         }
27647     },
27648     
27649     onLoadCanvas : function()
27650     {   
27651         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27652         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27653         
27654         this.bodyEl.un('click', this.beforeSelectFile, this);
27655         
27656         this.notifyEl.hide();
27657         this.thumbEl.show();
27658         this.footerEl.show();
27659         
27660         this.baseRotateLevel();
27661         
27662         if(this.isDocument){
27663             this.setThumbBoxSize();
27664         }
27665         
27666         this.setThumbBoxPosition();
27667         
27668         this.baseScaleLevel();
27669         
27670         this.draw();
27671         
27672         this.resize();
27673         
27674         this.canvasLoaded = true;
27675         
27676         if(this.loadMask){
27677             this.maskEl.unmask();
27678         }
27679         
27680     },
27681     
27682     setCanvasPosition : function()
27683     {   
27684         if(!this.canvasEl){
27685             return;
27686         }
27687         
27688         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27689         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27690         
27691         this.previewEl.setLeft(pw);
27692         this.previewEl.setTop(ph);
27693         
27694     },
27695     
27696     onMouseDown : function(e)
27697     {   
27698         e.stopEvent();
27699         
27700         this.dragable = true;
27701         this.pinching = false;
27702         
27703         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27704             this.dragable = false;
27705             return;
27706         }
27707         
27708         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27709         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27710         
27711     },
27712     
27713     onMouseMove : function(e)
27714     {   
27715         e.stopEvent();
27716         
27717         if(!this.canvasLoaded){
27718             return;
27719         }
27720         
27721         if (!this.dragable){
27722             return;
27723         }
27724         
27725         var minX = Math.ceil(this.thumbEl.getLeft(true));
27726         var minY = Math.ceil(this.thumbEl.getTop(true));
27727         
27728         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27729         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27730         
27731         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27732         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27733         
27734         x = x - this.mouseX;
27735         y = y - this.mouseY;
27736         
27737         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27738         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27739         
27740         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27741         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27742         
27743         this.previewEl.setLeft(bgX);
27744         this.previewEl.setTop(bgY);
27745         
27746         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27747         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27748     },
27749     
27750     onMouseUp : function(e)
27751     {   
27752         e.stopEvent();
27753         
27754         this.dragable = false;
27755     },
27756     
27757     onMouseWheel : function(e)
27758     {   
27759         e.stopEvent();
27760         
27761         this.startScale = this.scale;
27762         
27763         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27764         
27765         if(!this.zoomable()){
27766             this.scale = this.startScale;
27767             return;
27768         }
27769         
27770         this.draw();
27771         
27772         return;
27773     },
27774     
27775     zoomable : function()
27776     {
27777         var minScale = this.thumbEl.getWidth() / this.minWidth;
27778         
27779         if(this.minWidth < this.minHeight){
27780             minScale = this.thumbEl.getHeight() / this.minHeight;
27781         }
27782         
27783         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27784         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27785         
27786         if(
27787                 this.isDocument &&
27788                 (this.rotate == 0 || this.rotate == 180) && 
27789                 (
27790                     width > this.imageEl.OriginWidth || 
27791                     height > this.imageEl.OriginHeight ||
27792                     (width < this.minWidth && height < this.minHeight)
27793                 )
27794         ){
27795             return false;
27796         }
27797         
27798         if(
27799                 this.isDocument &&
27800                 (this.rotate == 90 || this.rotate == 270) && 
27801                 (
27802                     width > this.imageEl.OriginWidth || 
27803                     height > this.imageEl.OriginHeight ||
27804                     (width < this.minHeight && height < this.minWidth)
27805                 )
27806         ){
27807             return false;
27808         }
27809         
27810         if(
27811                 !this.isDocument &&
27812                 (this.rotate == 0 || this.rotate == 180) && 
27813                 (
27814                     width < this.minWidth || 
27815                     width > this.imageEl.OriginWidth || 
27816                     height < this.minHeight || 
27817                     height > this.imageEl.OriginHeight
27818                 )
27819         ){
27820             return false;
27821         }
27822         
27823         if(
27824                 !this.isDocument &&
27825                 (this.rotate == 90 || this.rotate == 270) && 
27826                 (
27827                     width < this.minHeight || 
27828                     width > this.imageEl.OriginWidth || 
27829                     height < this.minWidth || 
27830                     height > this.imageEl.OriginHeight
27831                 )
27832         ){
27833             return false;
27834         }
27835         
27836         return true;
27837         
27838     },
27839     
27840     onRotateLeft : function(e)
27841     {   
27842         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27843             
27844             var minScale = this.thumbEl.getWidth() / this.minWidth;
27845             
27846             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27847             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27848             
27849             this.startScale = this.scale;
27850             
27851             while (this.getScaleLevel() < minScale){
27852             
27853                 this.scale = this.scale + 1;
27854                 
27855                 if(!this.zoomable()){
27856                     break;
27857                 }
27858                 
27859                 if(
27860                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27861                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27862                 ){
27863                     continue;
27864                 }
27865                 
27866                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27867
27868                 this.draw();
27869                 
27870                 return;
27871             }
27872             
27873             this.scale = this.startScale;
27874             
27875             this.onRotateFail();
27876             
27877             return false;
27878         }
27879         
27880         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27881
27882         if(this.isDocument){
27883             this.setThumbBoxSize();
27884             this.setThumbBoxPosition();
27885             this.setCanvasPosition();
27886         }
27887         
27888         this.draw();
27889         
27890         this.fireEvent('rotate', this, 'left');
27891         
27892     },
27893     
27894     onRotateRight : function(e)
27895     {
27896         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27897             
27898             var minScale = this.thumbEl.getWidth() / this.minWidth;
27899         
27900             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27901             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27902             
27903             this.startScale = this.scale;
27904             
27905             while (this.getScaleLevel() < minScale){
27906             
27907                 this.scale = this.scale + 1;
27908                 
27909                 if(!this.zoomable()){
27910                     break;
27911                 }
27912                 
27913                 if(
27914                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27915                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27916                 ){
27917                     continue;
27918                 }
27919                 
27920                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27921
27922                 this.draw();
27923                 
27924                 return;
27925             }
27926             
27927             this.scale = this.startScale;
27928             
27929             this.onRotateFail();
27930             
27931             return false;
27932         }
27933         
27934         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27935
27936         if(this.isDocument){
27937             this.setThumbBoxSize();
27938             this.setThumbBoxPosition();
27939             this.setCanvasPosition();
27940         }
27941         
27942         this.draw();
27943         
27944         this.fireEvent('rotate', this, 'right');
27945     },
27946     
27947     onRotateFail : function()
27948     {
27949         this.errorEl.show(true);
27950         
27951         var _this = this;
27952         
27953         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27954     },
27955     
27956     draw : function()
27957     {
27958         this.previewEl.dom.innerHTML = '';
27959         
27960         var canvasEl = document.createElement("canvas");
27961         
27962         var contextEl = canvasEl.getContext("2d");
27963         
27964         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27965         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27966         var center = this.imageEl.OriginWidth / 2;
27967         
27968         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27969             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27970             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27971             center = this.imageEl.OriginHeight / 2;
27972         }
27973         
27974         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27975         
27976         contextEl.translate(center, center);
27977         contextEl.rotate(this.rotate * Math.PI / 180);
27978
27979         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27980         
27981         this.canvasEl = document.createElement("canvas");
27982         
27983         this.contextEl = this.canvasEl.getContext("2d");
27984         
27985         switch (this.rotate) {
27986             case 0 :
27987                 
27988                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27989                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27990                 
27991                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27992                 
27993                 break;
27994             case 90 : 
27995                 
27996                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27997                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27998                 
27999                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28000                     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);
28001                     break;
28002                 }
28003                 
28004                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28005                 
28006                 break;
28007             case 180 :
28008                 
28009                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28010                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28011                 
28012                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28013                     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);
28014                     break;
28015                 }
28016                 
28017                 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);
28018                 
28019                 break;
28020             case 270 :
28021                 
28022                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28023                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28024         
28025                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28026                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28027                     break;
28028                 }
28029                 
28030                 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);
28031                 
28032                 break;
28033             default : 
28034                 break;
28035         }
28036         
28037         this.previewEl.appendChild(this.canvasEl);
28038         
28039         this.setCanvasPosition();
28040     },
28041     
28042     crop : function()
28043     {
28044         if(!this.canvasLoaded){
28045             return;
28046         }
28047         
28048         var imageCanvas = document.createElement("canvas");
28049         
28050         var imageContext = imageCanvas.getContext("2d");
28051         
28052         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28053         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28054         
28055         var center = imageCanvas.width / 2;
28056         
28057         imageContext.translate(center, center);
28058         
28059         imageContext.rotate(this.rotate * Math.PI / 180);
28060         
28061         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28062         
28063         var canvas = document.createElement("canvas");
28064         
28065         var context = canvas.getContext("2d");
28066                 
28067         canvas.width = this.minWidth;
28068         canvas.height = this.minHeight;
28069
28070         switch (this.rotate) {
28071             case 0 :
28072                 
28073                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28074                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28075                 
28076                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28077                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28078                 
28079                 var targetWidth = this.minWidth - 2 * x;
28080                 var targetHeight = this.minHeight - 2 * y;
28081                 
28082                 var scale = 1;
28083                 
28084                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28085                     scale = targetWidth / width;
28086                 }
28087                 
28088                 if(x > 0 && y == 0){
28089                     scale = targetHeight / height;
28090                 }
28091                 
28092                 if(x > 0 && y > 0){
28093                     scale = targetWidth / width;
28094                     
28095                     if(width < height){
28096                         scale = targetHeight / height;
28097                     }
28098                 }
28099                 
28100                 context.scale(scale, scale);
28101                 
28102                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28103                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28104
28105                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28106                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28107
28108                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28109                 
28110                 break;
28111             case 90 : 
28112                 
28113                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28114                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28115                 
28116                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28117                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28118                 
28119                 var targetWidth = this.minWidth - 2 * x;
28120                 var targetHeight = this.minHeight - 2 * y;
28121                 
28122                 var scale = 1;
28123                 
28124                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28125                     scale = targetWidth / width;
28126                 }
28127                 
28128                 if(x > 0 && y == 0){
28129                     scale = targetHeight / height;
28130                 }
28131                 
28132                 if(x > 0 && y > 0){
28133                     scale = targetWidth / width;
28134                     
28135                     if(width < height){
28136                         scale = targetHeight / height;
28137                     }
28138                 }
28139                 
28140                 context.scale(scale, scale);
28141                 
28142                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28143                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28144
28145                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28146                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28147                 
28148                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28149                 
28150                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28151                 
28152                 break;
28153             case 180 :
28154                 
28155                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28156                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28157                 
28158                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28159                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28160                 
28161                 var targetWidth = this.minWidth - 2 * x;
28162                 var targetHeight = this.minHeight - 2 * y;
28163                 
28164                 var scale = 1;
28165                 
28166                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28167                     scale = targetWidth / width;
28168                 }
28169                 
28170                 if(x > 0 && y == 0){
28171                     scale = targetHeight / height;
28172                 }
28173                 
28174                 if(x > 0 && y > 0){
28175                     scale = targetWidth / width;
28176                     
28177                     if(width < height){
28178                         scale = targetHeight / height;
28179                     }
28180                 }
28181                 
28182                 context.scale(scale, scale);
28183                 
28184                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28185                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28186
28187                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28188                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28189
28190                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28191                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28192                 
28193                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28194                 
28195                 break;
28196             case 270 :
28197                 
28198                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28199                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28200                 
28201                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28202                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28203                 
28204                 var targetWidth = this.minWidth - 2 * x;
28205                 var targetHeight = this.minHeight - 2 * y;
28206                 
28207                 var scale = 1;
28208                 
28209                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28210                     scale = targetWidth / width;
28211                 }
28212                 
28213                 if(x > 0 && y == 0){
28214                     scale = targetHeight / height;
28215                 }
28216                 
28217                 if(x > 0 && y > 0){
28218                     scale = targetWidth / width;
28219                     
28220                     if(width < height){
28221                         scale = targetHeight / height;
28222                     }
28223                 }
28224                 
28225                 context.scale(scale, scale);
28226                 
28227                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28228                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28229
28230                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28231                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28232                 
28233                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28234                 
28235                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28236                 
28237                 break;
28238             default : 
28239                 break;
28240         }
28241         
28242         this.cropData = canvas.toDataURL(this.cropType);
28243         
28244         if(this.fireEvent('crop', this, this.cropData) !== false){
28245             this.process(this.file, this.cropData);
28246         }
28247         
28248         return;
28249         
28250     },
28251     
28252     setThumbBoxSize : function()
28253     {
28254         var width, height;
28255         
28256         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28257             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28258             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28259             
28260             this.minWidth = width;
28261             this.minHeight = height;
28262             
28263             if(this.rotate == 90 || this.rotate == 270){
28264                 this.minWidth = height;
28265                 this.minHeight = width;
28266             }
28267         }
28268         
28269         height = 300;
28270         width = Math.ceil(this.minWidth * height / this.minHeight);
28271         
28272         if(this.minWidth > this.minHeight){
28273             width = 300;
28274             height = Math.ceil(this.minHeight * width / this.minWidth);
28275         }
28276         
28277         this.thumbEl.setStyle({
28278             width : width + 'px',
28279             height : height + 'px'
28280         });
28281
28282         return;
28283             
28284     },
28285     
28286     setThumbBoxPosition : function()
28287     {
28288         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28289         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28290         
28291         this.thumbEl.setLeft(x);
28292         this.thumbEl.setTop(y);
28293         
28294     },
28295     
28296     baseRotateLevel : function()
28297     {
28298         this.baseRotate = 1;
28299         
28300         if(
28301                 typeof(this.exif) != 'undefined' &&
28302                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28303                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28304         ){
28305             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28306         }
28307         
28308         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28309         
28310     },
28311     
28312     baseScaleLevel : function()
28313     {
28314         var width, height;
28315         
28316         if(this.isDocument){
28317             
28318             if(this.baseRotate == 6 || this.baseRotate == 8){
28319             
28320                 height = this.thumbEl.getHeight();
28321                 this.baseScale = height / this.imageEl.OriginWidth;
28322
28323                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28324                     width = this.thumbEl.getWidth();
28325                     this.baseScale = width / this.imageEl.OriginHeight;
28326                 }
28327
28328                 return;
28329             }
28330
28331             height = this.thumbEl.getHeight();
28332             this.baseScale = height / this.imageEl.OriginHeight;
28333
28334             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28335                 width = this.thumbEl.getWidth();
28336                 this.baseScale = width / this.imageEl.OriginWidth;
28337             }
28338
28339             return;
28340         }
28341         
28342         if(this.baseRotate == 6 || this.baseRotate == 8){
28343             
28344             width = this.thumbEl.getHeight();
28345             this.baseScale = width / this.imageEl.OriginHeight;
28346             
28347             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28348                 height = this.thumbEl.getWidth();
28349                 this.baseScale = height / this.imageEl.OriginHeight;
28350             }
28351             
28352             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28353                 height = this.thumbEl.getWidth();
28354                 this.baseScale = height / this.imageEl.OriginHeight;
28355                 
28356                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28357                     width = this.thumbEl.getHeight();
28358                     this.baseScale = width / this.imageEl.OriginWidth;
28359                 }
28360             }
28361             
28362             return;
28363         }
28364         
28365         width = this.thumbEl.getWidth();
28366         this.baseScale = width / this.imageEl.OriginWidth;
28367         
28368         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28369             height = this.thumbEl.getHeight();
28370             this.baseScale = height / this.imageEl.OriginHeight;
28371         }
28372         
28373         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28374             
28375             height = this.thumbEl.getHeight();
28376             this.baseScale = height / this.imageEl.OriginHeight;
28377             
28378             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28379                 width = this.thumbEl.getWidth();
28380                 this.baseScale = width / this.imageEl.OriginWidth;
28381             }
28382             
28383         }
28384         
28385         return;
28386     },
28387     
28388     getScaleLevel : function()
28389     {
28390         return this.baseScale * Math.pow(1.1, this.scale);
28391     },
28392     
28393     onTouchStart : function(e)
28394     {
28395         if(!this.canvasLoaded){
28396             this.beforeSelectFile(e);
28397             return;
28398         }
28399         
28400         var touches = e.browserEvent.touches;
28401         
28402         if(!touches){
28403             return;
28404         }
28405         
28406         if(touches.length == 1){
28407             this.onMouseDown(e);
28408             return;
28409         }
28410         
28411         if(touches.length != 2){
28412             return;
28413         }
28414         
28415         var coords = [];
28416         
28417         for(var i = 0, finger; finger = touches[i]; i++){
28418             coords.push(finger.pageX, finger.pageY);
28419         }
28420         
28421         var x = Math.pow(coords[0] - coords[2], 2);
28422         var y = Math.pow(coords[1] - coords[3], 2);
28423         
28424         this.startDistance = Math.sqrt(x + y);
28425         
28426         this.startScale = this.scale;
28427         
28428         this.pinching = true;
28429         this.dragable = false;
28430         
28431     },
28432     
28433     onTouchMove : function(e)
28434     {
28435         if(!this.pinching && !this.dragable){
28436             return;
28437         }
28438         
28439         var touches = e.browserEvent.touches;
28440         
28441         if(!touches){
28442             return;
28443         }
28444         
28445         if(this.dragable){
28446             this.onMouseMove(e);
28447             return;
28448         }
28449         
28450         var coords = [];
28451         
28452         for(var i = 0, finger; finger = touches[i]; i++){
28453             coords.push(finger.pageX, finger.pageY);
28454         }
28455         
28456         var x = Math.pow(coords[0] - coords[2], 2);
28457         var y = Math.pow(coords[1] - coords[3], 2);
28458         
28459         this.endDistance = Math.sqrt(x + y);
28460         
28461         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28462         
28463         if(!this.zoomable()){
28464             this.scale = this.startScale;
28465             return;
28466         }
28467         
28468         this.draw();
28469         
28470     },
28471     
28472     onTouchEnd : function(e)
28473     {
28474         this.pinching = false;
28475         this.dragable = false;
28476         
28477     },
28478     
28479     process : function(file, crop)
28480     {
28481         if(this.loadMask){
28482             this.maskEl.mask(this.loadingText);
28483         }
28484         
28485         this.xhr = new XMLHttpRequest();
28486         
28487         file.xhr = this.xhr;
28488
28489         this.xhr.open(this.method, this.url, true);
28490         
28491         var headers = {
28492             "Accept": "application/json",
28493             "Cache-Control": "no-cache",
28494             "X-Requested-With": "XMLHttpRequest"
28495         };
28496         
28497         for (var headerName in headers) {
28498             var headerValue = headers[headerName];
28499             if (headerValue) {
28500                 this.xhr.setRequestHeader(headerName, headerValue);
28501             }
28502         }
28503         
28504         var _this = this;
28505         
28506         this.xhr.onload = function()
28507         {
28508             _this.xhrOnLoad(_this.xhr);
28509         }
28510         
28511         this.xhr.onerror = function()
28512         {
28513             _this.xhrOnError(_this.xhr);
28514         }
28515         
28516         var formData = new FormData();
28517
28518         formData.append('returnHTML', 'NO');
28519         
28520         if(crop){
28521             formData.append('crop', crop);
28522         }
28523         
28524         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28525             formData.append(this.paramName, file, file.name);
28526         }
28527         
28528         if(typeof(file.filename) != 'undefined'){
28529             formData.append('filename', file.filename);
28530         }
28531         
28532         if(typeof(file.mimetype) != 'undefined'){
28533             formData.append('mimetype', file.mimetype);
28534         }
28535         
28536         if(this.fireEvent('arrange', this, formData) != false){
28537             this.xhr.send(formData);
28538         };
28539     },
28540     
28541     xhrOnLoad : function(xhr)
28542     {
28543         if(this.loadMask){
28544             this.maskEl.unmask();
28545         }
28546         
28547         if (xhr.readyState !== 4) {
28548             this.fireEvent('exception', this, xhr);
28549             return;
28550         }
28551
28552         var response = Roo.decode(xhr.responseText);
28553         
28554         if(!response.success){
28555             this.fireEvent('exception', this, xhr);
28556             return;
28557         }
28558         
28559         var response = Roo.decode(xhr.responseText);
28560         
28561         this.fireEvent('upload', this, response);
28562         
28563     },
28564     
28565     xhrOnError : function()
28566     {
28567         if(this.loadMask){
28568             this.maskEl.unmask();
28569         }
28570         
28571         Roo.log('xhr on error');
28572         
28573         var response = Roo.decode(xhr.responseText);
28574           
28575         Roo.log(response);
28576         
28577     },
28578     
28579     prepare : function(file)
28580     {   
28581         if(this.loadMask){
28582             this.maskEl.mask(this.loadingText);
28583         }
28584         
28585         this.file = false;
28586         this.exif = {};
28587         
28588         if(typeof(file) === 'string'){
28589             this.loadCanvas(file);
28590             return;
28591         }
28592         
28593         if(!file || !this.urlAPI){
28594             return;
28595         }
28596         
28597         this.file = file;
28598         this.cropType = file.type;
28599         
28600         var _this = this;
28601         
28602         if(this.fireEvent('prepare', this, this.file) != false){
28603             
28604             var reader = new FileReader();
28605             
28606             reader.onload = function (e) {
28607                 if (e.target.error) {
28608                     Roo.log(e.target.error);
28609                     return;
28610                 }
28611                 
28612                 var buffer = e.target.result,
28613                     dataView = new DataView(buffer),
28614                     offset = 2,
28615                     maxOffset = dataView.byteLength - 4,
28616                     markerBytes,
28617                     markerLength;
28618                 
28619                 if (dataView.getUint16(0) === 0xffd8) {
28620                     while (offset < maxOffset) {
28621                         markerBytes = dataView.getUint16(offset);
28622                         
28623                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28624                             markerLength = dataView.getUint16(offset + 2) + 2;
28625                             if (offset + markerLength > dataView.byteLength) {
28626                                 Roo.log('Invalid meta data: Invalid segment size.');
28627                                 break;
28628                             }
28629                             
28630                             if(markerBytes == 0xffe1){
28631                                 _this.parseExifData(
28632                                     dataView,
28633                                     offset,
28634                                     markerLength
28635                                 );
28636                             }
28637                             
28638                             offset += markerLength;
28639                             
28640                             continue;
28641                         }
28642                         
28643                         break;
28644                     }
28645                     
28646                 }
28647                 
28648                 var url = _this.urlAPI.createObjectURL(_this.file);
28649                 
28650                 _this.loadCanvas(url);
28651                 
28652                 return;
28653             }
28654             
28655             reader.readAsArrayBuffer(this.file);
28656             
28657         }
28658         
28659     },
28660     
28661     parseExifData : function(dataView, offset, length)
28662     {
28663         var tiffOffset = offset + 10,
28664             littleEndian,
28665             dirOffset;
28666     
28667         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28668             // No Exif data, might be XMP data instead
28669             return;
28670         }
28671         
28672         // Check for the ASCII code for "Exif" (0x45786966):
28673         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28674             // No Exif data, might be XMP data instead
28675             return;
28676         }
28677         if (tiffOffset + 8 > dataView.byteLength) {
28678             Roo.log('Invalid Exif data: Invalid segment size.');
28679             return;
28680         }
28681         // Check for the two null bytes:
28682         if (dataView.getUint16(offset + 8) !== 0x0000) {
28683             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28684             return;
28685         }
28686         // Check the byte alignment:
28687         switch (dataView.getUint16(tiffOffset)) {
28688         case 0x4949:
28689             littleEndian = true;
28690             break;
28691         case 0x4D4D:
28692             littleEndian = false;
28693             break;
28694         default:
28695             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28696             return;
28697         }
28698         // Check for the TIFF tag marker (0x002A):
28699         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28700             Roo.log('Invalid Exif data: Missing TIFF marker.');
28701             return;
28702         }
28703         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28704         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28705         
28706         this.parseExifTags(
28707             dataView,
28708             tiffOffset,
28709             tiffOffset + dirOffset,
28710             littleEndian
28711         );
28712     },
28713     
28714     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28715     {
28716         var tagsNumber,
28717             dirEndOffset,
28718             i;
28719         if (dirOffset + 6 > dataView.byteLength) {
28720             Roo.log('Invalid Exif data: Invalid directory offset.');
28721             return;
28722         }
28723         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28724         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28725         if (dirEndOffset + 4 > dataView.byteLength) {
28726             Roo.log('Invalid Exif data: Invalid directory size.');
28727             return;
28728         }
28729         for (i = 0; i < tagsNumber; i += 1) {
28730             this.parseExifTag(
28731                 dataView,
28732                 tiffOffset,
28733                 dirOffset + 2 + 12 * i, // tag offset
28734                 littleEndian
28735             );
28736         }
28737         // Return the offset to the next directory:
28738         return dataView.getUint32(dirEndOffset, littleEndian);
28739     },
28740     
28741     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28742     {
28743         var tag = dataView.getUint16(offset, littleEndian);
28744         
28745         this.exif[tag] = this.getExifValue(
28746             dataView,
28747             tiffOffset,
28748             offset,
28749             dataView.getUint16(offset + 2, littleEndian), // tag type
28750             dataView.getUint32(offset + 4, littleEndian), // tag length
28751             littleEndian
28752         );
28753     },
28754     
28755     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28756     {
28757         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28758             tagSize,
28759             dataOffset,
28760             values,
28761             i,
28762             str,
28763             c;
28764     
28765         if (!tagType) {
28766             Roo.log('Invalid Exif data: Invalid tag type.');
28767             return;
28768         }
28769         
28770         tagSize = tagType.size * length;
28771         // Determine if the value is contained in the dataOffset bytes,
28772         // or if the value at the dataOffset is a pointer to the actual data:
28773         dataOffset = tagSize > 4 ?
28774                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28775         if (dataOffset + tagSize > dataView.byteLength) {
28776             Roo.log('Invalid Exif data: Invalid data offset.');
28777             return;
28778         }
28779         if (length === 1) {
28780             return tagType.getValue(dataView, dataOffset, littleEndian);
28781         }
28782         values = [];
28783         for (i = 0; i < length; i += 1) {
28784             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28785         }
28786         
28787         if (tagType.ascii) {
28788             str = '';
28789             // Concatenate the chars:
28790             for (i = 0; i < values.length; i += 1) {
28791                 c = values[i];
28792                 // Ignore the terminating NULL byte(s):
28793                 if (c === '\u0000') {
28794                     break;
28795                 }
28796                 str += c;
28797             }
28798             return str;
28799         }
28800         return values;
28801     }
28802     
28803 });
28804
28805 Roo.apply(Roo.bootstrap.UploadCropbox, {
28806     tags : {
28807         'Orientation': 0x0112
28808     },
28809     
28810     Orientation: {
28811             1: 0, //'top-left',
28812 //            2: 'top-right',
28813             3: 180, //'bottom-right',
28814 //            4: 'bottom-left',
28815 //            5: 'left-top',
28816             6: 90, //'right-top',
28817 //            7: 'right-bottom',
28818             8: 270 //'left-bottom'
28819     },
28820     
28821     exifTagTypes : {
28822         // byte, 8-bit unsigned int:
28823         1: {
28824             getValue: function (dataView, dataOffset) {
28825                 return dataView.getUint8(dataOffset);
28826             },
28827             size: 1
28828         },
28829         // ascii, 8-bit byte:
28830         2: {
28831             getValue: function (dataView, dataOffset) {
28832                 return String.fromCharCode(dataView.getUint8(dataOffset));
28833             },
28834             size: 1,
28835             ascii: true
28836         },
28837         // short, 16 bit int:
28838         3: {
28839             getValue: function (dataView, dataOffset, littleEndian) {
28840                 return dataView.getUint16(dataOffset, littleEndian);
28841             },
28842             size: 2
28843         },
28844         // long, 32 bit int:
28845         4: {
28846             getValue: function (dataView, dataOffset, littleEndian) {
28847                 return dataView.getUint32(dataOffset, littleEndian);
28848             },
28849             size: 4
28850         },
28851         // rational = two long values, first is numerator, second is denominator:
28852         5: {
28853             getValue: function (dataView, dataOffset, littleEndian) {
28854                 return dataView.getUint32(dataOffset, littleEndian) /
28855                     dataView.getUint32(dataOffset + 4, littleEndian);
28856             },
28857             size: 8
28858         },
28859         // slong, 32 bit signed int:
28860         9: {
28861             getValue: function (dataView, dataOffset, littleEndian) {
28862                 return dataView.getInt32(dataOffset, littleEndian);
28863             },
28864             size: 4
28865         },
28866         // srational, two slongs, first is numerator, second is denominator:
28867         10: {
28868             getValue: function (dataView, dataOffset, littleEndian) {
28869                 return dataView.getInt32(dataOffset, littleEndian) /
28870                     dataView.getInt32(dataOffset + 4, littleEndian);
28871             },
28872             size: 8
28873         }
28874     },
28875     
28876     footer : {
28877         STANDARD : [
28878             {
28879                 tag : 'div',
28880                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28881                 action : 'rotate-left',
28882                 cn : [
28883                     {
28884                         tag : 'button',
28885                         cls : 'btn btn-default',
28886                         html : '<i class="fa fa-undo"></i>'
28887                     }
28888                 ]
28889             },
28890             {
28891                 tag : 'div',
28892                 cls : 'btn-group roo-upload-cropbox-picture',
28893                 action : 'picture',
28894                 cn : [
28895                     {
28896                         tag : 'button',
28897                         cls : 'btn btn-default',
28898                         html : '<i class="fa fa-picture-o"></i>'
28899                     }
28900                 ]
28901             },
28902             {
28903                 tag : 'div',
28904                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28905                 action : 'rotate-right',
28906                 cn : [
28907                     {
28908                         tag : 'button',
28909                         cls : 'btn btn-default',
28910                         html : '<i class="fa fa-repeat"></i>'
28911                     }
28912                 ]
28913             }
28914         ],
28915         DOCUMENT : [
28916             {
28917                 tag : 'div',
28918                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28919                 action : 'rotate-left',
28920                 cn : [
28921                     {
28922                         tag : 'button',
28923                         cls : 'btn btn-default',
28924                         html : '<i class="fa fa-undo"></i>'
28925                     }
28926                 ]
28927             },
28928             {
28929                 tag : 'div',
28930                 cls : 'btn-group roo-upload-cropbox-download',
28931                 action : 'download',
28932                 cn : [
28933                     {
28934                         tag : 'button',
28935                         cls : 'btn btn-default',
28936                         html : '<i class="fa fa-download"></i>'
28937                     }
28938                 ]
28939             },
28940             {
28941                 tag : 'div',
28942                 cls : 'btn-group roo-upload-cropbox-crop',
28943                 action : 'crop',
28944                 cn : [
28945                     {
28946                         tag : 'button',
28947                         cls : 'btn btn-default',
28948                         html : '<i class="fa fa-crop"></i>'
28949                     }
28950                 ]
28951             },
28952             {
28953                 tag : 'div',
28954                 cls : 'btn-group roo-upload-cropbox-trash',
28955                 action : 'trash',
28956                 cn : [
28957                     {
28958                         tag : 'button',
28959                         cls : 'btn btn-default',
28960                         html : '<i class="fa fa-trash"></i>'
28961                     }
28962                 ]
28963             },
28964             {
28965                 tag : 'div',
28966                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28967                 action : 'rotate-right',
28968                 cn : [
28969                     {
28970                         tag : 'button',
28971                         cls : 'btn btn-default',
28972                         html : '<i class="fa fa-repeat"></i>'
28973                     }
28974                 ]
28975             }
28976         ],
28977         ROTATOR : [
28978             {
28979                 tag : 'div',
28980                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28981                 action : 'rotate-left',
28982                 cn : [
28983                     {
28984                         tag : 'button',
28985                         cls : 'btn btn-default',
28986                         html : '<i class="fa fa-undo"></i>'
28987                     }
28988                 ]
28989             },
28990             {
28991                 tag : 'div',
28992                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28993                 action : 'rotate-right',
28994                 cn : [
28995                     {
28996                         tag : 'button',
28997                         cls : 'btn btn-default',
28998                         html : '<i class="fa fa-repeat"></i>'
28999                     }
29000                 ]
29001             }
29002         ]
29003     }
29004 });
29005
29006 /*
29007 * Licence: LGPL
29008 */
29009
29010 /**
29011  * @class Roo.bootstrap.DocumentManager
29012  * @extends Roo.bootstrap.Component
29013  * Bootstrap DocumentManager class
29014  * @cfg {String} paramName default 'imageUpload'
29015  * @cfg {String} toolTipName default 'filename'
29016  * @cfg {String} method default POST
29017  * @cfg {String} url action url
29018  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29019  * @cfg {Boolean} multiple multiple upload default true
29020  * @cfg {Number} thumbSize default 300
29021  * @cfg {String} fieldLabel
29022  * @cfg {Number} labelWidth default 4
29023  * @cfg {String} labelAlign (left|top) default left
29024  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29025 * @cfg {Number} labellg set the width of label (1-12)
29026  * @cfg {Number} labelmd set the width of label (1-12)
29027  * @cfg {Number} labelsm set the width of label (1-12)
29028  * @cfg {Number} labelxs set the width of label (1-12)
29029  * 
29030  * @constructor
29031  * Create a new DocumentManager
29032  * @param {Object} config The config object
29033  */
29034
29035 Roo.bootstrap.DocumentManager = function(config){
29036     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29037     
29038     this.files = [];
29039     this.delegates = [];
29040     
29041     this.addEvents({
29042         /**
29043          * @event initial
29044          * Fire when initial the DocumentManager
29045          * @param {Roo.bootstrap.DocumentManager} this
29046          */
29047         "initial" : true,
29048         /**
29049          * @event inspect
29050          * inspect selected file
29051          * @param {Roo.bootstrap.DocumentManager} this
29052          * @param {File} file
29053          */
29054         "inspect" : true,
29055         /**
29056          * @event exception
29057          * Fire when xhr load exception
29058          * @param {Roo.bootstrap.DocumentManager} this
29059          * @param {XMLHttpRequest} xhr
29060          */
29061         "exception" : true,
29062         /**
29063          * @event afterupload
29064          * Fire when xhr load exception
29065          * @param {Roo.bootstrap.DocumentManager} this
29066          * @param {XMLHttpRequest} xhr
29067          */
29068         "afterupload" : true,
29069         /**
29070          * @event prepare
29071          * prepare the form data
29072          * @param {Roo.bootstrap.DocumentManager} this
29073          * @param {Object} formData
29074          */
29075         "prepare" : true,
29076         /**
29077          * @event remove
29078          * Fire when remove the file
29079          * @param {Roo.bootstrap.DocumentManager} this
29080          * @param {Object} file
29081          */
29082         "remove" : true,
29083         /**
29084          * @event refresh
29085          * Fire after refresh the file
29086          * @param {Roo.bootstrap.DocumentManager} this
29087          */
29088         "refresh" : true,
29089         /**
29090          * @event click
29091          * Fire after click the image
29092          * @param {Roo.bootstrap.DocumentManager} this
29093          * @param {Object} file
29094          */
29095         "click" : true,
29096         /**
29097          * @event edit
29098          * Fire when upload a image and editable set to true
29099          * @param {Roo.bootstrap.DocumentManager} this
29100          * @param {Object} file
29101          */
29102         "edit" : true,
29103         /**
29104          * @event beforeselectfile
29105          * Fire before select file
29106          * @param {Roo.bootstrap.DocumentManager} this
29107          */
29108         "beforeselectfile" : true,
29109         /**
29110          * @event process
29111          * Fire before process file
29112          * @param {Roo.bootstrap.DocumentManager} this
29113          * @param {Object} file
29114          */
29115         "process" : true,
29116         /**
29117          * @event previewrendered
29118          * Fire when preview rendered
29119          * @param {Roo.bootstrap.DocumentManager} this
29120          * @param {Object} file
29121          */
29122         "previewrendered" : true,
29123         /**
29124          */
29125         "previewResize" : true
29126         
29127     });
29128 };
29129
29130 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29131     
29132     boxes : 0,
29133     inputName : '',
29134     thumbSize : 300,
29135     multiple : true,
29136     files : false,
29137     method : 'POST',
29138     url : '',
29139     paramName : 'imageUpload',
29140     toolTipName : 'filename',
29141     fieldLabel : '',
29142     labelWidth : 4,
29143     labelAlign : 'left',
29144     editable : true,
29145     delegates : false,
29146     xhr : false, 
29147     
29148     labellg : 0,
29149     labelmd : 0,
29150     labelsm : 0,
29151     labelxs : 0,
29152     
29153     getAutoCreate : function()
29154     {   
29155         var managerWidget = {
29156             tag : 'div',
29157             cls : 'roo-document-manager',
29158             cn : [
29159                 {
29160                     tag : 'input',
29161                     cls : 'roo-document-manager-selector',
29162                     type : 'file'
29163                 },
29164                 {
29165                     tag : 'div',
29166                     cls : 'roo-document-manager-uploader',
29167                     cn : [
29168                         {
29169                             tag : 'div',
29170                             cls : 'roo-document-manager-upload-btn',
29171                             html : '<i class="fa fa-plus"></i>'
29172                         }
29173                     ]
29174                     
29175                 }
29176             ]
29177         };
29178         
29179         var content = [
29180             {
29181                 tag : 'div',
29182                 cls : 'column col-md-12',
29183                 cn : managerWidget
29184             }
29185         ];
29186         
29187         if(this.fieldLabel.length){
29188             
29189             content = [
29190                 {
29191                     tag : 'div',
29192                     cls : 'column col-md-12',
29193                     html : this.fieldLabel
29194                 },
29195                 {
29196                     tag : 'div',
29197                     cls : 'column col-md-12',
29198                     cn : managerWidget
29199                 }
29200             ];
29201
29202             if(this.labelAlign == 'left'){
29203                 content = [
29204                     {
29205                         tag : 'div',
29206                         cls : 'column',
29207                         html : this.fieldLabel
29208                     },
29209                     {
29210                         tag : 'div',
29211                         cls : 'column',
29212                         cn : managerWidget
29213                     }
29214                 ];
29215                 
29216                 if(this.labelWidth > 12){
29217                     content[0].style = "width: " + this.labelWidth + 'px';
29218                 }
29219
29220                 if(this.labelWidth < 13 && this.labelmd == 0){
29221                     this.labelmd = this.labelWidth;
29222                 }
29223
29224                 if(this.labellg > 0){
29225                     content[0].cls += ' col-lg-' + this.labellg;
29226                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29227                 }
29228
29229                 if(this.labelmd > 0){
29230                     content[0].cls += ' col-md-' + this.labelmd;
29231                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29232                 }
29233
29234                 if(this.labelsm > 0){
29235                     content[0].cls += ' col-sm-' + this.labelsm;
29236                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29237                 }
29238
29239                 if(this.labelxs > 0){
29240                     content[0].cls += ' col-xs-' + this.labelxs;
29241                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29242                 }
29243                 
29244             }
29245         }
29246         
29247         var cfg = {
29248             tag : 'div',
29249             cls : 'row clearfix',
29250             cn : content
29251         };
29252         
29253         return cfg;
29254         
29255     },
29256     
29257     initEvents : function()
29258     {
29259         this.managerEl = this.el.select('.roo-document-manager', true).first();
29260         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29261         
29262         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29263         this.selectorEl.hide();
29264         
29265         if(this.multiple){
29266             this.selectorEl.attr('multiple', 'multiple');
29267         }
29268         
29269         this.selectorEl.on('change', this.onFileSelected, this);
29270         
29271         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29272         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29273         
29274         this.uploader.on('click', this.onUploaderClick, this);
29275         
29276         this.renderProgressDialog();
29277         
29278         var _this = this;
29279         
29280         window.addEventListener("resize", function() { _this.refresh(); } );
29281         
29282         this.fireEvent('initial', this);
29283     },
29284     
29285     renderProgressDialog : function()
29286     {
29287         var _this = this;
29288         
29289         this.progressDialog = new Roo.bootstrap.Modal({
29290             cls : 'roo-document-manager-progress-dialog',
29291             allow_close : false,
29292             animate : false,
29293             title : '',
29294             buttons : [
29295                 {
29296                     name  :'cancel',
29297                     weight : 'danger',
29298                     html : 'Cancel'
29299                 }
29300             ], 
29301             listeners : { 
29302                 btnclick : function() {
29303                     _this.uploadCancel();
29304                     this.hide();
29305                 }
29306             }
29307         });
29308          
29309         this.progressDialog.render(Roo.get(document.body));
29310          
29311         this.progress = new Roo.bootstrap.Progress({
29312             cls : 'roo-document-manager-progress',
29313             active : true,
29314             striped : true
29315         });
29316         
29317         this.progress.render(this.progressDialog.getChildContainer());
29318         
29319         this.progressBar = new Roo.bootstrap.ProgressBar({
29320             cls : 'roo-document-manager-progress-bar',
29321             aria_valuenow : 0,
29322             aria_valuemin : 0,
29323             aria_valuemax : 12,
29324             panel : 'success'
29325         });
29326         
29327         this.progressBar.render(this.progress.getChildContainer());
29328     },
29329     
29330     onUploaderClick : function(e)
29331     {
29332         e.preventDefault();
29333      
29334         if(this.fireEvent('beforeselectfile', this) != false){
29335             this.selectorEl.dom.click();
29336         }
29337         
29338     },
29339     
29340     onFileSelected : function(e)
29341     {
29342         e.preventDefault();
29343         
29344         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29345             return;
29346         }
29347         
29348         Roo.each(this.selectorEl.dom.files, function(file){
29349             if(this.fireEvent('inspect', this, file) != false){
29350                 this.files.push(file);
29351             }
29352         }, this);
29353         
29354         this.queue();
29355         
29356     },
29357     
29358     queue : function()
29359     {
29360         this.selectorEl.dom.value = '';
29361         
29362         if(!this.files || !this.files.length){
29363             return;
29364         }
29365         
29366         if(this.boxes > 0 && this.files.length > this.boxes){
29367             this.files = this.files.slice(0, this.boxes);
29368         }
29369         
29370         this.uploader.show();
29371         
29372         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29373             this.uploader.hide();
29374         }
29375         
29376         var _this = this;
29377         
29378         var files = [];
29379         
29380         var docs = [];
29381         
29382         Roo.each(this.files, function(file){
29383             
29384             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29385                 var f = this.renderPreview(file);
29386                 files.push(f);
29387                 return;
29388             }
29389             
29390             if(file.type.indexOf('image') != -1){
29391                 this.delegates.push(
29392                     (function(){
29393                         _this.process(file);
29394                     }).createDelegate(this)
29395                 );
29396         
29397                 return;
29398             }
29399             
29400             docs.push(
29401                 (function(){
29402                     _this.process(file);
29403                 }).createDelegate(this)
29404             );
29405             
29406         }, this);
29407         
29408         this.files = files;
29409         
29410         this.delegates = this.delegates.concat(docs);
29411         
29412         if(!this.delegates.length){
29413             this.refresh();
29414             return;
29415         }
29416         
29417         this.progressBar.aria_valuemax = this.delegates.length;
29418         
29419         this.arrange();
29420         
29421         return;
29422     },
29423     
29424     arrange : function()
29425     {
29426         if(!this.delegates.length){
29427             this.progressDialog.hide();
29428             this.refresh();
29429             return;
29430         }
29431         
29432         var delegate = this.delegates.shift();
29433         
29434         this.progressDialog.show();
29435         
29436         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29437         
29438         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29439         
29440         delegate();
29441     },
29442     
29443     refresh : function()
29444     {
29445         this.uploader.show();
29446         
29447         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29448             this.uploader.hide();
29449         }
29450         
29451         Roo.isTouch ? this.closable(false) : this.closable(true);
29452         
29453         this.fireEvent('refresh', this);
29454     },
29455     
29456     onRemove : function(e, el, o)
29457     {
29458         e.preventDefault();
29459         
29460         this.fireEvent('remove', this, o);
29461         
29462     },
29463     
29464     remove : function(o)
29465     {
29466         var files = [];
29467         
29468         Roo.each(this.files, function(file){
29469             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29470                 files.push(file);
29471                 return;
29472             }
29473
29474             o.target.remove();
29475
29476         }, this);
29477         
29478         this.files = files;
29479         
29480         this.refresh();
29481     },
29482     
29483     clear : function()
29484     {
29485         Roo.each(this.files, function(file){
29486             if(!file.target){
29487                 return;
29488             }
29489             
29490             file.target.remove();
29491
29492         }, this);
29493         
29494         this.files = [];
29495         
29496         this.refresh();
29497     },
29498     
29499     onClick : function(e, el, o)
29500     {
29501         e.preventDefault();
29502         
29503         this.fireEvent('click', this, o);
29504         
29505     },
29506     
29507     closable : function(closable)
29508     {
29509         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29510             
29511             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29512             
29513             if(closable){
29514                 el.show();
29515                 return;
29516             }
29517             
29518             el.hide();
29519             
29520         }, this);
29521     },
29522     
29523     xhrOnLoad : function(xhr)
29524     {
29525         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29526             el.remove();
29527         }, this);
29528         
29529         if (xhr.readyState !== 4) {
29530             this.arrange();
29531             this.fireEvent('exception', this, xhr);
29532             return;
29533         }
29534
29535         var response = Roo.decode(xhr.responseText);
29536         
29537         if(!response.success){
29538             this.arrange();
29539             this.fireEvent('exception', this, xhr);
29540             return;
29541         }
29542         
29543         var file = this.renderPreview(response.data);
29544         
29545         this.files.push(file);
29546         
29547         this.arrange();
29548         
29549         this.fireEvent('afterupload', this, xhr);
29550         
29551     },
29552     
29553     xhrOnError : function(xhr)
29554     {
29555         Roo.log('xhr on error');
29556         
29557         var response = Roo.decode(xhr.responseText);
29558           
29559         Roo.log(response);
29560         
29561         this.arrange();
29562     },
29563     
29564     process : function(file)
29565     {
29566         if(this.fireEvent('process', this, file) !== false){
29567             if(this.editable && file.type.indexOf('image') != -1){
29568                 this.fireEvent('edit', this, file);
29569                 return;
29570             }
29571
29572             this.uploadStart(file, false);
29573
29574             return;
29575         }
29576         
29577     },
29578     
29579     uploadStart : function(file, crop)
29580     {
29581         this.xhr = new XMLHttpRequest();
29582         
29583         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29584             this.arrange();
29585             return;
29586         }
29587         
29588         file.xhr = this.xhr;
29589             
29590         this.managerEl.createChild({
29591             tag : 'div',
29592             cls : 'roo-document-manager-loading',
29593             cn : [
29594                 {
29595                     tag : 'div',
29596                     tooltip : file.name,
29597                     cls : 'roo-document-manager-thumb',
29598                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29599                 }
29600             ]
29601
29602         });
29603
29604         this.xhr.open(this.method, this.url, true);
29605         
29606         var headers = {
29607             "Accept": "application/json",
29608             "Cache-Control": "no-cache",
29609             "X-Requested-With": "XMLHttpRequest"
29610         };
29611         
29612         for (var headerName in headers) {
29613             var headerValue = headers[headerName];
29614             if (headerValue) {
29615                 this.xhr.setRequestHeader(headerName, headerValue);
29616             }
29617         }
29618         
29619         var _this = this;
29620         
29621         this.xhr.onload = function()
29622         {
29623             _this.xhrOnLoad(_this.xhr);
29624         }
29625         
29626         this.xhr.onerror = function()
29627         {
29628             _this.xhrOnError(_this.xhr);
29629         }
29630         
29631         var formData = new FormData();
29632
29633         formData.append('returnHTML', 'NO');
29634         
29635         if(crop){
29636             formData.append('crop', crop);
29637         }
29638         
29639         formData.append(this.paramName, file, file.name);
29640         
29641         var options = {
29642             file : file, 
29643             manually : false
29644         };
29645         
29646         if(this.fireEvent('prepare', this, formData, options) != false){
29647             
29648             if(options.manually){
29649                 return;
29650             }
29651             
29652             this.xhr.send(formData);
29653             return;
29654         };
29655         
29656         this.uploadCancel();
29657     },
29658     
29659     uploadCancel : function()
29660     {
29661         if (this.xhr) {
29662             this.xhr.abort();
29663         }
29664         
29665         this.delegates = [];
29666         
29667         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29668             el.remove();
29669         }, this);
29670         
29671         this.arrange();
29672     },
29673     
29674     renderPreview : function(file)
29675     {
29676         if(typeof(file.target) != 'undefined' && file.target){
29677             return file;
29678         }
29679         
29680         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29681         
29682         var previewEl = this.managerEl.createChild({
29683             tag : 'div',
29684             cls : 'roo-document-manager-preview',
29685             cn : [
29686                 {
29687                     tag : 'div',
29688                     tooltip : file[this.toolTipName],
29689                     cls : 'roo-document-manager-thumb',
29690                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29691                 },
29692                 {
29693                     tag : 'button',
29694                     cls : 'close',
29695                     html : '<i class="fa fa-times-circle"></i>'
29696                 }
29697             ]
29698         });
29699
29700         var close = previewEl.select('button.close', true).first();
29701
29702         close.on('click', this.onRemove, this, file);
29703
29704         file.target = previewEl;
29705
29706         var image = previewEl.select('img', true).first();
29707         
29708         var _this = this;
29709         
29710         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29711         
29712         image.on('click', this.onClick, this, file);
29713         
29714         this.fireEvent('previewrendered', this, file);
29715         
29716         return file;
29717         
29718     },
29719     
29720     onPreviewLoad : function(file, image)
29721     {
29722         if(typeof(file.target) == 'undefined' || !file.target){
29723             return;
29724         }
29725         
29726         var width = image.dom.naturalWidth || image.dom.width;
29727         var height = image.dom.naturalHeight || image.dom.height;
29728         
29729         if(!this.previewResize) {
29730             return;
29731         }
29732         
29733         if(width > height){
29734             file.target.addClass('wide');
29735             return;
29736         }
29737         
29738         file.target.addClass('tall');
29739         return;
29740         
29741     },
29742     
29743     uploadFromSource : function(file, crop)
29744     {
29745         this.xhr = new XMLHttpRequest();
29746         
29747         this.managerEl.createChild({
29748             tag : 'div',
29749             cls : 'roo-document-manager-loading',
29750             cn : [
29751                 {
29752                     tag : 'div',
29753                     tooltip : file.name,
29754                     cls : 'roo-document-manager-thumb',
29755                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29756                 }
29757             ]
29758
29759         });
29760
29761         this.xhr.open(this.method, this.url, true);
29762         
29763         var headers = {
29764             "Accept": "application/json",
29765             "Cache-Control": "no-cache",
29766             "X-Requested-With": "XMLHttpRequest"
29767         };
29768         
29769         for (var headerName in headers) {
29770             var headerValue = headers[headerName];
29771             if (headerValue) {
29772                 this.xhr.setRequestHeader(headerName, headerValue);
29773             }
29774         }
29775         
29776         var _this = this;
29777         
29778         this.xhr.onload = function()
29779         {
29780             _this.xhrOnLoad(_this.xhr);
29781         }
29782         
29783         this.xhr.onerror = function()
29784         {
29785             _this.xhrOnError(_this.xhr);
29786         }
29787         
29788         var formData = new FormData();
29789
29790         formData.append('returnHTML', 'NO');
29791         
29792         formData.append('crop', crop);
29793         
29794         if(typeof(file.filename) != 'undefined'){
29795             formData.append('filename', file.filename);
29796         }
29797         
29798         if(typeof(file.mimetype) != 'undefined'){
29799             formData.append('mimetype', file.mimetype);
29800         }
29801         
29802         Roo.log(formData);
29803         
29804         if(this.fireEvent('prepare', this, formData) != false){
29805             this.xhr.send(formData);
29806         };
29807     }
29808 });
29809
29810 /*
29811 * Licence: LGPL
29812 */
29813
29814 /**
29815  * @class Roo.bootstrap.DocumentViewer
29816  * @extends Roo.bootstrap.Component
29817  * Bootstrap DocumentViewer class
29818  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29819  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29820  * 
29821  * @constructor
29822  * Create a new DocumentViewer
29823  * @param {Object} config The config object
29824  */
29825
29826 Roo.bootstrap.DocumentViewer = function(config){
29827     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29828     
29829     this.addEvents({
29830         /**
29831          * @event initial
29832          * Fire after initEvent
29833          * @param {Roo.bootstrap.DocumentViewer} this
29834          */
29835         "initial" : true,
29836         /**
29837          * @event click
29838          * Fire after click
29839          * @param {Roo.bootstrap.DocumentViewer} this
29840          */
29841         "click" : true,
29842         /**
29843          * @event download
29844          * Fire after download button
29845          * @param {Roo.bootstrap.DocumentViewer} this
29846          */
29847         "download" : true,
29848         /**
29849          * @event trash
29850          * Fire after trash button
29851          * @param {Roo.bootstrap.DocumentViewer} this
29852          */
29853         "trash" : true
29854         
29855     });
29856 };
29857
29858 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29859     
29860     showDownload : true,
29861     
29862     showTrash : true,
29863     
29864     getAutoCreate : function()
29865     {
29866         var cfg = {
29867             tag : 'div',
29868             cls : 'roo-document-viewer',
29869             cn : [
29870                 {
29871                     tag : 'div',
29872                     cls : 'roo-document-viewer-body',
29873                     cn : [
29874                         {
29875                             tag : 'div',
29876                             cls : 'roo-document-viewer-thumb',
29877                             cn : [
29878                                 {
29879                                     tag : 'img',
29880                                     cls : 'roo-document-viewer-image'
29881                                 }
29882                             ]
29883                         }
29884                     ]
29885                 },
29886                 {
29887                     tag : 'div',
29888                     cls : 'roo-document-viewer-footer',
29889                     cn : {
29890                         tag : 'div',
29891                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29892                         cn : [
29893                             {
29894                                 tag : 'div',
29895                                 cls : 'btn-group roo-document-viewer-download',
29896                                 cn : [
29897                                     {
29898                                         tag : 'button',
29899                                         cls : 'btn btn-default',
29900                                         html : '<i class="fa fa-download"></i>'
29901                                     }
29902                                 ]
29903                             },
29904                             {
29905                                 tag : 'div',
29906                                 cls : 'btn-group roo-document-viewer-trash',
29907                                 cn : [
29908                                     {
29909                                         tag : 'button',
29910                                         cls : 'btn btn-default',
29911                                         html : '<i class="fa fa-trash"></i>'
29912                                     }
29913                                 ]
29914                             }
29915                         ]
29916                     }
29917                 }
29918             ]
29919         };
29920         
29921         return cfg;
29922     },
29923     
29924     initEvents : function()
29925     {
29926         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29927         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29928         
29929         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29930         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29931         
29932         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29933         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29934         
29935         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29936         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29937         
29938         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29939         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29940         
29941         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29942         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29943         
29944         this.bodyEl.on('click', this.onClick, this);
29945         this.downloadBtn.on('click', this.onDownload, this);
29946         this.trashBtn.on('click', this.onTrash, this);
29947         
29948         this.downloadBtn.hide();
29949         this.trashBtn.hide();
29950         
29951         if(this.showDownload){
29952             this.downloadBtn.show();
29953         }
29954         
29955         if(this.showTrash){
29956             this.trashBtn.show();
29957         }
29958         
29959         if(!this.showDownload && !this.showTrash) {
29960             this.footerEl.hide();
29961         }
29962         
29963     },
29964     
29965     initial : function()
29966     {
29967         this.fireEvent('initial', this);
29968         
29969     },
29970     
29971     onClick : function(e)
29972     {
29973         e.preventDefault();
29974         
29975         this.fireEvent('click', this);
29976     },
29977     
29978     onDownload : function(e)
29979     {
29980         e.preventDefault();
29981         
29982         this.fireEvent('download', this);
29983     },
29984     
29985     onTrash : function(e)
29986     {
29987         e.preventDefault();
29988         
29989         this.fireEvent('trash', this);
29990     }
29991     
29992 });
29993 /*
29994  * - LGPL
29995  *
29996  * nav progress bar
29997  * 
29998  */
29999
30000 /**
30001  * @class Roo.bootstrap.NavProgressBar
30002  * @extends Roo.bootstrap.Component
30003  * Bootstrap NavProgressBar class
30004  * 
30005  * @constructor
30006  * Create a new nav progress bar
30007  * @param {Object} config The config object
30008  */
30009
30010 Roo.bootstrap.NavProgressBar = function(config){
30011     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30012
30013     this.bullets = this.bullets || [];
30014    
30015 //    Roo.bootstrap.NavProgressBar.register(this);
30016      this.addEvents({
30017         /**
30018              * @event changed
30019              * Fires when the active item changes
30020              * @param {Roo.bootstrap.NavProgressBar} this
30021              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30022              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
30023          */
30024         'changed': true
30025      });
30026     
30027 };
30028
30029 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
30030     
30031     bullets : [],
30032     barItems : [],
30033     
30034     getAutoCreate : function()
30035     {
30036         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30037         
30038         cfg = {
30039             tag : 'div',
30040             cls : 'roo-navigation-bar-group',
30041             cn : [
30042                 {
30043                     tag : 'div',
30044                     cls : 'roo-navigation-top-bar'
30045                 },
30046                 {
30047                     tag : 'div',
30048                     cls : 'roo-navigation-bullets-bar',
30049                     cn : [
30050                         {
30051                             tag : 'ul',
30052                             cls : 'roo-navigation-bar'
30053                         }
30054                     ]
30055                 },
30056                 
30057                 {
30058                     tag : 'div',
30059                     cls : 'roo-navigation-bottom-bar'
30060                 }
30061             ]
30062             
30063         };
30064         
30065         return cfg;
30066         
30067     },
30068     
30069     initEvents: function() 
30070     {
30071         
30072     },
30073     
30074     onRender : function(ct, position) 
30075     {
30076         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30077         
30078         if(this.bullets.length){
30079             Roo.each(this.bullets, function(b){
30080                this.addItem(b);
30081             }, this);
30082         }
30083         
30084         this.format();
30085         
30086     },
30087     
30088     addItem : function(cfg)
30089     {
30090         var item = new Roo.bootstrap.NavProgressItem(cfg);
30091         
30092         item.parentId = this.id;
30093         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30094         
30095         if(cfg.html){
30096             var top = new Roo.bootstrap.Element({
30097                 tag : 'div',
30098                 cls : 'roo-navigation-bar-text'
30099             });
30100             
30101             var bottom = new Roo.bootstrap.Element({
30102                 tag : 'div',
30103                 cls : 'roo-navigation-bar-text'
30104             });
30105             
30106             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30107             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30108             
30109             var topText = new Roo.bootstrap.Element({
30110                 tag : 'span',
30111                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30112             });
30113             
30114             var bottomText = new Roo.bootstrap.Element({
30115                 tag : 'span',
30116                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30117             });
30118             
30119             topText.onRender(top.el, null);
30120             bottomText.onRender(bottom.el, null);
30121             
30122             item.topEl = top;
30123             item.bottomEl = bottom;
30124         }
30125         
30126         this.barItems.push(item);
30127         
30128         return item;
30129     },
30130     
30131     getActive : function()
30132     {
30133         var active = false;
30134         
30135         Roo.each(this.barItems, function(v){
30136             
30137             if (!v.isActive()) {
30138                 return;
30139             }
30140             
30141             active = v;
30142             return false;
30143             
30144         });
30145         
30146         return active;
30147     },
30148     
30149     setActiveItem : function(item)
30150     {
30151         var prev = false;
30152         
30153         Roo.each(this.barItems, function(v){
30154             if (v.rid == item.rid) {
30155                 return ;
30156             }
30157             
30158             if (v.isActive()) {
30159                 v.setActive(false);
30160                 prev = v;
30161             }
30162         });
30163
30164         item.setActive(true);
30165         
30166         this.fireEvent('changed', this, item, prev);
30167     },
30168     
30169     getBarItem: function(rid)
30170     {
30171         var ret = false;
30172         
30173         Roo.each(this.barItems, function(e) {
30174             if (e.rid != rid) {
30175                 return;
30176             }
30177             
30178             ret =  e;
30179             return false;
30180         });
30181         
30182         return ret;
30183     },
30184     
30185     indexOfItem : function(item)
30186     {
30187         var index = false;
30188         
30189         Roo.each(this.barItems, function(v, i){
30190             
30191             if (v.rid != item.rid) {
30192                 return;
30193             }
30194             
30195             index = i;
30196             return false
30197         });
30198         
30199         return index;
30200     },
30201     
30202     setActiveNext : function()
30203     {
30204         var i = this.indexOfItem(this.getActive());
30205         
30206         if (i > this.barItems.length) {
30207             return;
30208         }
30209         
30210         this.setActiveItem(this.barItems[i+1]);
30211     },
30212     
30213     setActivePrev : function()
30214     {
30215         var i = this.indexOfItem(this.getActive());
30216         
30217         if (i  < 1) {
30218             return;
30219         }
30220         
30221         this.setActiveItem(this.barItems[i-1]);
30222     },
30223     
30224     format : function()
30225     {
30226         if(!this.barItems.length){
30227             return;
30228         }
30229      
30230         var width = 100 / this.barItems.length;
30231         
30232         Roo.each(this.barItems, function(i){
30233             i.el.setStyle('width', width + '%');
30234             i.topEl.el.setStyle('width', width + '%');
30235             i.bottomEl.el.setStyle('width', width + '%');
30236         }, this);
30237         
30238     }
30239     
30240 });
30241 /*
30242  * - LGPL
30243  *
30244  * Nav Progress Item
30245  * 
30246  */
30247
30248 /**
30249  * @class Roo.bootstrap.NavProgressItem
30250  * @extends Roo.bootstrap.Component
30251  * Bootstrap NavProgressItem class
30252  * @cfg {String} rid the reference id
30253  * @cfg {Boolean} active (true|false) Is item active default false
30254  * @cfg {Boolean} disabled (true|false) Is item active default false
30255  * @cfg {String} html
30256  * @cfg {String} position (top|bottom) text position default bottom
30257  * @cfg {String} icon show icon instead of number
30258  * 
30259  * @constructor
30260  * Create a new NavProgressItem
30261  * @param {Object} config The config object
30262  */
30263 Roo.bootstrap.NavProgressItem = function(config){
30264     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30265     this.addEvents({
30266         // raw events
30267         /**
30268          * @event click
30269          * The raw click event for the entire grid.
30270          * @param {Roo.bootstrap.NavProgressItem} this
30271          * @param {Roo.EventObject} e
30272          */
30273         "click" : true
30274     });
30275    
30276 };
30277
30278 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30279     
30280     rid : '',
30281     active : false,
30282     disabled : false,
30283     html : '',
30284     position : 'bottom',
30285     icon : false,
30286     
30287     getAutoCreate : function()
30288     {
30289         var iconCls = 'roo-navigation-bar-item-icon';
30290         
30291         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30292         
30293         var cfg = {
30294             tag: 'li',
30295             cls: 'roo-navigation-bar-item',
30296             cn : [
30297                 {
30298                     tag : 'i',
30299                     cls : iconCls
30300                 }
30301             ]
30302         };
30303         
30304         if(this.active){
30305             cfg.cls += ' active';
30306         }
30307         if(this.disabled){
30308             cfg.cls += ' disabled';
30309         }
30310         
30311         return cfg;
30312     },
30313     
30314     disable : function()
30315     {
30316         this.setDisabled(true);
30317     },
30318     
30319     enable : function()
30320     {
30321         this.setDisabled(false);
30322     },
30323     
30324     initEvents: function() 
30325     {
30326         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30327         
30328         this.iconEl.on('click', this.onClick, this);
30329     },
30330     
30331     onClick : function(e)
30332     {
30333         e.preventDefault();
30334         
30335         if(this.disabled){
30336             return;
30337         }
30338         
30339         if(this.fireEvent('click', this, e) === false){
30340             return;
30341         };
30342         
30343         this.parent().setActiveItem(this);
30344     },
30345     
30346     isActive: function () 
30347     {
30348         return this.active;
30349     },
30350     
30351     setActive : function(state)
30352     {
30353         if(this.active == state){
30354             return;
30355         }
30356         
30357         this.active = state;
30358         
30359         if (state) {
30360             this.el.addClass('active');
30361             return;
30362         }
30363         
30364         this.el.removeClass('active');
30365         
30366         return;
30367     },
30368     
30369     setDisabled : function(state)
30370     {
30371         if(this.disabled == state){
30372             return;
30373         }
30374         
30375         this.disabled = state;
30376         
30377         if (state) {
30378             this.el.addClass('disabled');
30379             return;
30380         }
30381         
30382         this.el.removeClass('disabled');
30383     },
30384     
30385     tooltipEl : function()
30386     {
30387         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30388     }
30389 });
30390  
30391
30392  /*
30393  * - LGPL
30394  *
30395  * FieldLabel
30396  * 
30397  */
30398
30399 /**
30400  * @class Roo.bootstrap.FieldLabel
30401  * @extends Roo.bootstrap.Component
30402  * Bootstrap FieldLabel class
30403  * @cfg {String} html contents of the element
30404  * @cfg {String} tag tag of the element default label
30405  * @cfg {String} cls class of the element
30406  * @cfg {String} target label target 
30407  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30408  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30409  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30410  * @cfg {String} iconTooltip default "This field is required"
30411  * @cfg {String} indicatorpos (left|right) default left
30412  * 
30413  * @constructor
30414  * Create a new FieldLabel
30415  * @param {Object} config The config object
30416  */
30417
30418 Roo.bootstrap.FieldLabel = function(config){
30419     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30420     
30421     this.addEvents({
30422             /**
30423              * @event invalid
30424              * Fires after the field has been marked as invalid.
30425              * @param {Roo.form.FieldLabel} this
30426              * @param {String} msg The validation message
30427              */
30428             invalid : true,
30429             /**
30430              * @event valid
30431              * Fires after the field has been validated with no errors.
30432              * @param {Roo.form.FieldLabel} this
30433              */
30434             valid : true
30435         });
30436 };
30437
30438 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30439     
30440     tag: 'label',
30441     cls: '',
30442     html: '',
30443     target: '',
30444     allowBlank : true,
30445     invalidClass : 'has-warning',
30446     validClass : 'has-success',
30447     iconTooltip : 'This field is required',
30448     indicatorpos : 'left',
30449     
30450     getAutoCreate : function(){
30451         
30452         var cls = "";
30453         if (!this.allowBlank) {
30454             cls  = "visible";
30455         }
30456         
30457         var cfg = {
30458             tag : this.tag,
30459             cls : 'roo-bootstrap-field-label ' + this.cls,
30460             for : this.target,
30461             cn : [
30462                 {
30463                     tag : 'i',
30464                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30465                     tooltip : this.iconTooltip
30466                 },
30467                 {
30468                     tag : 'span',
30469                     html : this.html
30470                 }
30471             ] 
30472         };
30473         
30474         if(this.indicatorpos == 'right'){
30475             var cfg = {
30476                 tag : this.tag,
30477                 cls : 'roo-bootstrap-field-label ' + this.cls,
30478                 for : this.target,
30479                 cn : [
30480                     {
30481                         tag : 'span',
30482                         html : this.html
30483                     },
30484                     {
30485                         tag : 'i',
30486                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30487                         tooltip : this.iconTooltip
30488                     }
30489                 ] 
30490             };
30491         }
30492         
30493         return cfg;
30494     },
30495     
30496     initEvents: function() 
30497     {
30498         Roo.bootstrap.Element.superclass.initEvents.call(this);
30499         
30500         this.indicator = this.indicatorEl();
30501         
30502         if(this.indicator){
30503             this.indicator.removeClass('visible');
30504             this.indicator.addClass('invisible');
30505         }
30506         
30507         Roo.bootstrap.FieldLabel.register(this);
30508     },
30509     
30510     indicatorEl : function()
30511     {
30512         var indicator = this.el.select('i.roo-required-indicator',true).first();
30513         
30514         if(!indicator){
30515             return false;
30516         }
30517         
30518         return indicator;
30519         
30520     },
30521     
30522     /**
30523      * Mark this field as valid
30524      */
30525     markValid : function()
30526     {
30527         if(this.indicator){
30528             this.indicator.removeClass('visible');
30529             this.indicator.addClass('invisible');
30530         }
30531         if (Roo.bootstrap.version == 3) {
30532             this.el.removeClass(this.invalidClass);
30533             this.el.addClass(this.validClass);
30534         } else {
30535             this.el.removeClass('is-invalid');
30536             this.el.addClass('is-valid');
30537         }
30538         
30539         
30540         this.fireEvent('valid', this);
30541     },
30542     
30543     /**
30544      * Mark this field as invalid
30545      * @param {String} msg The validation message
30546      */
30547     markInvalid : function(msg)
30548     {
30549         if(this.indicator){
30550             this.indicator.removeClass('invisible');
30551             this.indicator.addClass('visible');
30552         }
30553           if (Roo.bootstrap.version == 3) {
30554             this.el.removeClass(this.validClass);
30555             this.el.addClass(this.invalidClass);
30556         } else {
30557             this.el.removeClass('is-valid');
30558             this.el.addClass('is-invalid');
30559         }
30560         
30561         
30562         this.fireEvent('invalid', this, msg);
30563     }
30564     
30565    
30566 });
30567
30568 Roo.apply(Roo.bootstrap.FieldLabel, {
30569     
30570     groups: {},
30571     
30572      /**
30573     * register a FieldLabel Group
30574     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30575     */
30576     register : function(label)
30577     {
30578         if(this.groups.hasOwnProperty(label.target)){
30579             return;
30580         }
30581      
30582         this.groups[label.target] = label;
30583         
30584     },
30585     /**
30586     * fetch a FieldLabel Group based on the target
30587     * @param {string} target
30588     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30589     */
30590     get: function(target) {
30591         if (typeof(this.groups[target]) == 'undefined') {
30592             return false;
30593         }
30594         
30595         return this.groups[target] ;
30596     }
30597 });
30598
30599  
30600
30601  /*
30602  * - LGPL
30603  *
30604  * page DateSplitField.
30605  * 
30606  */
30607
30608
30609 /**
30610  * @class Roo.bootstrap.DateSplitField
30611  * @extends Roo.bootstrap.Component
30612  * Bootstrap DateSplitField class
30613  * @cfg {string} fieldLabel - the label associated
30614  * @cfg {Number} labelWidth set the width of label (0-12)
30615  * @cfg {String} labelAlign (top|left)
30616  * @cfg {Boolean} dayAllowBlank (true|false) default false
30617  * @cfg {Boolean} monthAllowBlank (true|false) default false
30618  * @cfg {Boolean} yearAllowBlank (true|false) default false
30619  * @cfg {string} dayPlaceholder 
30620  * @cfg {string} monthPlaceholder
30621  * @cfg {string} yearPlaceholder
30622  * @cfg {string} dayFormat default 'd'
30623  * @cfg {string} monthFormat default 'm'
30624  * @cfg {string} yearFormat default 'Y'
30625  * @cfg {Number} labellg set the width of label (1-12)
30626  * @cfg {Number} labelmd set the width of label (1-12)
30627  * @cfg {Number} labelsm set the width of label (1-12)
30628  * @cfg {Number} labelxs set the width of label (1-12)
30629
30630  *     
30631  * @constructor
30632  * Create a new DateSplitField
30633  * @param {Object} config The config object
30634  */
30635
30636 Roo.bootstrap.DateSplitField = function(config){
30637     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30638     
30639     this.addEvents({
30640         // raw events
30641          /**
30642          * @event years
30643          * getting the data of years
30644          * @param {Roo.bootstrap.DateSplitField} this
30645          * @param {Object} years
30646          */
30647         "years" : true,
30648         /**
30649          * @event days
30650          * getting the data of days
30651          * @param {Roo.bootstrap.DateSplitField} this
30652          * @param {Object} days
30653          */
30654         "days" : true,
30655         /**
30656          * @event invalid
30657          * Fires after the field has been marked as invalid.
30658          * @param {Roo.form.Field} this
30659          * @param {String} msg The validation message
30660          */
30661         invalid : true,
30662        /**
30663          * @event valid
30664          * Fires after the field has been validated with no errors.
30665          * @param {Roo.form.Field} this
30666          */
30667         valid : true
30668     });
30669 };
30670
30671 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30672     
30673     fieldLabel : '',
30674     labelAlign : 'top',
30675     labelWidth : 3,
30676     dayAllowBlank : false,
30677     monthAllowBlank : false,
30678     yearAllowBlank : false,
30679     dayPlaceholder : '',
30680     monthPlaceholder : '',
30681     yearPlaceholder : '',
30682     dayFormat : 'd',
30683     monthFormat : 'm',
30684     yearFormat : 'Y',
30685     isFormField : true,
30686     labellg : 0,
30687     labelmd : 0,
30688     labelsm : 0,
30689     labelxs : 0,
30690     
30691     getAutoCreate : function()
30692     {
30693         var cfg = {
30694             tag : 'div',
30695             cls : 'row roo-date-split-field-group',
30696             cn : [
30697                 {
30698                     tag : 'input',
30699                     type : 'hidden',
30700                     cls : 'form-hidden-field roo-date-split-field-group-value',
30701                     name : this.name
30702                 }
30703             ]
30704         };
30705         
30706         var labelCls = 'col-md-12';
30707         var contentCls = 'col-md-4';
30708         
30709         if(this.fieldLabel){
30710             
30711             var label = {
30712                 tag : 'div',
30713                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30714                 cn : [
30715                     {
30716                         tag : 'label',
30717                         html : this.fieldLabel
30718                     }
30719                 ]
30720             };
30721             
30722             if(this.labelAlign == 'left'){
30723             
30724                 if(this.labelWidth > 12){
30725                     label.style = "width: " + this.labelWidth + 'px';
30726                 }
30727
30728                 if(this.labelWidth < 13 && this.labelmd == 0){
30729                     this.labelmd = this.labelWidth;
30730                 }
30731
30732                 if(this.labellg > 0){
30733                     labelCls = ' col-lg-' + this.labellg;
30734                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30735                 }
30736
30737                 if(this.labelmd > 0){
30738                     labelCls = ' col-md-' + this.labelmd;
30739                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30740                 }
30741
30742                 if(this.labelsm > 0){
30743                     labelCls = ' col-sm-' + this.labelsm;
30744                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30745                 }
30746
30747                 if(this.labelxs > 0){
30748                     labelCls = ' col-xs-' + this.labelxs;
30749                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30750                 }
30751             }
30752             
30753             label.cls += ' ' + labelCls;
30754             
30755             cfg.cn.push(label);
30756         }
30757         
30758         Roo.each(['day', 'month', 'year'], function(t){
30759             cfg.cn.push({
30760                 tag : 'div',
30761                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30762             });
30763         }, this);
30764         
30765         return cfg;
30766     },
30767     
30768     inputEl: function ()
30769     {
30770         return this.el.select('.roo-date-split-field-group-value', true).first();
30771     },
30772     
30773     onRender : function(ct, position) 
30774     {
30775         var _this = this;
30776         
30777         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30778         
30779         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30780         
30781         this.dayField = new Roo.bootstrap.ComboBox({
30782             allowBlank : this.dayAllowBlank,
30783             alwaysQuery : true,
30784             displayField : 'value',
30785             editable : false,
30786             fieldLabel : '',
30787             forceSelection : true,
30788             mode : 'local',
30789             placeholder : this.dayPlaceholder,
30790             selectOnFocus : true,
30791             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30792             triggerAction : 'all',
30793             typeAhead : true,
30794             valueField : 'value',
30795             store : new Roo.data.SimpleStore({
30796                 data : (function() {    
30797                     var days = [];
30798                     _this.fireEvent('days', _this, days);
30799                     return days;
30800                 })(),
30801                 fields : [ 'value' ]
30802             }),
30803             listeners : {
30804                 select : function (_self, record, index)
30805                 {
30806                     _this.setValue(_this.getValue());
30807                 }
30808             }
30809         });
30810
30811         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30812         
30813         this.monthField = new Roo.bootstrap.MonthField({
30814             after : '<i class=\"fa fa-calendar\"></i>',
30815             allowBlank : this.monthAllowBlank,
30816             placeholder : this.monthPlaceholder,
30817             readOnly : true,
30818             listeners : {
30819                 render : function (_self)
30820                 {
30821                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30822                         e.preventDefault();
30823                         _self.focus();
30824                     });
30825                 },
30826                 select : function (_self, oldvalue, newvalue)
30827                 {
30828                     _this.setValue(_this.getValue());
30829                 }
30830             }
30831         });
30832         
30833         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30834         
30835         this.yearField = new Roo.bootstrap.ComboBox({
30836             allowBlank : this.yearAllowBlank,
30837             alwaysQuery : true,
30838             displayField : 'value',
30839             editable : false,
30840             fieldLabel : '',
30841             forceSelection : true,
30842             mode : 'local',
30843             placeholder : this.yearPlaceholder,
30844             selectOnFocus : true,
30845             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30846             triggerAction : 'all',
30847             typeAhead : true,
30848             valueField : 'value',
30849             store : new Roo.data.SimpleStore({
30850                 data : (function() {
30851                     var years = [];
30852                     _this.fireEvent('years', _this, years);
30853                     return years;
30854                 })(),
30855                 fields : [ 'value' ]
30856             }),
30857             listeners : {
30858                 select : function (_self, record, index)
30859                 {
30860                     _this.setValue(_this.getValue());
30861                 }
30862             }
30863         });
30864
30865         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30866     },
30867     
30868     setValue : function(v, format)
30869     {
30870         this.inputEl.dom.value = v;
30871         
30872         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30873         
30874         var d = Date.parseDate(v, f);
30875         
30876         if(!d){
30877             this.validate();
30878             return;
30879         }
30880         
30881         this.setDay(d.format(this.dayFormat));
30882         this.setMonth(d.format(this.monthFormat));
30883         this.setYear(d.format(this.yearFormat));
30884         
30885         this.validate();
30886         
30887         return;
30888     },
30889     
30890     setDay : function(v)
30891     {
30892         this.dayField.setValue(v);
30893         this.inputEl.dom.value = this.getValue();
30894         this.validate();
30895         return;
30896     },
30897     
30898     setMonth : function(v)
30899     {
30900         this.monthField.setValue(v, true);
30901         this.inputEl.dom.value = this.getValue();
30902         this.validate();
30903         return;
30904     },
30905     
30906     setYear : function(v)
30907     {
30908         this.yearField.setValue(v);
30909         this.inputEl.dom.value = this.getValue();
30910         this.validate();
30911         return;
30912     },
30913     
30914     getDay : function()
30915     {
30916         return this.dayField.getValue();
30917     },
30918     
30919     getMonth : function()
30920     {
30921         return this.monthField.getValue();
30922     },
30923     
30924     getYear : function()
30925     {
30926         return this.yearField.getValue();
30927     },
30928     
30929     getValue : function()
30930     {
30931         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30932         
30933         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30934         
30935         return date;
30936     },
30937     
30938     reset : function()
30939     {
30940         this.setDay('');
30941         this.setMonth('');
30942         this.setYear('');
30943         this.inputEl.dom.value = '';
30944         this.validate();
30945         return;
30946     },
30947     
30948     validate : function()
30949     {
30950         var d = this.dayField.validate();
30951         var m = this.monthField.validate();
30952         var y = this.yearField.validate();
30953         
30954         var valid = true;
30955         
30956         if(
30957                 (!this.dayAllowBlank && !d) ||
30958                 (!this.monthAllowBlank && !m) ||
30959                 (!this.yearAllowBlank && !y)
30960         ){
30961             valid = false;
30962         }
30963         
30964         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30965             return valid;
30966         }
30967         
30968         if(valid){
30969             this.markValid();
30970             return valid;
30971         }
30972         
30973         this.markInvalid();
30974         
30975         return valid;
30976     },
30977     
30978     markValid : function()
30979     {
30980         
30981         var label = this.el.select('label', true).first();
30982         var icon = this.el.select('i.fa-star', true).first();
30983
30984         if(label && icon){
30985             icon.remove();
30986         }
30987         
30988         this.fireEvent('valid', this);
30989     },
30990     
30991      /**
30992      * Mark this field as invalid
30993      * @param {String} msg The validation message
30994      */
30995     markInvalid : function(msg)
30996     {
30997         
30998         var label = this.el.select('label', true).first();
30999         var icon = this.el.select('i.fa-star', true).first();
31000
31001         if(label && !icon){
31002             this.el.select('.roo-date-split-field-label', true).createChild({
31003                 tag : 'i',
31004                 cls : 'text-danger fa fa-lg fa-star',
31005                 tooltip : 'This field is required',
31006                 style : 'margin-right:5px;'
31007             }, label, true);
31008         }
31009         
31010         this.fireEvent('invalid', this, msg);
31011     },
31012     
31013     clearInvalid : function()
31014     {
31015         var label = this.el.select('label', true).first();
31016         var icon = this.el.select('i.fa-star', true).first();
31017
31018         if(label && icon){
31019             icon.remove();
31020         }
31021         
31022         this.fireEvent('valid', this);
31023     },
31024     
31025     getName: function()
31026     {
31027         return this.name;
31028     }
31029     
31030 });
31031
31032  /**
31033  *
31034  * This is based on 
31035  * http://masonry.desandro.com
31036  *
31037  * The idea is to render all the bricks based on vertical width...
31038  *
31039  * The original code extends 'outlayer' - we might need to use that....
31040  * 
31041  */
31042
31043
31044 /**
31045  * @class Roo.bootstrap.LayoutMasonry
31046  * @extends Roo.bootstrap.Component
31047  * Bootstrap Layout Masonry class
31048  * 
31049  * @constructor
31050  * Create a new Element
31051  * @param {Object} config The config object
31052  */
31053
31054 Roo.bootstrap.LayoutMasonry = function(config){
31055     
31056     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31057     
31058     this.bricks = [];
31059     
31060     Roo.bootstrap.LayoutMasonry.register(this);
31061     
31062     this.addEvents({
31063         // raw events
31064         /**
31065          * @event layout
31066          * Fire after layout the items
31067          * @param {Roo.bootstrap.LayoutMasonry} this
31068          * @param {Roo.EventObject} e
31069          */
31070         "layout" : true
31071     });
31072     
31073 };
31074
31075 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31076     
31077     /**
31078      * @cfg {Boolean} isLayoutInstant = no animation?
31079      */   
31080     isLayoutInstant : false, // needed?
31081    
31082     /**
31083      * @cfg {Number} boxWidth  width of the columns
31084      */   
31085     boxWidth : 450,
31086     
31087       /**
31088      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31089      */   
31090     boxHeight : 0,
31091     
31092     /**
31093      * @cfg {Number} padWidth padding below box..
31094      */   
31095     padWidth : 10, 
31096     
31097     /**
31098      * @cfg {Number} gutter gutter width..
31099      */   
31100     gutter : 10,
31101     
31102      /**
31103      * @cfg {Number} maxCols maximum number of columns
31104      */   
31105     
31106     maxCols: 0,
31107     
31108     /**
31109      * @cfg {Boolean} isAutoInitial defalut true
31110      */   
31111     isAutoInitial : true, 
31112     
31113     containerWidth: 0,
31114     
31115     /**
31116      * @cfg {Boolean} isHorizontal defalut false
31117      */   
31118     isHorizontal : false, 
31119
31120     currentSize : null,
31121     
31122     tag: 'div',
31123     
31124     cls: '',
31125     
31126     bricks: null, //CompositeElement
31127     
31128     cols : 1,
31129     
31130     _isLayoutInited : false,
31131     
31132 //    isAlternative : false, // only use for vertical layout...
31133     
31134     /**
31135      * @cfg {Number} alternativePadWidth padding below box..
31136      */   
31137     alternativePadWidth : 50,
31138     
31139     selectedBrick : [],
31140     
31141     getAutoCreate : function(){
31142         
31143         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31144         
31145         var cfg = {
31146             tag: this.tag,
31147             cls: 'blog-masonary-wrapper ' + this.cls,
31148             cn : {
31149                 cls : 'mas-boxes masonary'
31150             }
31151         };
31152         
31153         return cfg;
31154     },
31155     
31156     getChildContainer: function( )
31157     {
31158         if (this.boxesEl) {
31159             return this.boxesEl;
31160         }
31161         
31162         this.boxesEl = this.el.select('.mas-boxes').first();
31163         
31164         return this.boxesEl;
31165     },
31166     
31167     
31168     initEvents : function()
31169     {
31170         var _this = this;
31171         
31172         if(this.isAutoInitial){
31173             Roo.log('hook children rendered');
31174             this.on('childrenrendered', function() {
31175                 Roo.log('children rendered');
31176                 _this.initial();
31177             } ,this);
31178         }
31179     },
31180     
31181     initial : function()
31182     {
31183         this.selectedBrick = [];
31184         
31185         this.currentSize = this.el.getBox(true);
31186         
31187         Roo.EventManager.onWindowResize(this.resize, this); 
31188
31189         if(!this.isAutoInitial){
31190             this.layout();
31191             return;
31192         }
31193         
31194         this.layout();
31195         
31196         return;
31197         //this.layout.defer(500,this);
31198         
31199     },
31200     
31201     resize : function()
31202     {
31203         var cs = this.el.getBox(true);
31204         
31205         if (
31206                 this.currentSize.width == cs.width && 
31207                 this.currentSize.x == cs.x && 
31208                 this.currentSize.height == cs.height && 
31209                 this.currentSize.y == cs.y 
31210         ) {
31211             Roo.log("no change in with or X or Y");
31212             return;
31213         }
31214         
31215         this.currentSize = cs;
31216         
31217         this.layout();
31218         
31219     },
31220     
31221     layout : function()
31222     {   
31223         this._resetLayout();
31224         
31225         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31226         
31227         this.layoutItems( isInstant );
31228       
31229         this._isLayoutInited = true;
31230         
31231         this.fireEvent('layout', this);
31232         
31233     },
31234     
31235     _resetLayout : function()
31236     {
31237         if(this.isHorizontal){
31238             this.horizontalMeasureColumns();
31239             return;
31240         }
31241         
31242         this.verticalMeasureColumns();
31243         
31244     },
31245     
31246     verticalMeasureColumns : function()
31247     {
31248         this.getContainerWidth();
31249         
31250 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31251 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31252 //            return;
31253 //        }
31254         
31255         var boxWidth = this.boxWidth + this.padWidth;
31256         
31257         if(this.containerWidth < this.boxWidth){
31258             boxWidth = this.containerWidth
31259         }
31260         
31261         var containerWidth = this.containerWidth;
31262         
31263         var cols = Math.floor(containerWidth / boxWidth);
31264         
31265         this.cols = Math.max( cols, 1 );
31266         
31267         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31268         
31269         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31270         
31271         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31272         
31273         this.colWidth = boxWidth + avail - this.padWidth;
31274         
31275         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31276         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31277     },
31278     
31279     horizontalMeasureColumns : function()
31280     {
31281         this.getContainerWidth();
31282         
31283         var boxWidth = this.boxWidth;
31284         
31285         if(this.containerWidth < boxWidth){
31286             boxWidth = this.containerWidth;
31287         }
31288         
31289         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31290         
31291         this.el.setHeight(boxWidth);
31292         
31293     },
31294     
31295     getContainerWidth : function()
31296     {
31297         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31298     },
31299     
31300     layoutItems : function( isInstant )
31301     {
31302         Roo.log(this.bricks);
31303         
31304         var items = Roo.apply([], this.bricks);
31305         
31306         if(this.isHorizontal){
31307             this._horizontalLayoutItems( items , isInstant );
31308             return;
31309         }
31310         
31311 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31312 //            this._verticalAlternativeLayoutItems( items , isInstant );
31313 //            return;
31314 //        }
31315         
31316         this._verticalLayoutItems( items , isInstant );
31317         
31318     },
31319     
31320     _verticalLayoutItems : function ( items , isInstant)
31321     {
31322         if ( !items || !items.length ) {
31323             return;
31324         }
31325         
31326         var standard = [
31327             ['xs', 'xs', 'xs', 'tall'],
31328             ['xs', 'xs', 'tall'],
31329             ['xs', 'xs', 'sm'],
31330             ['xs', 'xs', 'xs'],
31331             ['xs', 'tall'],
31332             ['xs', 'sm'],
31333             ['xs', 'xs'],
31334             ['xs'],
31335             
31336             ['sm', 'xs', 'xs'],
31337             ['sm', 'xs'],
31338             ['sm'],
31339             
31340             ['tall', 'xs', 'xs', 'xs'],
31341             ['tall', 'xs', 'xs'],
31342             ['tall', 'xs'],
31343             ['tall']
31344             
31345         ];
31346         
31347         var queue = [];
31348         
31349         var boxes = [];
31350         
31351         var box = [];
31352         
31353         Roo.each(items, function(item, k){
31354             
31355             switch (item.size) {
31356                 // these layouts take up a full box,
31357                 case 'md' :
31358                 case 'md-left' :
31359                 case 'md-right' :
31360                 case 'wide' :
31361                     
31362                     if(box.length){
31363                         boxes.push(box);
31364                         box = [];
31365                     }
31366                     
31367                     boxes.push([item]);
31368                     
31369                     break;
31370                     
31371                 case 'xs' :
31372                 case 'sm' :
31373                 case 'tall' :
31374                     
31375                     box.push(item);
31376                     
31377                     break;
31378                 default :
31379                     break;
31380                     
31381             }
31382             
31383         }, this);
31384         
31385         if(box.length){
31386             boxes.push(box);
31387             box = [];
31388         }
31389         
31390         var filterPattern = function(box, length)
31391         {
31392             if(!box.length){
31393                 return;
31394             }
31395             
31396             var match = false;
31397             
31398             var pattern = box.slice(0, length);
31399             
31400             var format = [];
31401             
31402             Roo.each(pattern, function(i){
31403                 format.push(i.size);
31404             }, this);
31405             
31406             Roo.each(standard, function(s){
31407                 
31408                 if(String(s) != String(format)){
31409                     return;
31410                 }
31411                 
31412                 match = true;
31413                 return false;
31414                 
31415             }, this);
31416             
31417             if(!match && length == 1){
31418                 return;
31419             }
31420             
31421             if(!match){
31422                 filterPattern(box, length - 1);
31423                 return;
31424             }
31425                 
31426             queue.push(pattern);
31427
31428             box = box.slice(length, box.length);
31429
31430             filterPattern(box, 4);
31431
31432             return;
31433             
31434         }
31435         
31436         Roo.each(boxes, function(box, k){
31437             
31438             if(!box.length){
31439                 return;
31440             }
31441             
31442             if(box.length == 1){
31443                 queue.push(box);
31444                 return;
31445             }
31446             
31447             filterPattern(box, 4);
31448             
31449         }, this);
31450         
31451         this._processVerticalLayoutQueue( queue, isInstant );
31452         
31453     },
31454     
31455 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31456 //    {
31457 //        if ( !items || !items.length ) {
31458 //            return;
31459 //        }
31460 //
31461 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31462 //        
31463 //    },
31464     
31465     _horizontalLayoutItems : function ( items , isInstant)
31466     {
31467         if ( !items || !items.length || items.length < 3) {
31468             return;
31469         }
31470         
31471         items.reverse();
31472         
31473         var eItems = items.slice(0, 3);
31474         
31475         items = items.slice(3, items.length);
31476         
31477         var standard = [
31478             ['xs', 'xs', 'xs', 'wide'],
31479             ['xs', 'xs', 'wide'],
31480             ['xs', 'xs', 'sm'],
31481             ['xs', 'xs', 'xs'],
31482             ['xs', 'wide'],
31483             ['xs', 'sm'],
31484             ['xs', 'xs'],
31485             ['xs'],
31486             
31487             ['sm', 'xs', 'xs'],
31488             ['sm', 'xs'],
31489             ['sm'],
31490             
31491             ['wide', 'xs', 'xs', 'xs'],
31492             ['wide', 'xs', 'xs'],
31493             ['wide', 'xs'],
31494             ['wide'],
31495             
31496             ['wide-thin']
31497         ];
31498         
31499         var queue = [];
31500         
31501         var boxes = [];
31502         
31503         var box = [];
31504         
31505         Roo.each(items, function(item, k){
31506             
31507             switch (item.size) {
31508                 case 'md' :
31509                 case 'md-left' :
31510                 case 'md-right' :
31511                 case 'tall' :
31512                     
31513                     if(box.length){
31514                         boxes.push(box);
31515                         box = [];
31516                     }
31517                     
31518                     boxes.push([item]);
31519                     
31520                     break;
31521                     
31522                 case 'xs' :
31523                 case 'sm' :
31524                 case 'wide' :
31525                 case 'wide-thin' :
31526                     
31527                     box.push(item);
31528                     
31529                     break;
31530                 default :
31531                     break;
31532                     
31533             }
31534             
31535         }, this);
31536         
31537         if(box.length){
31538             boxes.push(box);
31539             box = [];
31540         }
31541         
31542         var filterPattern = function(box, length)
31543         {
31544             if(!box.length){
31545                 return;
31546             }
31547             
31548             var match = false;
31549             
31550             var pattern = box.slice(0, length);
31551             
31552             var format = [];
31553             
31554             Roo.each(pattern, function(i){
31555                 format.push(i.size);
31556             }, this);
31557             
31558             Roo.each(standard, function(s){
31559                 
31560                 if(String(s) != String(format)){
31561                     return;
31562                 }
31563                 
31564                 match = true;
31565                 return false;
31566                 
31567             }, this);
31568             
31569             if(!match && length == 1){
31570                 return;
31571             }
31572             
31573             if(!match){
31574                 filterPattern(box, length - 1);
31575                 return;
31576             }
31577                 
31578             queue.push(pattern);
31579
31580             box = box.slice(length, box.length);
31581
31582             filterPattern(box, 4);
31583
31584             return;
31585             
31586         }
31587         
31588         Roo.each(boxes, function(box, k){
31589             
31590             if(!box.length){
31591                 return;
31592             }
31593             
31594             if(box.length == 1){
31595                 queue.push(box);
31596                 return;
31597             }
31598             
31599             filterPattern(box, 4);
31600             
31601         }, this);
31602         
31603         
31604         var prune = [];
31605         
31606         var pos = this.el.getBox(true);
31607         
31608         var minX = pos.x;
31609         
31610         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31611         
31612         var hit_end = false;
31613         
31614         Roo.each(queue, function(box){
31615             
31616             if(hit_end){
31617                 
31618                 Roo.each(box, function(b){
31619                 
31620                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31621                     b.el.hide();
31622
31623                 }, this);
31624
31625                 return;
31626             }
31627             
31628             var mx = 0;
31629             
31630             Roo.each(box, function(b){
31631                 
31632                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31633                 b.el.show();
31634
31635                 mx = Math.max(mx, b.x);
31636                 
31637             }, this);
31638             
31639             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31640             
31641             if(maxX < minX){
31642                 
31643                 Roo.each(box, function(b){
31644                 
31645                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31646                     b.el.hide();
31647                     
31648                 }, this);
31649                 
31650                 hit_end = true;
31651                 
31652                 return;
31653             }
31654             
31655             prune.push(box);
31656             
31657         }, this);
31658         
31659         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31660     },
31661     
31662     /** Sets position of item in DOM
31663     * @param {Element} item
31664     * @param {Number} x - horizontal position
31665     * @param {Number} y - vertical position
31666     * @param {Boolean} isInstant - disables transitions
31667     */
31668     _processVerticalLayoutQueue : function( queue, isInstant )
31669     {
31670         var pos = this.el.getBox(true);
31671         var x = pos.x;
31672         var y = pos.y;
31673         var maxY = [];
31674         
31675         for (var i = 0; i < this.cols; i++){
31676             maxY[i] = pos.y;
31677         }
31678         
31679         Roo.each(queue, function(box, k){
31680             
31681             var col = k % this.cols;
31682             
31683             Roo.each(box, function(b,kk){
31684                 
31685                 b.el.position('absolute');
31686                 
31687                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31688                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31689                 
31690                 if(b.size == 'md-left' || b.size == 'md-right'){
31691                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31692                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31693                 }
31694                 
31695                 b.el.setWidth(width);
31696                 b.el.setHeight(height);
31697                 // iframe?
31698                 b.el.select('iframe',true).setSize(width,height);
31699                 
31700             }, this);
31701             
31702             for (var i = 0; i < this.cols; i++){
31703                 
31704                 if(maxY[i] < maxY[col]){
31705                     col = i;
31706                     continue;
31707                 }
31708                 
31709                 col = Math.min(col, i);
31710                 
31711             }
31712             
31713             x = pos.x + col * (this.colWidth + this.padWidth);
31714             
31715             y = maxY[col];
31716             
31717             var positions = [];
31718             
31719             switch (box.length){
31720                 case 1 :
31721                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31722                     break;
31723                 case 2 :
31724                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31725                     break;
31726                 case 3 :
31727                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31728                     break;
31729                 case 4 :
31730                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31731                     break;
31732                 default :
31733                     break;
31734             }
31735             
31736             Roo.each(box, function(b,kk){
31737                 
31738                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31739                 
31740                 var sz = b.el.getSize();
31741                 
31742                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31743                 
31744             }, this);
31745             
31746         }, this);
31747         
31748         var mY = 0;
31749         
31750         for (var i = 0; i < this.cols; i++){
31751             mY = Math.max(mY, maxY[i]);
31752         }
31753         
31754         this.el.setHeight(mY - pos.y);
31755         
31756     },
31757     
31758 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31759 //    {
31760 //        var pos = this.el.getBox(true);
31761 //        var x = pos.x;
31762 //        var y = pos.y;
31763 //        var maxX = pos.right;
31764 //        
31765 //        var maxHeight = 0;
31766 //        
31767 //        Roo.each(items, function(item, k){
31768 //            
31769 //            var c = k % 2;
31770 //            
31771 //            item.el.position('absolute');
31772 //                
31773 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31774 //
31775 //            item.el.setWidth(width);
31776 //
31777 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31778 //
31779 //            item.el.setHeight(height);
31780 //            
31781 //            if(c == 0){
31782 //                item.el.setXY([x, y], isInstant ? false : true);
31783 //            } else {
31784 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31785 //            }
31786 //            
31787 //            y = y + height + this.alternativePadWidth;
31788 //            
31789 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31790 //            
31791 //        }, this);
31792 //        
31793 //        this.el.setHeight(maxHeight);
31794 //        
31795 //    },
31796     
31797     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31798     {
31799         var pos = this.el.getBox(true);
31800         
31801         var minX = pos.x;
31802         var minY = pos.y;
31803         
31804         var maxX = pos.right;
31805         
31806         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31807         
31808         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31809         
31810         Roo.each(queue, function(box, k){
31811             
31812             Roo.each(box, function(b, kk){
31813                 
31814                 b.el.position('absolute');
31815                 
31816                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31817                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31818                 
31819                 if(b.size == 'md-left' || b.size == 'md-right'){
31820                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31821                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31822                 }
31823                 
31824                 b.el.setWidth(width);
31825                 b.el.setHeight(height);
31826                 
31827             }, this);
31828             
31829             if(!box.length){
31830                 return;
31831             }
31832             
31833             var positions = [];
31834             
31835             switch (box.length){
31836                 case 1 :
31837                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31838                     break;
31839                 case 2 :
31840                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31841                     break;
31842                 case 3 :
31843                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31844                     break;
31845                 case 4 :
31846                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31847                     break;
31848                 default :
31849                     break;
31850             }
31851             
31852             Roo.each(box, function(b,kk){
31853                 
31854                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31855                 
31856                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31857                 
31858             }, this);
31859             
31860         }, this);
31861         
31862     },
31863     
31864     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31865     {
31866         Roo.each(eItems, function(b,k){
31867             
31868             b.size = (k == 0) ? 'sm' : 'xs';
31869             b.x = (k == 0) ? 2 : 1;
31870             b.y = (k == 0) ? 2 : 1;
31871             
31872             b.el.position('absolute');
31873             
31874             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31875                 
31876             b.el.setWidth(width);
31877             
31878             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31879             
31880             b.el.setHeight(height);
31881             
31882         }, this);
31883
31884         var positions = [];
31885         
31886         positions.push({
31887             x : maxX - this.unitWidth * 2 - this.gutter,
31888             y : minY
31889         });
31890         
31891         positions.push({
31892             x : maxX - this.unitWidth,
31893             y : minY + (this.unitWidth + this.gutter) * 2
31894         });
31895         
31896         positions.push({
31897             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31898             y : minY
31899         });
31900         
31901         Roo.each(eItems, function(b,k){
31902             
31903             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31904
31905         }, this);
31906         
31907     },
31908     
31909     getVerticalOneBoxColPositions : function(x, y, box)
31910     {
31911         var pos = [];
31912         
31913         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31914         
31915         if(box[0].size == 'md-left'){
31916             rand = 0;
31917         }
31918         
31919         if(box[0].size == 'md-right'){
31920             rand = 1;
31921         }
31922         
31923         pos.push({
31924             x : x + (this.unitWidth + this.gutter) * rand,
31925             y : y
31926         });
31927         
31928         return pos;
31929     },
31930     
31931     getVerticalTwoBoxColPositions : function(x, y, box)
31932     {
31933         var pos = [];
31934         
31935         if(box[0].size == 'xs'){
31936             
31937             pos.push({
31938                 x : x,
31939                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31940             });
31941
31942             pos.push({
31943                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31944                 y : y
31945             });
31946             
31947             return pos;
31948             
31949         }
31950         
31951         pos.push({
31952             x : x,
31953             y : y
31954         });
31955
31956         pos.push({
31957             x : x + (this.unitWidth + this.gutter) * 2,
31958             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31959         });
31960         
31961         return pos;
31962         
31963     },
31964     
31965     getVerticalThreeBoxColPositions : function(x, y, box)
31966     {
31967         var pos = [];
31968         
31969         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31970             
31971             pos.push({
31972                 x : x,
31973                 y : y
31974             });
31975
31976             pos.push({
31977                 x : x + (this.unitWidth + this.gutter) * 1,
31978                 y : y
31979             });
31980             
31981             pos.push({
31982                 x : x + (this.unitWidth + this.gutter) * 2,
31983                 y : y
31984             });
31985             
31986             return pos;
31987             
31988         }
31989         
31990         if(box[0].size == 'xs' && box[1].size == 'xs'){
31991             
31992             pos.push({
31993                 x : x,
31994                 y : y
31995             });
31996
31997             pos.push({
31998                 x : x,
31999                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32000             });
32001             
32002             pos.push({
32003                 x : x + (this.unitWidth + this.gutter) * 1,
32004                 y : y
32005             });
32006             
32007             return pos;
32008             
32009         }
32010         
32011         pos.push({
32012             x : x,
32013             y : y
32014         });
32015
32016         pos.push({
32017             x : x + (this.unitWidth + this.gutter) * 2,
32018             y : y
32019         });
32020
32021         pos.push({
32022             x : x + (this.unitWidth + this.gutter) * 2,
32023             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32024         });
32025             
32026         return pos;
32027         
32028     },
32029     
32030     getVerticalFourBoxColPositions : function(x, y, box)
32031     {
32032         var pos = [];
32033         
32034         if(box[0].size == 'xs'){
32035             
32036             pos.push({
32037                 x : x,
32038                 y : y
32039             });
32040
32041             pos.push({
32042                 x : x,
32043                 y : y + (this.unitHeight + this.gutter) * 1
32044             });
32045             
32046             pos.push({
32047                 x : x,
32048                 y : y + (this.unitHeight + this.gutter) * 2
32049             });
32050             
32051             pos.push({
32052                 x : x + (this.unitWidth + this.gutter) * 1,
32053                 y : y
32054             });
32055             
32056             return pos;
32057             
32058         }
32059         
32060         pos.push({
32061             x : x,
32062             y : y
32063         });
32064
32065         pos.push({
32066             x : x + (this.unitWidth + this.gutter) * 2,
32067             y : y
32068         });
32069
32070         pos.push({
32071             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32072             y : y + (this.unitHeight + this.gutter) * 1
32073         });
32074
32075         pos.push({
32076             x : x + (this.unitWidth + this.gutter) * 2,
32077             y : y + (this.unitWidth + this.gutter) * 2
32078         });
32079
32080         return pos;
32081         
32082     },
32083     
32084     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32085     {
32086         var pos = [];
32087         
32088         if(box[0].size == 'md-left'){
32089             pos.push({
32090                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32091                 y : minY
32092             });
32093             
32094             return pos;
32095         }
32096         
32097         if(box[0].size == 'md-right'){
32098             pos.push({
32099                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32100                 y : minY + (this.unitWidth + this.gutter) * 1
32101             });
32102             
32103             return pos;
32104         }
32105         
32106         var rand = Math.floor(Math.random() * (4 - box[0].y));
32107         
32108         pos.push({
32109             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32110             y : minY + (this.unitWidth + this.gutter) * rand
32111         });
32112         
32113         return pos;
32114         
32115     },
32116     
32117     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32118     {
32119         var pos = [];
32120         
32121         if(box[0].size == 'xs'){
32122             
32123             pos.push({
32124                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32125                 y : minY
32126             });
32127
32128             pos.push({
32129                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32130                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32131             });
32132             
32133             return pos;
32134             
32135         }
32136         
32137         pos.push({
32138             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32139             y : minY
32140         });
32141
32142         pos.push({
32143             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32144             y : minY + (this.unitWidth + this.gutter) * 2
32145         });
32146         
32147         return pos;
32148         
32149     },
32150     
32151     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32152     {
32153         var pos = [];
32154         
32155         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32156             
32157             pos.push({
32158                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32159                 y : minY
32160             });
32161
32162             pos.push({
32163                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32164                 y : minY + (this.unitWidth + this.gutter) * 1
32165             });
32166             
32167             pos.push({
32168                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32169                 y : minY + (this.unitWidth + this.gutter) * 2
32170             });
32171             
32172             return pos;
32173             
32174         }
32175         
32176         if(box[0].size == 'xs' && box[1].size == 'xs'){
32177             
32178             pos.push({
32179                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32180                 y : minY
32181             });
32182
32183             pos.push({
32184                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32185                 y : minY
32186             });
32187             
32188             pos.push({
32189                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32190                 y : minY + (this.unitWidth + this.gutter) * 1
32191             });
32192             
32193             return pos;
32194             
32195         }
32196         
32197         pos.push({
32198             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32199             y : minY
32200         });
32201
32202         pos.push({
32203             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32204             y : minY + (this.unitWidth + this.gutter) * 2
32205         });
32206
32207         pos.push({
32208             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32209             y : minY + (this.unitWidth + this.gutter) * 2
32210         });
32211             
32212         return pos;
32213         
32214     },
32215     
32216     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32217     {
32218         var pos = [];
32219         
32220         if(box[0].size == 'xs'){
32221             
32222             pos.push({
32223                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32224                 y : minY
32225             });
32226
32227             pos.push({
32228                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32229                 y : minY
32230             });
32231             
32232             pos.push({
32233                 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),
32234                 y : minY
32235             });
32236             
32237             pos.push({
32238                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32239                 y : minY + (this.unitWidth + this.gutter) * 1
32240             });
32241             
32242             return pos;
32243             
32244         }
32245         
32246         pos.push({
32247             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32248             y : minY
32249         });
32250         
32251         pos.push({
32252             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32253             y : minY + (this.unitWidth + this.gutter) * 2
32254         });
32255         
32256         pos.push({
32257             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32258             y : minY + (this.unitWidth + this.gutter) * 2
32259         });
32260         
32261         pos.push({
32262             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),
32263             y : minY + (this.unitWidth + this.gutter) * 2
32264         });
32265
32266         return pos;
32267         
32268     },
32269     
32270     /**
32271     * remove a Masonry Brick
32272     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32273     */
32274     removeBrick : function(brick_id)
32275     {
32276         if (!brick_id) {
32277             return;
32278         }
32279         
32280         for (var i = 0; i<this.bricks.length; i++) {
32281             if (this.bricks[i].id == brick_id) {
32282                 this.bricks.splice(i,1);
32283                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32284                 this.initial();
32285             }
32286         }
32287     },
32288     
32289     /**
32290     * adds a Masonry Brick
32291     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32292     */
32293     addBrick : function(cfg)
32294     {
32295         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32296         //this.register(cn);
32297         cn.parentId = this.id;
32298         cn.render(this.el);
32299         return cn;
32300     },
32301     
32302     /**
32303     * register a Masonry Brick
32304     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32305     */
32306     
32307     register : function(brick)
32308     {
32309         this.bricks.push(brick);
32310         brick.masonryId = this.id;
32311     },
32312     
32313     /**
32314     * clear all the Masonry Brick
32315     */
32316     clearAll : function()
32317     {
32318         this.bricks = [];
32319         //this.getChildContainer().dom.innerHTML = "";
32320         this.el.dom.innerHTML = '';
32321     },
32322     
32323     getSelected : function()
32324     {
32325         if (!this.selectedBrick) {
32326             return false;
32327         }
32328         
32329         return this.selectedBrick;
32330     }
32331 });
32332
32333 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32334     
32335     groups: {},
32336      /**
32337     * register a Masonry Layout
32338     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32339     */
32340     
32341     register : function(layout)
32342     {
32343         this.groups[layout.id] = layout;
32344     },
32345     /**
32346     * fetch a  Masonry Layout based on the masonry layout ID
32347     * @param {string} the masonry layout to add
32348     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32349     */
32350     
32351     get: function(layout_id) {
32352         if (typeof(this.groups[layout_id]) == 'undefined') {
32353             return false;
32354         }
32355         return this.groups[layout_id] ;
32356     }
32357     
32358     
32359     
32360 });
32361
32362  
32363
32364  /**
32365  *
32366  * This is based on 
32367  * http://masonry.desandro.com
32368  *
32369  * The idea is to render all the bricks based on vertical width...
32370  *
32371  * The original code extends 'outlayer' - we might need to use that....
32372  * 
32373  */
32374
32375
32376 /**
32377  * @class Roo.bootstrap.LayoutMasonryAuto
32378  * @extends Roo.bootstrap.Component
32379  * Bootstrap Layout Masonry class
32380  * 
32381  * @constructor
32382  * Create a new Element
32383  * @param {Object} config The config object
32384  */
32385
32386 Roo.bootstrap.LayoutMasonryAuto = function(config){
32387     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32388 };
32389
32390 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32391     
32392       /**
32393      * @cfg {Boolean} isFitWidth  - resize the width..
32394      */   
32395     isFitWidth : false,  // options..
32396     /**
32397      * @cfg {Boolean} isOriginLeft = left align?
32398      */   
32399     isOriginLeft : true,
32400     /**
32401      * @cfg {Boolean} isOriginTop = top align?
32402      */   
32403     isOriginTop : false,
32404     /**
32405      * @cfg {Boolean} isLayoutInstant = no animation?
32406      */   
32407     isLayoutInstant : false, // needed?
32408     /**
32409      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32410      */   
32411     isResizingContainer : true,
32412     /**
32413      * @cfg {Number} columnWidth  width of the columns 
32414      */   
32415     
32416     columnWidth : 0,
32417     
32418     /**
32419      * @cfg {Number} maxCols maximum number of columns
32420      */   
32421     
32422     maxCols: 0,
32423     /**
32424      * @cfg {Number} padHeight padding below box..
32425      */   
32426     
32427     padHeight : 10, 
32428     
32429     /**
32430      * @cfg {Boolean} isAutoInitial defalut true
32431      */   
32432     
32433     isAutoInitial : true, 
32434     
32435     // private?
32436     gutter : 0,
32437     
32438     containerWidth: 0,
32439     initialColumnWidth : 0,
32440     currentSize : null,
32441     
32442     colYs : null, // array.
32443     maxY : 0,
32444     padWidth: 10,
32445     
32446     
32447     tag: 'div',
32448     cls: '',
32449     bricks: null, //CompositeElement
32450     cols : 0, // array?
32451     // element : null, // wrapped now this.el
32452     _isLayoutInited : null, 
32453     
32454     
32455     getAutoCreate : function(){
32456         
32457         var cfg = {
32458             tag: this.tag,
32459             cls: 'blog-masonary-wrapper ' + this.cls,
32460             cn : {
32461                 cls : 'mas-boxes masonary'
32462             }
32463         };
32464         
32465         return cfg;
32466     },
32467     
32468     getChildContainer: function( )
32469     {
32470         if (this.boxesEl) {
32471             return this.boxesEl;
32472         }
32473         
32474         this.boxesEl = this.el.select('.mas-boxes').first();
32475         
32476         return this.boxesEl;
32477     },
32478     
32479     
32480     initEvents : function()
32481     {
32482         var _this = this;
32483         
32484         if(this.isAutoInitial){
32485             Roo.log('hook children rendered');
32486             this.on('childrenrendered', function() {
32487                 Roo.log('children rendered');
32488                 _this.initial();
32489             } ,this);
32490         }
32491         
32492     },
32493     
32494     initial : function()
32495     {
32496         this.reloadItems();
32497
32498         this.currentSize = this.el.getBox(true);
32499
32500         /// was window resize... - let's see if this works..
32501         Roo.EventManager.onWindowResize(this.resize, this); 
32502
32503         if(!this.isAutoInitial){
32504             this.layout();
32505             return;
32506         }
32507         
32508         this.layout.defer(500,this);
32509     },
32510     
32511     reloadItems: function()
32512     {
32513         this.bricks = this.el.select('.masonry-brick', true);
32514         
32515         this.bricks.each(function(b) {
32516             //Roo.log(b.getSize());
32517             if (!b.attr('originalwidth')) {
32518                 b.attr('originalwidth',  b.getSize().width);
32519             }
32520             
32521         });
32522         
32523         Roo.log(this.bricks.elements.length);
32524     },
32525     
32526     resize : function()
32527     {
32528         Roo.log('resize');
32529         var cs = this.el.getBox(true);
32530         
32531         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32532             Roo.log("no change in with or X");
32533             return;
32534         }
32535         this.currentSize = cs;
32536         this.layout();
32537     },
32538     
32539     layout : function()
32540     {
32541          Roo.log('layout');
32542         this._resetLayout();
32543         //this._manageStamps();
32544       
32545         // don't animate first layout
32546         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32547         this.layoutItems( isInstant );
32548       
32549         // flag for initalized
32550         this._isLayoutInited = true;
32551     },
32552     
32553     layoutItems : function( isInstant )
32554     {
32555         //var items = this._getItemsForLayout( this.items );
32556         // original code supports filtering layout items.. we just ignore it..
32557         
32558         this._layoutItems( this.bricks , isInstant );
32559       
32560         this._postLayout();
32561     },
32562     _layoutItems : function ( items , isInstant)
32563     {
32564        //this.fireEvent( 'layout', this, items );
32565     
32566
32567         if ( !items || !items.elements.length ) {
32568           // no items, emit event with empty array
32569             return;
32570         }
32571
32572         var queue = [];
32573         items.each(function(item) {
32574             Roo.log("layout item");
32575             Roo.log(item);
32576             // get x/y object from method
32577             var position = this._getItemLayoutPosition( item );
32578             // enqueue
32579             position.item = item;
32580             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32581             queue.push( position );
32582         }, this);
32583       
32584         this._processLayoutQueue( queue );
32585     },
32586     /** Sets position of item in DOM
32587     * @param {Element} item
32588     * @param {Number} x - horizontal position
32589     * @param {Number} y - vertical position
32590     * @param {Boolean} isInstant - disables transitions
32591     */
32592     _processLayoutQueue : function( queue )
32593     {
32594         for ( var i=0, len = queue.length; i < len; i++ ) {
32595             var obj = queue[i];
32596             obj.item.position('absolute');
32597             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32598         }
32599     },
32600       
32601     
32602     /**
32603     * Any logic you want to do after each layout,
32604     * i.e. size the container
32605     */
32606     _postLayout : function()
32607     {
32608         this.resizeContainer();
32609     },
32610     
32611     resizeContainer : function()
32612     {
32613         if ( !this.isResizingContainer ) {
32614             return;
32615         }
32616         var size = this._getContainerSize();
32617         if ( size ) {
32618             this.el.setSize(size.width,size.height);
32619             this.boxesEl.setSize(size.width,size.height);
32620         }
32621     },
32622     
32623     
32624     
32625     _resetLayout : function()
32626     {
32627         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32628         this.colWidth = this.el.getWidth();
32629         //this.gutter = this.el.getWidth(); 
32630         
32631         this.measureColumns();
32632
32633         // reset column Y
32634         var i = this.cols;
32635         this.colYs = [];
32636         while (i--) {
32637             this.colYs.push( 0 );
32638         }
32639     
32640         this.maxY = 0;
32641     },
32642
32643     measureColumns : function()
32644     {
32645         this.getContainerWidth();
32646       // if columnWidth is 0, default to outerWidth of first item
32647         if ( !this.columnWidth ) {
32648             var firstItem = this.bricks.first();
32649             Roo.log(firstItem);
32650             this.columnWidth  = this.containerWidth;
32651             if (firstItem && firstItem.attr('originalwidth') ) {
32652                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32653             }
32654             // columnWidth fall back to item of first element
32655             Roo.log("set column width?");
32656                         this.initialColumnWidth = this.columnWidth  ;
32657
32658             // if first elem has no width, default to size of container
32659             
32660         }
32661         
32662         
32663         if (this.initialColumnWidth) {
32664             this.columnWidth = this.initialColumnWidth;
32665         }
32666         
32667         
32668             
32669         // column width is fixed at the top - however if container width get's smaller we should
32670         // reduce it...
32671         
32672         // this bit calcs how man columns..
32673             
32674         var columnWidth = this.columnWidth += this.gutter;
32675       
32676         // calculate columns
32677         var containerWidth = this.containerWidth + this.gutter;
32678         
32679         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32680         // fix rounding errors, typically with gutters
32681         var excess = columnWidth - containerWidth % columnWidth;
32682         
32683         
32684         // if overshoot is less than a pixel, round up, otherwise floor it
32685         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32686         cols = Math[ mathMethod ]( cols );
32687         this.cols = Math.max( cols, 1 );
32688         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32689         
32690          // padding positioning..
32691         var totalColWidth = this.cols * this.columnWidth;
32692         var padavail = this.containerWidth - totalColWidth;
32693         // so for 2 columns - we need 3 'pads'
32694         
32695         var padNeeded = (1+this.cols) * this.padWidth;
32696         
32697         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32698         
32699         this.columnWidth += padExtra
32700         //this.padWidth = Math.floor(padavail /  ( this.cols));
32701         
32702         // adjust colum width so that padding is fixed??
32703         
32704         // we have 3 columns ... total = width * 3
32705         // we have X left over... that should be used by 
32706         
32707         //if (this.expandC) {
32708             
32709         //}
32710         
32711         
32712         
32713     },
32714     
32715     getContainerWidth : function()
32716     {
32717        /* // container is parent if fit width
32718         var container = this.isFitWidth ? this.element.parentNode : this.element;
32719         // check that this.size and size are there
32720         // IE8 triggers resize on body size change, so they might not be
32721         
32722         var size = getSize( container );  //FIXME
32723         this.containerWidth = size && size.innerWidth; //FIXME
32724         */
32725          
32726         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32727         
32728     },
32729     
32730     _getItemLayoutPosition : function( item )  // what is item?
32731     {
32732         // we resize the item to our columnWidth..
32733       
32734         item.setWidth(this.columnWidth);
32735         item.autoBoxAdjust  = false;
32736         
32737         var sz = item.getSize();
32738  
32739         // how many columns does this brick span
32740         var remainder = this.containerWidth % this.columnWidth;
32741         
32742         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32743         // round if off by 1 pixel, otherwise use ceil
32744         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32745         colSpan = Math.min( colSpan, this.cols );
32746         
32747         // normally this should be '1' as we dont' currently allow multi width columns..
32748         
32749         var colGroup = this._getColGroup( colSpan );
32750         // get the minimum Y value from the columns
32751         var minimumY = Math.min.apply( Math, colGroup );
32752         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32753         
32754         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32755          
32756         // position the brick
32757         var position = {
32758             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32759             y: this.currentSize.y + minimumY + this.padHeight
32760         };
32761         
32762         Roo.log(position);
32763         // apply setHeight to necessary columns
32764         var setHeight = minimumY + sz.height + this.padHeight;
32765         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32766         
32767         var setSpan = this.cols + 1 - colGroup.length;
32768         for ( var i = 0; i < setSpan; i++ ) {
32769           this.colYs[ shortColIndex + i ] = setHeight ;
32770         }
32771       
32772         return position;
32773     },
32774     
32775     /**
32776      * @param {Number} colSpan - number of columns the element spans
32777      * @returns {Array} colGroup
32778      */
32779     _getColGroup : function( colSpan )
32780     {
32781         if ( colSpan < 2 ) {
32782           // if brick spans only one column, use all the column Ys
32783           return this.colYs;
32784         }
32785       
32786         var colGroup = [];
32787         // how many different places could this brick fit horizontally
32788         var groupCount = this.cols + 1 - colSpan;
32789         // for each group potential horizontal position
32790         for ( var i = 0; i < groupCount; i++ ) {
32791           // make an array of colY values for that one group
32792           var groupColYs = this.colYs.slice( i, i + colSpan );
32793           // and get the max value of the array
32794           colGroup[i] = Math.max.apply( Math, groupColYs );
32795         }
32796         return colGroup;
32797     },
32798     /*
32799     _manageStamp : function( stamp )
32800     {
32801         var stampSize =  stamp.getSize();
32802         var offset = stamp.getBox();
32803         // get the columns that this stamp affects
32804         var firstX = this.isOriginLeft ? offset.x : offset.right;
32805         var lastX = firstX + stampSize.width;
32806         var firstCol = Math.floor( firstX / this.columnWidth );
32807         firstCol = Math.max( 0, firstCol );
32808         
32809         var lastCol = Math.floor( lastX / this.columnWidth );
32810         // lastCol should not go over if multiple of columnWidth #425
32811         lastCol -= lastX % this.columnWidth ? 0 : 1;
32812         lastCol = Math.min( this.cols - 1, lastCol );
32813         
32814         // set colYs to bottom of the stamp
32815         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32816             stampSize.height;
32817             
32818         for ( var i = firstCol; i <= lastCol; i++ ) {
32819           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32820         }
32821     },
32822     */
32823     
32824     _getContainerSize : function()
32825     {
32826         this.maxY = Math.max.apply( Math, this.colYs );
32827         var size = {
32828             height: this.maxY
32829         };
32830       
32831         if ( this.isFitWidth ) {
32832             size.width = this._getContainerFitWidth();
32833         }
32834       
32835         return size;
32836     },
32837     
32838     _getContainerFitWidth : function()
32839     {
32840         var unusedCols = 0;
32841         // count unused columns
32842         var i = this.cols;
32843         while ( --i ) {
32844           if ( this.colYs[i] !== 0 ) {
32845             break;
32846           }
32847           unusedCols++;
32848         }
32849         // fit container to columns that have been used
32850         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32851     },
32852     
32853     needsResizeLayout : function()
32854     {
32855         var previousWidth = this.containerWidth;
32856         this.getContainerWidth();
32857         return previousWidth !== this.containerWidth;
32858     }
32859  
32860 });
32861
32862  
32863
32864  /*
32865  * - LGPL
32866  *
32867  * element
32868  * 
32869  */
32870
32871 /**
32872  * @class Roo.bootstrap.MasonryBrick
32873  * @extends Roo.bootstrap.Component
32874  * Bootstrap MasonryBrick class
32875  * 
32876  * @constructor
32877  * Create a new MasonryBrick
32878  * @param {Object} config The config object
32879  */
32880
32881 Roo.bootstrap.MasonryBrick = function(config){
32882     
32883     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32884     
32885     Roo.bootstrap.MasonryBrick.register(this);
32886     
32887     this.addEvents({
32888         // raw events
32889         /**
32890          * @event click
32891          * When a MasonryBrick is clcik
32892          * @param {Roo.bootstrap.MasonryBrick} this
32893          * @param {Roo.EventObject} e
32894          */
32895         "click" : true
32896     });
32897 };
32898
32899 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32900     
32901     /**
32902      * @cfg {String} title
32903      */   
32904     title : '',
32905     /**
32906      * @cfg {String} html
32907      */   
32908     html : '',
32909     /**
32910      * @cfg {String} bgimage
32911      */   
32912     bgimage : '',
32913     /**
32914      * @cfg {String} videourl
32915      */   
32916     videourl : '',
32917     /**
32918      * @cfg {String} cls
32919      */   
32920     cls : '',
32921     /**
32922      * @cfg {String} href
32923      */   
32924     href : '',
32925     /**
32926      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32927      */   
32928     size : 'xs',
32929     
32930     /**
32931      * @cfg {String} placetitle (center|bottom)
32932      */   
32933     placetitle : '',
32934     
32935     /**
32936      * @cfg {Boolean} isFitContainer defalut true
32937      */   
32938     isFitContainer : true, 
32939     
32940     /**
32941      * @cfg {Boolean} preventDefault defalut false
32942      */   
32943     preventDefault : false, 
32944     
32945     /**
32946      * @cfg {Boolean} inverse defalut false
32947      */   
32948     maskInverse : false, 
32949     
32950     getAutoCreate : function()
32951     {
32952         if(!this.isFitContainer){
32953             return this.getSplitAutoCreate();
32954         }
32955         
32956         var cls = 'masonry-brick masonry-brick-full';
32957         
32958         if(this.href.length){
32959             cls += ' masonry-brick-link';
32960         }
32961         
32962         if(this.bgimage.length){
32963             cls += ' masonry-brick-image';
32964         }
32965         
32966         if(this.maskInverse){
32967             cls += ' mask-inverse';
32968         }
32969         
32970         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32971             cls += ' enable-mask';
32972         }
32973         
32974         if(this.size){
32975             cls += ' masonry-' + this.size + '-brick';
32976         }
32977         
32978         if(this.placetitle.length){
32979             
32980             switch (this.placetitle) {
32981                 case 'center' :
32982                     cls += ' masonry-center-title';
32983                     break;
32984                 case 'bottom' :
32985                     cls += ' masonry-bottom-title';
32986                     break;
32987                 default:
32988                     break;
32989             }
32990             
32991         } else {
32992             if(!this.html.length && !this.bgimage.length){
32993                 cls += ' masonry-center-title';
32994             }
32995
32996             if(!this.html.length && this.bgimage.length){
32997                 cls += ' masonry-bottom-title';
32998             }
32999         }
33000         
33001         if(this.cls){
33002             cls += ' ' + this.cls;
33003         }
33004         
33005         var cfg = {
33006             tag: (this.href.length) ? 'a' : 'div',
33007             cls: cls,
33008             cn: [
33009                 {
33010                     tag: 'div',
33011                     cls: 'masonry-brick-mask'
33012                 },
33013                 {
33014                     tag: 'div',
33015                     cls: 'masonry-brick-paragraph',
33016                     cn: []
33017                 }
33018             ]
33019         };
33020         
33021         if(this.href.length){
33022             cfg.href = this.href;
33023         }
33024         
33025         var cn = cfg.cn[1].cn;
33026         
33027         if(this.title.length){
33028             cn.push({
33029                 tag: 'h4',
33030                 cls: 'masonry-brick-title',
33031                 html: this.title
33032             });
33033         }
33034         
33035         if(this.html.length){
33036             cn.push({
33037                 tag: 'p',
33038                 cls: 'masonry-brick-text',
33039                 html: this.html
33040             });
33041         }
33042         
33043         if (!this.title.length && !this.html.length) {
33044             cfg.cn[1].cls += ' hide';
33045         }
33046         
33047         if(this.bgimage.length){
33048             cfg.cn.push({
33049                 tag: 'img',
33050                 cls: 'masonry-brick-image-view',
33051                 src: this.bgimage
33052             });
33053         }
33054         
33055         if(this.videourl.length){
33056             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33057             // youtube support only?
33058             cfg.cn.push({
33059                 tag: 'iframe',
33060                 cls: 'masonry-brick-image-view',
33061                 src: vurl,
33062                 frameborder : 0,
33063                 allowfullscreen : true
33064             });
33065         }
33066         
33067         return cfg;
33068         
33069     },
33070     
33071     getSplitAutoCreate : function()
33072     {
33073         var cls = 'masonry-brick masonry-brick-split';
33074         
33075         if(this.href.length){
33076             cls += ' masonry-brick-link';
33077         }
33078         
33079         if(this.bgimage.length){
33080             cls += ' masonry-brick-image';
33081         }
33082         
33083         if(this.size){
33084             cls += ' masonry-' + this.size + '-brick';
33085         }
33086         
33087         switch (this.placetitle) {
33088             case 'center' :
33089                 cls += ' masonry-center-title';
33090                 break;
33091             case 'bottom' :
33092                 cls += ' masonry-bottom-title';
33093                 break;
33094             default:
33095                 if(!this.bgimage.length){
33096                     cls += ' masonry-center-title';
33097                 }
33098
33099                 if(this.bgimage.length){
33100                     cls += ' masonry-bottom-title';
33101                 }
33102                 break;
33103         }
33104         
33105         if(this.cls){
33106             cls += ' ' + this.cls;
33107         }
33108         
33109         var cfg = {
33110             tag: (this.href.length) ? 'a' : 'div',
33111             cls: cls,
33112             cn: [
33113                 {
33114                     tag: 'div',
33115                     cls: 'masonry-brick-split-head',
33116                     cn: [
33117                         {
33118                             tag: 'div',
33119                             cls: 'masonry-brick-paragraph',
33120                             cn: []
33121                         }
33122                     ]
33123                 },
33124                 {
33125                     tag: 'div',
33126                     cls: 'masonry-brick-split-body',
33127                     cn: []
33128                 }
33129             ]
33130         };
33131         
33132         if(this.href.length){
33133             cfg.href = this.href;
33134         }
33135         
33136         if(this.title.length){
33137             cfg.cn[0].cn[0].cn.push({
33138                 tag: 'h4',
33139                 cls: 'masonry-brick-title',
33140                 html: this.title
33141             });
33142         }
33143         
33144         if(this.html.length){
33145             cfg.cn[1].cn.push({
33146                 tag: 'p',
33147                 cls: 'masonry-brick-text',
33148                 html: this.html
33149             });
33150         }
33151
33152         if(this.bgimage.length){
33153             cfg.cn[0].cn.push({
33154                 tag: 'img',
33155                 cls: 'masonry-brick-image-view',
33156                 src: this.bgimage
33157             });
33158         }
33159         
33160         if(this.videourl.length){
33161             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33162             // youtube support only?
33163             cfg.cn[0].cn.cn.push({
33164                 tag: 'iframe',
33165                 cls: 'masonry-brick-image-view',
33166                 src: vurl,
33167                 frameborder : 0,
33168                 allowfullscreen : true
33169             });
33170         }
33171         
33172         return cfg;
33173     },
33174     
33175     initEvents: function() 
33176     {
33177         switch (this.size) {
33178             case 'xs' :
33179                 this.x = 1;
33180                 this.y = 1;
33181                 break;
33182             case 'sm' :
33183                 this.x = 2;
33184                 this.y = 2;
33185                 break;
33186             case 'md' :
33187             case 'md-left' :
33188             case 'md-right' :
33189                 this.x = 3;
33190                 this.y = 3;
33191                 break;
33192             case 'tall' :
33193                 this.x = 2;
33194                 this.y = 3;
33195                 break;
33196             case 'wide' :
33197                 this.x = 3;
33198                 this.y = 2;
33199                 break;
33200             case 'wide-thin' :
33201                 this.x = 3;
33202                 this.y = 1;
33203                 break;
33204                         
33205             default :
33206                 break;
33207         }
33208         
33209         if(Roo.isTouch){
33210             this.el.on('touchstart', this.onTouchStart, this);
33211             this.el.on('touchmove', this.onTouchMove, this);
33212             this.el.on('touchend', this.onTouchEnd, this);
33213             this.el.on('contextmenu', this.onContextMenu, this);
33214         } else {
33215             this.el.on('mouseenter'  ,this.enter, this);
33216             this.el.on('mouseleave', this.leave, this);
33217             this.el.on('click', this.onClick, this);
33218         }
33219         
33220         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33221             this.parent().bricks.push(this);   
33222         }
33223         
33224     },
33225     
33226     onClick: function(e, el)
33227     {
33228         var time = this.endTimer - this.startTimer;
33229         // Roo.log(e.preventDefault());
33230         if(Roo.isTouch){
33231             if(time > 1000){
33232                 e.preventDefault();
33233                 return;
33234             }
33235         }
33236         
33237         if(!this.preventDefault){
33238             return;
33239         }
33240         
33241         e.preventDefault();
33242         
33243         if (this.activeClass != '') {
33244             this.selectBrick();
33245         }
33246         
33247         this.fireEvent('click', this, e);
33248     },
33249     
33250     enter: function(e, el)
33251     {
33252         e.preventDefault();
33253         
33254         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33255             return;
33256         }
33257         
33258         if(this.bgimage.length && this.html.length){
33259             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33260         }
33261     },
33262     
33263     leave: function(e, el)
33264     {
33265         e.preventDefault();
33266         
33267         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33268             return;
33269         }
33270         
33271         if(this.bgimage.length && this.html.length){
33272             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33273         }
33274     },
33275     
33276     onTouchStart: function(e, el)
33277     {
33278 //        e.preventDefault();
33279         
33280         this.touchmoved = false;
33281         
33282         if(!this.isFitContainer){
33283             return;
33284         }
33285         
33286         if(!this.bgimage.length || !this.html.length){
33287             return;
33288         }
33289         
33290         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33291         
33292         this.timer = new Date().getTime();
33293         
33294     },
33295     
33296     onTouchMove: function(e, el)
33297     {
33298         this.touchmoved = true;
33299     },
33300     
33301     onContextMenu : function(e,el)
33302     {
33303         e.preventDefault();
33304         e.stopPropagation();
33305         return false;
33306     },
33307     
33308     onTouchEnd: function(e, el)
33309     {
33310 //        e.preventDefault();
33311         
33312         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33313         
33314             this.leave(e,el);
33315             
33316             return;
33317         }
33318         
33319         if(!this.bgimage.length || !this.html.length){
33320             
33321             if(this.href.length){
33322                 window.location.href = this.href;
33323             }
33324             
33325             return;
33326         }
33327         
33328         if(!this.isFitContainer){
33329             return;
33330         }
33331         
33332         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33333         
33334         window.location.href = this.href;
33335     },
33336     
33337     //selection on single brick only
33338     selectBrick : function() {
33339         
33340         if (!this.parentId) {
33341             return;
33342         }
33343         
33344         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33345         var index = m.selectedBrick.indexOf(this.id);
33346         
33347         if ( index > -1) {
33348             m.selectedBrick.splice(index,1);
33349             this.el.removeClass(this.activeClass);
33350             return;
33351         }
33352         
33353         for(var i = 0; i < m.selectedBrick.length; i++) {
33354             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33355             b.el.removeClass(b.activeClass);
33356         }
33357         
33358         m.selectedBrick = [];
33359         
33360         m.selectedBrick.push(this.id);
33361         this.el.addClass(this.activeClass);
33362         return;
33363     },
33364     
33365     isSelected : function(){
33366         return this.el.hasClass(this.activeClass);
33367         
33368     }
33369 });
33370
33371 Roo.apply(Roo.bootstrap.MasonryBrick, {
33372     
33373     //groups: {},
33374     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33375      /**
33376     * register a Masonry Brick
33377     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33378     */
33379     
33380     register : function(brick)
33381     {
33382         //this.groups[brick.id] = brick;
33383         this.groups.add(brick.id, brick);
33384     },
33385     /**
33386     * fetch a  masonry brick based on the masonry brick ID
33387     * @param {string} the masonry brick to add
33388     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33389     */
33390     
33391     get: function(brick_id) 
33392     {
33393         // if (typeof(this.groups[brick_id]) == 'undefined') {
33394         //     return false;
33395         // }
33396         // return this.groups[brick_id] ;
33397         
33398         if(this.groups.key(brick_id)) {
33399             return this.groups.key(brick_id);
33400         }
33401         
33402         return false;
33403     }
33404     
33405     
33406     
33407 });
33408
33409  /*
33410  * - LGPL
33411  *
33412  * element
33413  * 
33414  */
33415
33416 /**
33417  * @class Roo.bootstrap.Brick
33418  * @extends Roo.bootstrap.Component
33419  * Bootstrap Brick class
33420  * 
33421  * @constructor
33422  * Create a new Brick
33423  * @param {Object} config The config object
33424  */
33425
33426 Roo.bootstrap.Brick = function(config){
33427     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33428     
33429     this.addEvents({
33430         // raw events
33431         /**
33432          * @event click
33433          * When a Brick is click
33434          * @param {Roo.bootstrap.Brick} this
33435          * @param {Roo.EventObject} e
33436          */
33437         "click" : true
33438     });
33439 };
33440
33441 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33442     
33443     /**
33444      * @cfg {String} title
33445      */   
33446     title : '',
33447     /**
33448      * @cfg {String} html
33449      */   
33450     html : '',
33451     /**
33452      * @cfg {String} bgimage
33453      */   
33454     bgimage : '',
33455     /**
33456      * @cfg {String} cls
33457      */   
33458     cls : '',
33459     /**
33460      * @cfg {String} href
33461      */   
33462     href : '',
33463     /**
33464      * @cfg {String} video
33465      */   
33466     video : '',
33467     /**
33468      * @cfg {Boolean} square
33469      */   
33470     square : true,
33471     
33472     getAutoCreate : function()
33473     {
33474         var cls = 'roo-brick';
33475         
33476         if(this.href.length){
33477             cls += ' roo-brick-link';
33478         }
33479         
33480         if(this.bgimage.length){
33481             cls += ' roo-brick-image';
33482         }
33483         
33484         if(!this.html.length && !this.bgimage.length){
33485             cls += ' roo-brick-center-title';
33486         }
33487         
33488         if(!this.html.length && this.bgimage.length){
33489             cls += ' roo-brick-bottom-title';
33490         }
33491         
33492         if(this.cls){
33493             cls += ' ' + this.cls;
33494         }
33495         
33496         var cfg = {
33497             tag: (this.href.length) ? 'a' : 'div',
33498             cls: cls,
33499             cn: [
33500                 {
33501                     tag: 'div',
33502                     cls: 'roo-brick-paragraph',
33503                     cn: []
33504                 }
33505             ]
33506         };
33507         
33508         if(this.href.length){
33509             cfg.href = this.href;
33510         }
33511         
33512         var cn = cfg.cn[0].cn;
33513         
33514         if(this.title.length){
33515             cn.push({
33516                 tag: 'h4',
33517                 cls: 'roo-brick-title',
33518                 html: this.title
33519             });
33520         }
33521         
33522         if(this.html.length){
33523             cn.push({
33524                 tag: 'p',
33525                 cls: 'roo-brick-text',
33526                 html: this.html
33527             });
33528         } else {
33529             cn.cls += ' hide';
33530         }
33531         
33532         if(this.bgimage.length){
33533             cfg.cn.push({
33534                 tag: 'img',
33535                 cls: 'roo-brick-image-view',
33536                 src: this.bgimage
33537             });
33538         }
33539         
33540         return cfg;
33541     },
33542     
33543     initEvents: function() 
33544     {
33545         if(this.title.length || this.html.length){
33546             this.el.on('mouseenter'  ,this.enter, this);
33547             this.el.on('mouseleave', this.leave, this);
33548         }
33549         
33550         Roo.EventManager.onWindowResize(this.resize, this); 
33551         
33552         if(this.bgimage.length){
33553             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33554             this.imageEl.on('load', this.onImageLoad, this);
33555             return;
33556         }
33557         
33558         this.resize();
33559     },
33560     
33561     onImageLoad : function()
33562     {
33563         this.resize();
33564     },
33565     
33566     resize : function()
33567     {
33568         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33569         
33570         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33571         
33572         if(this.bgimage.length){
33573             var image = this.el.select('.roo-brick-image-view', true).first();
33574             
33575             image.setWidth(paragraph.getWidth());
33576             
33577             if(this.square){
33578                 image.setHeight(paragraph.getWidth());
33579             }
33580             
33581             this.el.setHeight(image.getHeight());
33582             paragraph.setHeight(image.getHeight());
33583             
33584         }
33585         
33586     },
33587     
33588     enter: function(e, el)
33589     {
33590         e.preventDefault();
33591         
33592         if(this.bgimage.length){
33593             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33594             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33595         }
33596     },
33597     
33598     leave: function(e, el)
33599     {
33600         e.preventDefault();
33601         
33602         if(this.bgimage.length){
33603             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33604             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33605         }
33606     }
33607     
33608 });
33609
33610  
33611
33612  /*
33613  * - LGPL
33614  *
33615  * Number field 
33616  */
33617
33618 /**
33619  * @class Roo.bootstrap.NumberField
33620  * @extends Roo.bootstrap.Input
33621  * Bootstrap NumberField class
33622  * 
33623  * 
33624  * 
33625  * 
33626  * @constructor
33627  * Create a new NumberField
33628  * @param {Object} config The config object
33629  */
33630
33631 Roo.bootstrap.NumberField = function(config){
33632     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33633 };
33634
33635 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33636     
33637     /**
33638      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33639      */
33640     allowDecimals : true,
33641     /**
33642      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33643      */
33644     decimalSeparator : ".",
33645     /**
33646      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33647      */
33648     decimalPrecision : 2,
33649     /**
33650      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33651      */
33652     allowNegative : true,
33653     
33654     /**
33655      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33656      */
33657     allowZero: true,
33658     /**
33659      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33660      */
33661     minValue : Number.NEGATIVE_INFINITY,
33662     /**
33663      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33664      */
33665     maxValue : Number.MAX_VALUE,
33666     /**
33667      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33668      */
33669     minText : "The minimum value for this field is {0}",
33670     /**
33671      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33672      */
33673     maxText : "The maximum value for this field is {0}",
33674     /**
33675      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33676      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33677      */
33678     nanText : "{0} is not a valid number",
33679     /**
33680      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33681      */
33682     thousandsDelimiter : false,
33683     /**
33684      * @cfg {String} valueAlign alignment of value
33685      */
33686     valueAlign : "left",
33687
33688     getAutoCreate : function()
33689     {
33690         var hiddenInput = {
33691             tag: 'input',
33692             type: 'hidden',
33693             id: Roo.id(),
33694             cls: 'hidden-number-input'
33695         };
33696         
33697         if (this.name) {
33698             hiddenInput.name = this.name;
33699         }
33700         
33701         this.name = '';
33702         
33703         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33704         
33705         this.name = hiddenInput.name;
33706         
33707         if(cfg.cn.length > 0) {
33708             cfg.cn.push(hiddenInput);
33709         }
33710         
33711         return cfg;
33712     },
33713
33714     // private
33715     initEvents : function()
33716     {   
33717         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33718         
33719         var allowed = "0123456789";
33720         
33721         if(this.allowDecimals){
33722             allowed += this.decimalSeparator;
33723         }
33724         
33725         if(this.allowNegative){
33726             allowed += "-";
33727         }
33728         
33729         if(this.thousandsDelimiter) {
33730             allowed += ",";
33731         }
33732         
33733         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33734         
33735         var keyPress = function(e){
33736             
33737             var k = e.getKey();
33738             
33739             var c = e.getCharCode();
33740             
33741             if(
33742                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33743                     allowed.indexOf(String.fromCharCode(c)) === -1
33744             ){
33745                 e.stopEvent();
33746                 return;
33747             }
33748             
33749             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33750                 return;
33751             }
33752             
33753             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33754                 e.stopEvent();
33755             }
33756         };
33757         
33758         this.el.on("keypress", keyPress, this);
33759     },
33760     
33761     validateValue : function(value)
33762     {
33763         
33764         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33765             return false;
33766         }
33767         
33768         var num = this.parseValue(value);
33769         
33770         if(isNaN(num)){
33771             this.markInvalid(String.format(this.nanText, value));
33772             return false;
33773         }
33774         
33775         if(num < this.minValue){
33776             this.markInvalid(String.format(this.minText, this.minValue));
33777             return false;
33778         }
33779         
33780         if(num > this.maxValue){
33781             this.markInvalid(String.format(this.maxText, this.maxValue));
33782             return false;
33783         }
33784         
33785         return true;
33786     },
33787
33788     getValue : function()
33789     {
33790         var v = this.hiddenEl().getValue();
33791         
33792         return this.fixPrecision(this.parseValue(v));
33793     },
33794
33795     parseValue : function(value)
33796     {
33797         if(this.thousandsDelimiter) {
33798             value += "";
33799             r = new RegExp(",", "g");
33800             value = value.replace(r, "");
33801         }
33802         
33803         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33804         return isNaN(value) ? '' : value;
33805     },
33806
33807     fixPrecision : function(value)
33808     {
33809         if(this.thousandsDelimiter) {
33810             value += "";
33811             r = new RegExp(",", "g");
33812             value = value.replace(r, "");
33813         }
33814         
33815         var nan = isNaN(value);
33816         
33817         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33818             return nan ? '' : value;
33819         }
33820         return parseFloat(value).toFixed(this.decimalPrecision);
33821     },
33822
33823     setValue : function(v)
33824     {
33825         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33826         
33827         this.value = v;
33828         
33829         if(this.rendered){
33830             
33831             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33832             
33833             this.inputEl().dom.value = (v == '') ? '' :
33834                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33835             
33836             if(!this.allowZero && v === '0') {
33837                 this.hiddenEl().dom.value = '';
33838                 this.inputEl().dom.value = '';
33839             }
33840             
33841             this.validate();
33842         }
33843     },
33844
33845     decimalPrecisionFcn : function(v)
33846     {
33847         return Math.floor(v);
33848     },
33849
33850     beforeBlur : function()
33851     {
33852         var v = this.parseValue(this.getRawValue());
33853         
33854         if(v || v === 0 || v === ''){
33855             this.setValue(v);
33856         }
33857     },
33858     
33859     hiddenEl : function()
33860     {
33861         return this.el.select('input.hidden-number-input',true).first();
33862     }
33863     
33864 });
33865
33866  
33867
33868 /*
33869 * Licence: LGPL
33870 */
33871
33872 /**
33873  * @class Roo.bootstrap.DocumentSlider
33874  * @extends Roo.bootstrap.Component
33875  * Bootstrap DocumentSlider class
33876  * 
33877  * @constructor
33878  * Create a new DocumentViewer
33879  * @param {Object} config The config object
33880  */
33881
33882 Roo.bootstrap.DocumentSlider = function(config){
33883     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33884     
33885     this.files = [];
33886     
33887     this.addEvents({
33888         /**
33889          * @event initial
33890          * Fire after initEvent
33891          * @param {Roo.bootstrap.DocumentSlider} this
33892          */
33893         "initial" : true,
33894         /**
33895          * @event update
33896          * Fire after update
33897          * @param {Roo.bootstrap.DocumentSlider} this
33898          */
33899         "update" : true,
33900         /**
33901          * @event click
33902          * Fire after click
33903          * @param {Roo.bootstrap.DocumentSlider} this
33904          */
33905         "click" : true
33906     });
33907 };
33908
33909 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33910     
33911     files : false,
33912     
33913     indicator : 0,
33914     
33915     getAutoCreate : function()
33916     {
33917         var cfg = {
33918             tag : 'div',
33919             cls : 'roo-document-slider',
33920             cn : [
33921                 {
33922                     tag : 'div',
33923                     cls : 'roo-document-slider-header',
33924                     cn : [
33925                         {
33926                             tag : 'div',
33927                             cls : 'roo-document-slider-header-title'
33928                         }
33929                     ]
33930                 },
33931                 {
33932                     tag : 'div',
33933                     cls : 'roo-document-slider-body',
33934                     cn : [
33935                         {
33936                             tag : 'div',
33937                             cls : 'roo-document-slider-prev',
33938                             cn : [
33939                                 {
33940                                     tag : 'i',
33941                                     cls : 'fa fa-chevron-left'
33942                                 }
33943                             ]
33944                         },
33945                         {
33946                             tag : 'div',
33947                             cls : 'roo-document-slider-thumb',
33948                             cn : [
33949                                 {
33950                                     tag : 'img',
33951                                     cls : 'roo-document-slider-image'
33952                                 }
33953                             ]
33954                         },
33955                         {
33956                             tag : 'div',
33957                             cls : 'roo-document-slider-next',
33958                             cn : [
33959                                 {
33960                                     tag : 'i',
33961                                     cls : 'fa fa-chevron-right'
33962                                 }
33963                             ]
33964                         }
33965                     ]
33966                 }
33967             ]
33968         };
33969         
33970         return cfg;
33971     },
33972     
33973     initEvents : function()
33974     {
33975         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33976         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33977         
33978         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33979         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33980         
33981         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33982         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33983         
33984         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33985         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33986         
33987         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33988         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33989         
33990         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33991         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33992         
33993         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33994         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33995         
33996         this.thumbEl.on('click', this.onClick, this);
33997         
33998         this.prevIndicator.on('click', this.prev, this);
33999         
34000         this.nextIndicator.on('click', this.next, this);
34001         
34002     },
34003     
34004     initial : function()
34005     {
34006         if(this.files.length){
34007             this.indicator = 1;
34008             this.update()
34009         }
34010         
34011         this.fireEvent('initial', this);
34012     },
34013     
34014     update : function()
34015     {
34016         this.imageEl.attr('src', this.files[this.indicator - 1]);
34017         
34018         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34019         
34020         this.prevIndicator.show();
34021         
34022         if(this.indicator == 1){
34023             this.prevIndicator.hide();
34024         }
34025         
34026         this.nextIndicator.show();
34027         
34028         if(this.indicator == this.files.length){
34029             this.nextIndicator.hide();
34030         }
34031         
34032         this.thumbEl.scrollTo('top');
34033         
34034         this.fireEvent('update', this);
34035     },
34036     
34037     onClick : function(e)
34038     {
34039         e.preventDefault();
34040         
34041         this.fireEvent('click', this);
34042     },
34043     
34044     prev : function(e)
34045     {
34046         e.preventDefault();
34047         
34048         this.indicator = Math.max(1, this.indicator - 1);
34049         
34050         this.update();
34051     },
34052     
34053     next : function(e)
34054     {
34055         e.preventDefault();
34056         
34057         this.indicator = Math.min(this.files.length, this.indicator + 1);
34058         
34059         this.update();
34060     }
34061 });
34062 /*
34063  * - LGPL
34064  *
34065  * RadioSet
34066  *
34067  *
34068  */
34069
34070 /**
34071  * @class Roo.bootstrap.RadioSet
34072  * @extends Roo.bootstrap.Input
34073  * Bootstrap RadioSet class
34074  * @cfg {String} indicatorpos (left|right) default left
34075  * @cfg {Boolean} inline (true|false) inline the element (default true)
34076  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34077  * @constructor
34078  * Create a new RadioSet
34079  * @param {Object} config The config object
34080  */
34081
34082 Roo.bootstrap.RadioSet = function(config){
34083     
34084     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34085     
34086     this.radioes = [];
34087     
34088     Roo.bootstrap.RadioSet.register(this);
34089     
34090     this.addEvents({
34091         /**
34092         * @event check
34093         * Fires when the element is checked or unchecked.
34094         * @param {Roo.bootstrap.RadioSet} this This radio
34095         * @param {Roo.bootstrap.Radio} item The checked item
34096         */
34097        check : true,
34098        /**
34099         * @event click
34100         * Fires when the element is click.
34101         * @param {Roo.bootstrap.RadioSet} this This radio set
34102         * @param {Roo.bootstrap.Radio} item The checked item
34103         * @param {Roo.EventObject} e The event object
34104         */
34105        click : true
34106     });
34107     
34108 };
34109
34110 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34111
34112     radioes : false,
34113     
34114     inline : true,
34115     
34116     weight : '',
34117     
34118     indicatorpos : 'left',
34119     
34120     getAutoCreate : function()
34121     {
34122         var label = {
34123             tag : 'label',
34124             cls : 'roo-radio-set-label',
34125             cn : [
34126                 {
34127                     tag : 'span',
34128                     html : this.fieldLabel
34129                 }
34130             ]
34131         };
34132         if (Roo.bootstrap.version == 3) {
34133             
34134             
34135             if(this.indicatorpos == 'left'){
34136                 label.cn.unshift({
34137                     tag : 'i',
34138                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34139                     tooltip : 'This field is required'
34140                 });
34141             } else {
34142                 label.cn.push({
34143                     tag : 'i',
34144                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34145                     tooltip : 'This field is required'
34146                 });
34147             }
34148         }
34149         var items = {
34150             tag : 'div',
34151             cls : 'roo-radio-set-items'
34152         };
34153         
34154         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34155         
34156         if (align === 'left' && this.fieldLabel.length) {
34157             
34158             items = {
34159                 cls : "roo-radio-set-right", 
34160                 cn: [
34161                     items
34162                 ]
34163             };
34164             
34165             if(this.labelWidth > 12){
34166                 label.style = "width: " + this.labelWidth + 'px';
34167             }
34168             
34169             if(this.labelWidth < 13 && this.labelmd == 0){
34170                 this.labelmd = this.labelWidth;
34171             }
34172             
34173             if(this.labellg > 0){
34174                 label.cls += ' col-lg-' + this.labellg;
34175                 items.cls += ' col-lg-' + (12 - this.labellg);
34176             }
34177             
34178             if(this.labelmd > 0){
34179                 label.cls += ' col-md-' + this.labelmd;
34180                 items.cls += ' col-md-' + (12 - this.labelmd);
34181             }
34182             
34183             if(this.labelsm > 0){
34184                 label.cls += ' col-sm-' + this.labelsm;
34185                 items.cls += ' col-sm-' + (12 - this.labelsm);
34186             }
34187             
34188             if(this.labelxs > 0){
34189                 label.cls += ' col-xs-' + this.labelxs;
34190                 items.cls += ' col-xs-' + (12 - this.labelxs);
34191             }
34192         }
34193         
34194         var cfg = {
34195             tag : 'div',
34196             cls : 'roo-radio-set',
34197             cn : [
34198                 {
34199                     tag : 'input',
34200                     cls : 'roo-radio-set-input',
34201                     type : 'hidden',
34202                     name : this.name,
34203                     value : this.value ? this.value :  ''
34204                 },
34205                 label,
34206                 items
34207             ]
34208         };
34209         
34210         if(this.weight.length){
34211             cfg.cls += ' roo-radio-' + this.weight;
34212         }
34213         
34214         if(this.inline) {
34215             cfg.cls += ' roo-radio-set-inline';
34216         }
34217         
34218         var settings=this;
34219         ['xs','sm','md','lg'].map(function(size){
34220             if (settings[size]) {
34221                 cfg.cls += ' col-' + size + '-' + settings[size];
34222             }
34223         });
34224         
34225         return cfg;
34226         
34227     },
34228
34229     initEvents : function()
34230     {
34231         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34232         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34233         
34234         if(!this.fieldLabel.length){
34235             this.labelEl.hide();
34236         }
34237         
34238         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34239         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34240         
34241         this.indicator = this.indicatorEl();
34242         
34243         if(this.indicator){
34244             this.indicator.addClass('invisible');
34245         }
34246         
34247         this.originalValue = this.getValue();
34248         
34249     },
34250     
34251     inputEl: function ()
34252     {
34253         return this.el.select('.roo-radio-set-input', true).first();
34254     },
34255     
34256     getChildContainer : function()
34257     {
34258         return this.itemsEl;
34259     },
34260     
34261     register : function(item)
34262     {
34263         this.radioes.push(item);
34264         
34265     },
34266     
34267     validate : function()
34268     {   
34269         if(this.getVisibilityEl().hasClass('hidden')){
34270             return true;
34271         }
34272         
34273         var valid = false;
34274         
34275         Roo.each(this.radioes, function(i){
34276             if(!i.checked){
34277                 return;
34278             }
34279             
34280             valid = true;
34281             return false;
34282         });
34283         
34284         if(this.allowBlank) {
34285             return true;
34286         }
34287         
34288         if(this.disabled || valid){
34289             this.markValid();
34290             return true;
34291         }
34292         
34293         this.markInvalid();
34294         return false;
34295         
34296     },
34297     
34298     markValid : function()
34299     {
34300         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34301             this.indicatorEl().removeClass('visible');
34302             this.indicatorEl().addClass('invisible');
34303         }
34304         
34305         
34306         if (Roo.bootstrap.version == 3) {
34307             this.el.removeClass([this.invalidClass, this.validClass]);
34308             this.el.addClass(this.validClass);
34309         } else {
34310             this.el.removeClass(['is-invalid','is-valid']);
34311             this.el.addClass(['is-valid']);
34312         }
34313         this.fireEvent('valid', this);
34314     },
34315     
34316     markInvalid : function(msg)
34317     {
34318         if(this.allowBlank || this.disabled){
34319             return;
34320         }
34321         
34322         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34323             this.indicatorEl().removeClass('invisible');
34324             this.indicatorEl().addClass('visible');
34325         }
34326         if (Roo.bootstrap.version == 3) {
34327             this.el.removeClass([this.invalidClass, this.validClass]);
34328             this.el.addClass(this.invalidClass);
34329         } else {
34330             this.el.removeClass(['is-invalid','is-valid']);
34331             this.el.addClass(['is-invalid']);
34332         }
34333         
34334         this.fireEvent('invalid', this, msg);
34335         
34336     },
34337     
34338     setValue : function(v, suppressEvent)
34339     {   
34340         if(this.value === v){
34341             return;
34342         }
34343         
34344         this.value = v;
34345         
34346         if(this.rendered){
34347             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34348         }
34349         
34350         Roo.each(this.radioes, function(i){
34351             i.checked = false;
34352             i.el.removeClass('checked');
34353         });
34354         
34355         Roo.each(this.radioes, function(i){
34356             
34357             if(i.value === v || i.value.toString() === v.toString()){
34358                 i.checked = true;
34359                 i.el.addClass('checked');
34360                 
34361                 if(suppressEvent !== true){
34362                     this.fireEvent('check', this, i);
34363                 }
34364                 
34365                 return false;
34366             }
34367             
34368         }, this);
34369         
34370         this.validate();
34371     },
34372     
34373     clearInvalid : function(){
34374         
34375         if(!this.el || this.preventMark){
34376             return;
34377         }
34378         
34379         this.el.removeClass([this.invalidClass]);
34380         
34381         this.fireEvent('valid', this);
34382     }
34383     
34384 });
34385
34386 Roo.apply(Roo.bootstrap.RadioSet, {
34387     
34388     groups: {},
34389     
34390     register : function(set)
34391     {
34392         this.groups[set.name] = set;
34393     },
34394     
34395     get: function(name) 
34396     {
34397         if (typeof(this.groups[name]) == 'undefined') {
34398             return false;
34399         }
34400         
34401         return this.groups[name] ;
34402     }
34403     
34404 });
34405 /*
34406  * Based on:
34407  * Ext JS Library 1.1.1
34408  * Copyright(c) 2006-2007, Ext JS, LLC.
34409  *
34410  * Originally Released Under LGPL - original licence link has changed is not relivant.
34411  *
34412  * Fork - LGPL
34413  * <script type="text/javascript">
34414  */
34415
34416
34417 /**
34418  * @class Roo.bootstrap.SplitBar
34419  * @extends Roo.util.Observable
34420  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34421  * <br><br>
34422  * Usage:
34423  * <pre><code>
34424 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34425                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34426 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34427 split.minSize = 100;
34428 split.maxSize = 600;
34429 split.animate = true;
34430 split.on('moved', splitterMoved);
34431 </code></pre>
34432  * @constructor
34433  * Create a new SplitBar
34434  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34435  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34436  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34437  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34438                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34439                         position of the SplitBar).
34440  */
34441 Roo.bootstrap.SplitBar = function(cfg){
34442     
34443     /** @private */
34444     
34445     //{
34446     //  dragElement : elm
34447     //  resizingElement: el,
34448         // optional..
34449     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34450     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34451         // existingProxy ???
34452     //}
34453     
34454     this.el = Roo.get(cfg.dragElement, true);
34455     this.el.dom.unselectable = "on";
34456     /** @private */
34457     this.resizingEl = Roo.get(cfg.resizingElement, true);
34458
34459     /**
34460      * @private
34461      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34462      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34463      * @type Number
34464      */
34465     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34466     
34467     /**
34468      * The minimum size of the resizing element. (Defaults to 0)
34469      * @type Number
34470      */
34471     this.minSize = 0;
34472     
34473     /**
34474      * The maximum size of the resizing element. (Defaults to 2000)
34475      * @type Number
34476      */
34477     this.maxSize = 2000;
34478     
34479     /**
34480      * Whether to animate the transition to the new size
34481      * @type Boolean
34482      */
34483     this.animate = false;
34484     
34485     /**
34486      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34487      * @type Boolean
34488      */
34489     this.useShim = false;
34490     
34491     /** @private */
34492     this.shim = null;
34493     
34494     if(!cfg.existingProxy){
34495         /** @private */
34496         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34497     }else{
34498         this.proxy = Roo.get(cfg.existingProxy).dom;
34499     }
34500     /** @private */
34501     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34502     
34503     /** @private */
34504     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34505     
34506     /** @private */
34507     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34508     
34509     /** @private */
34510     this.dragSpecs = {};
34511     
34512     /**
34513      * @private The adapter to use to positon and resize elements
34514      */
34515     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34516     this.adapter.init(this);
34517     
34518     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34519         /** @private */
34520         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34521         this.el.addClass("roo-splitbar-h");
34522     }else{
34523         /** @private */
34524         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34525         this.el.addClass("roo-splitbar-v");
34526     }
34527     
34528     this.addEvents({
34529         /**
34530          * @event resize
34531          * Fires when the splitter is moved (alias for {@link #event-moved})
34532          * @param {Roo.bootstrap.SplitBar} this
34533          * @param {Number} newSize the new width or height
34534          */
34535         "resize" : true,
34536         /**
34537          * @event moved
34538          * Fires when the splitter is moved
34539          * @param {Roo.bootstrap.SplitBar} this
34540          * @param {Number} newSize the new width or height
34541          */
34542         "moved" : true,
34543         /**
34544          * @event beforeresize
34545          * Fires before the splitter is dragged
34546          * @param {Roo.bootstrap.SplitBar} this
34547          */
34548         "beforeresize" : true,
34549
34550         "beforeapply" : true
34551     });
34552
34553     Roo.util.Observable.call(this);
34554 };
34555
34556 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34557     onStartProxyDrag : function(x, y){
34558         this.fireEvent("beforeresize", this);
34559         if(!this.overlay){
34560             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34561             o.unselectable();
34562             o.enableDisplayMode("block");
34563             // all splitbars share the same overlay
34564             Roo.bootstrap.SplitBar.prototype.overlay = o;
34565         }
34566         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34567         this.overlay.show();
34568         Roo.get(this.proxy).setDisplayed("block");
34569         var size = this.adapter.getElementSize(this);
34570         this.activeMinSize = this.getMinimumSize();;
34571         this.activeMaxSize = this.getMaximumSize();;
34572         var c1 = size - this.activeMinSize;
34573         var c2 = Math.max(this.activeMaxSize - size, 0);
34574         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34575             this.dd.resetConstraints();
34576             this.dd.setXConstraint(
34577                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34578                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34579             );
34580             this.dd.setYConstraint(0, 0);
34581         }else{
34582             this.dd.resetConstraints();
34583             this.dd.setXConstraint(0, 0);
34584             this.dd.setYConstraint(
34585                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34586                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34587             );
34588          }
34589         this.dragSpecs.startSize = size;
34590         this.dragSpecs.startPoint = [x, y];
34591         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34592     },
34593     
34594     /** 
34595      * @private Called after the drag operation by the DDProxy
34596      */
34597     onEndProxyDrag : function(e){
34598         Roo.get(this.proxy).setDisplayed(false);
34599         var endPoint = Roo.lib.Event.getXY(e);
34600         if(this.overlay){
34601             this.overlay.hide();
34602         }
34603         var newSize;
34604         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34605             newSize = this.dragSpecs.startSize + 
34606                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34607                     endPoint[0] - this.dragSpecs.startPoint[0] :
34608                     this.dragSpecs.startPoint[0] - endPoint[0]
34609                 );
34610         }else{
34611             newSize = this.dragSpecs.startSize + 
34612                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34613                     endPoint[1] - this.dragSpecs.startPoint[1] :
34614                     this.dragSpecs.startPoint[1] - endPoint[1]
34615                 );
34616         }
34617         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34618         if(newSize != this.dragSpecs.startSize){
34619             if(this.fireEvent('beforeapply', this, newSize) !== false){
34620                 this.adapter.setElementSize(this, newSize);
34621                 this.fireEvent("moved", this, newSize);
34622                 this.fireEvent("resize", this, newSize);
34623             }
34624         }
34625     },
34626     
34627     /**
34628      * Get the adapter this SplitBar uses
34629      * @return The adapter object
34630      */
34631     getAdapter : function(){
34632         return this.adapter;
34633     },
34634     
34635     /**
34636      * Set the adapter this SplitBar uses
34637      * @param {Object} adapter A SplitBar adapter object
34638      */
34639     setAdapter : function(adapter){
34640         this.adapter = adapter;
34641         this.adapter.init(this);
34642     },
34643     
34644     /**
34645      * Gets the minimum size for the resizing element
34646      * @return {Number} The minimum size
34647      */
34648     getMinimumSize : function(){
34649         return this.minSize;
34650     },
34651     
34652     /**
34653      * Sets the minimum size for the resizing element
34654      * @param {Number} minSize The minimum size
34655      */
34656     setMinimumSize : function(minSize){
34657         this.minSize = minSize;
34658     },
34659     
34660     /**
34661      * Gets the maximum size for the resizing element
34662      * @return {Number} The maximum size
34663      */
34664     getMaximumSize : function(){
34665         return this.maxSize;
34666     },
34667     
34668     /**
34669      * Sets the maximum size for the resizing element
34670      * @param {Number} maxSize The maximum size
34671      */
34672     setMaximumSize : function(maxSize){
34673         this.maxSize = maxSize;
34674     },
34675     
34676     /**
34677      * Sets the initialize size for the resizing element
34678      * @param {Number} size The initial size
34679      */
34680     setCurrentSize : function(size){
34681         var oldAnimate = this.animate;
34682         this.animate = false;
34683         this.adapter.setElementSize(this, size);
34684         this.animate = oldAnimate;
34685     },
34686     
34687     /**
34688      * Destroy this splitbar. 
34689      * @param {Boolean} removeEl True to remove the element
34690      */
34691     destroy : function(removeEl){
34692         if(this.shim){
34693             this.shim.remove();
34694         }
34695         this.dd.unreg();
34696         this.proxy.parentNode.removeChild(this.proxy);
34697         if(removeEl){
34698             this.el.remove();
34699         }
34700     }
34701 });
34702
34703 /**
34704  * @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.
34705  */
34706 Roo.bootstrap.SplitBar.createProxy = function(dir){
34707     var proxy = new Roo.Element(document.createElement("div"));
34708     proxy.unselectable();
34709     var cls = 'roo-splitbar-proxy';
34710     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34711     document.body.appendChild(proxy.dom);
34712     return proxy.dom;
34713 };
34714
34715 /** 
34716  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34717  * Default Adapter. It assumes the splitter and resizing element are not positioned
34718  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34719  */
34720 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34721 };
34722
34723 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34724     // do nothing for now
34725     init : function(s){
34726     
34727     },
34728     /**
34729      * Called before drag operations to get the current size of the resizing element. 
34730      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34731      */
34732      getElementSize : function(s){
34733         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34734             return s.resizingEl.getWidth();
34735         }else{
34736             return s.resizingEl.getHeight();
34737         }
34738     },
34739     
34740     /**
34741      * Called after drag operations to set the size of the resizing element.
34742      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34743      * @param {Number} newSize The new size to set
34744      * @param {Function} onComplete A function to be invoked when resizing is complete
34745      */
34746     setElementSize : function(s, newSize, onComplete){
34747         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34748             if(!s.animate){
34749                 s.resizingEl.setWidth(newSize);
34750                 if(onComplete){
34751                     onComplete(s, newSize);
34752                 }
34753             }else{
34754                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34755             }
34756         }else{
34757             
34758             if(!s.animate){
34759                 s.resizingEl.setHeight(newSize);
34760                 if(onComplete){
34761                     onComplete(s, newSize);
34762                 }
34763             }else{
34764                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34765             }
34766         }
34767     }
34768 };
34769
34770 /** 
34771  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34772  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34773  * Adapter that  moves the splitter element to align with the resized sizing element. 
34774  * Used with an absolute positioned SplitBar.
34775  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34776  * document.body, make sure you assign an id to the body element.
34777  */
34778 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34779     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34780     this.container = Roo.get(container);
34781 };
34782
34783 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34784     init : function(s){
34785         this.basic.init(s);
34786     },
34787     
34788     getElementSize : function(s){
34789         return this.basic.getElementSize(s);
34790     },
34791     
34792     setElementSize : function(s, newSize, onComplete){
34793         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34794     },
34795     
34796     moveSplitter : function(s){
34797         var yes = Roo.bootstrap.SplitBar;
34798         switch(s.placement){
34799             case yes.LEFT:
34800                 s.el.setX(s.resizingEl.getRight());
34801                 break;
34802             case yes.RIGHT:
34803                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34804                 break;
34805             case yes.TOP:
34806                 s.el.setY(s.resizingEl.getBottom());
34807                 break;
34808             case yes.BOTTOM:
34809                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34810                 break;
34811         }
34812     }
34813 };
34814
34815 /**
34816  * Orientation constant - Create a vertical SplitBar
34817  * @static
34818  * @type Number
34819  */
34820 Roo.bootstrap.SplitBar.VERTICAL = 1;
34821
34822 /**
34823  * Orientation constant - Create a horizontal SplitBar
34824  * @static
34825  * @type Number
34826  */
34827 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34828
34829 /**
34830  * Placement constant - The resizing element is to the left of the splitter element
34831  * @static
34832  * @type Number
34833  */
34834 Roo.bootstrap.SplitBar.LEFT = 1;
34835
34836 /**
34837  * Placement constant - The resizing element is to the right of the splitter element
34838  * @static
34839  * @type Number
34840  */
34841 Roo.bootstrap.SplitBar.RIGHT = 2;
34842
34843 /**
34844  * Placement constant - The resizing element is positioned above the splitter element
34845  * @static
34846  * @type Number
34847  */
34848 Roo.bootstrap.SplitBar.TOP = 3;
34849
34850 /**
34851  * Placement constant - The resizing element is positioned under splitter element
34852  * @static
34853  * @type Number
34854  */
34855 Roo.bootstrap.SplitBar.BOTTOM = 4;
34856 Roo.namespace("Roo.bootstrap.layout");/*
34857  * Based on:
34858  * Ext JS Library 1.1.1
34859  * Copyright(c) 2006-2007, Ext JS, LLC.
34860  *
34861  * Originally Released Under LGPL - original licence link has changed is not relivant.
34862  *
34863  * Fork - LGPL
34864  * <script type="text/javascript">
34865  */
34866
34867 /**
34868  * @class Roo.bootstrap.layout.Manager
34869  * @extends Roo.bootstrap.Component
34870  * Base class for layout managers.
34871  */
34872 Roo.bootstrap.layout.Manager = function(config)
34873 {
34874     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34875
34876
34877
34878
34879
34880     /** false to disable window resize monitoring @type Boolean */
34881     this.monitorWindowResize = true;
34882     this.regions = {};
34883     this.addEvents({
34884         /**
34885          * @event layout
34886          * Fires when a layout is performed.
34887          * @param {Roo.LayoutManager} this
34888          */
34889         "layout" : true,
34890         /**
34891          * @event regionresized
34892          * Fires when the user resizes a region.
34893          * @param {Roo.LayoutRegion} region The resized region
34894          * @param {Number} newSize The new size (width for east/west, height for north/south)
34895          */
34896         "regionresized" : true,
34897         /**
34898          * @event regioncollapsed
34899          * Fires when a region is collapsed.
34900          * @param {Roo.LayoutRegion} region The collapsed region
34901          */
34902         "regioncollapsed" : true,
34903         /**
34904          * @event regionexpanded
34905          * Fires when a region is expanded.
34906          * @param {Roo.LayoutRegion} region The expanded region
34907          */
34908         "regionexpanded" : true
34909     });
34910     this.updating = false;
34911
34912     if (config.el) {
34913         this.el = Roo.get(config.el);
34914         this.initEvents();
34915     }
34916
34917 };
34918
34919 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34920
34921
34922     regions : null,
34923
34924     monitorWindowResize : true,
34925
34926
34927     updating : false,
34928
34929
34930     onRender : function(ct, position)
34931     {
34932         if(!this.el){
34933             this.el = Roo.get(ct);
34934             this.initEvents();
34935         }
34936         //this.fireEvent('render',this);
34937     },
34938
34939
34940     initEvents: function()
34941     {
34942
34943
34944         // ie scrollbar fix
34945         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34946             document.body.scroll = "no";
34947         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34948             this.el.position('relative');
34949         }
34950         this.id = this.el.id;
34951         this.el.addClass("roo-layout-container");
34952         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34953         if(this.el.dom != document.body ) {
34954             this.el.on('resize', this.layout,this);
34955             this.el.on('show', this.layout,this);
34956         }
34957
34958     },
34959
34960     /**
34961      * Returns true if this layout is currently being updated
34962      * @return {Boolean}
34963      */
34964     isUpdating : function(){
34965         return this.updating;
34966     },
34967
34968     /**
34969      * Suspend the LayoutManager from doing auto-layouts while
34970      * making multiple add or remove calls
34971      */
34972     beginUpdate : function(){
34973         this.updating = true;
34974     },
34975
34976     /**
34977      * Restore auto-layouts and optionally disable the manager from performing a layout
34978      * @param {Boolean} noLayout true to disable a layout update
34979      */
34980     endUpdate : function(noLayout){
34981         this.updating = false;
34982         if(!noLayout){
34983             this.layout();
34984         }
34985     },
34986
34987     layout: function(){
34988         // abstract...
34989     },
34990
34991     onRegionResized : function(region, newSize){
34992         this.fireEvent("regionresized", region, newSize);
34993         this.layout();
34994     },
34995
34996     onRegionCollapsed : function(region){
34997         this.fireEvent("regioncollapsed", region);
34998     },
34999
35000     onRegionExpanded : function(region){
35001         this.fireEvent("regionexpanded", region);
35002     },
35003
35004     /**
35005      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35006      * performs box-model adjustments.
35007      * @return {Object} The size as an object {width: (the width), height: (the height)}
35008      */
35009     getViewSize : function()
35010     {
35011         var size;
35012         if(this.el.dom != document.body){
35013             size = this.el.getSize();
35014         }else{
35015             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35016         }
35017         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35018         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35019         return size;
35020     },
35021
35022     /**
35023      * Returns the Element this layout is bound to.
35024      * @return {Roo.Element}
35025      */
35026     getEl : function(){
35027         return this.el;
35028     },
35029
35030     /**
35031      * Returns the specified region.
35032      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35033      * @return {Roo.LayoutRegion}
35034      */
35035     getRegion : function(target){
35036         return this.regions[target.toLowerCase()];
35037     },
35038
35039     onWindowResize : function(){
35040         if(this.monitorWindowResize){
35041             this.layout();
35042         }
35043     }
35044 });
35045 /*
35046  * Based on:
35047  * Ext JS Library 1.1.1
35048  * Copyright(c) 2006-2007, Ext JS, LLC.
35049  *
35050  * Originally Released Under LGPL - original licence link has changed is not relivant.
35051  *
35052  * Fork - LGPL
35053  * <script type="text/javascript">
35054  */
35055 /**
35056  * @class Roo.bootstrap.layout.Border
35057  * @extends Roo.bootstrap.layout.Manager
35058  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35059  * please see: examples/bootstrap/nested.html<br><br>
35060  
35061 <b>The container the layout is rendered into can be either the body element or any other element.
35062 If it is not the body element, the container needs to either be an absolute positioned element,
35063 or you will need to add "position:relative" to the css of the container.  You will also need to specify
35064 the container size if it is not the body element.</b>
35065
35066 * @constructor
35067 * Create a new Border
35068 * @param {Object} config Configuration options
35069  */
35070 Roo.bootstrap.layout.Border = function(config){
35071     config = config || {};
35072     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35073     
35074     
35075     
35076     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35077         if(config[region]){
35078             config[region].region = region;
35079             this.addRegion(config[region]);
35080         }
35081     },this);
35082     
35083 };
35084
35085 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35086
35087 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35088     /**
35089      * Creates and adds a new region if it doesn't already exist.
35090      * @param {String} target The target region key (north, south, east, west or center).
35091      * @param {Object} config The regions config object
35092      * @return {BorderLayoutRegion} The new region
35093      */
35094     addRegion : function(config)
35095     {
35096         if(!this.regions[config.region]){
35097             var r = this.factory(config);
35098             this.bindRegion(r);
35099         }
35100         return this.regions[config.region];
35101     },
35102
35103     // private (kinda)
35104     bindRegion : function(r){
35105         this.regions[r.config.region] = r;
35106         
35107         r.on("visibilitychange",    this.layout, this);
35108         r.on("paneladded",          this.layout, this);
35109         r.on("panelremoved",        this.layout, this);
35110         r.on("invalidated",         this.layout, this);
35111         r.on("resized",             this.onRegionResized, this);
35112         r.on("collapsed",           this.onRegionCollapsed, this);
35113         r.on("expanded",            this.onRegionExpanded, this);
35114     },
35115
35116     /**
35117      * Performs a layout update.
35118      */
35119     layout : function()
35120     {
35121         if(this.updating) {
35122             return;
35123         }
35124         
35125         // render all the rebions if they have not been done alreayd?
35126         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35127             if(this.regions[region] && !this.regions[region].bodyEl){
35128                 this.regions[region].onRender(this.el)
35129             }
35130         },this);
35131         
35132         var size = this.getViewSize();
35133         var w = size.width;
35134         var h = size.height;
35135         var centerW = w;
35136         var centerH = h;
35137         var centerY = 0;
35138         var centerX = 0;
35139         //var x = 0, y = 0;
35140
35141         var rs = this.regions;
35142         var north = rs["north"];
35143         var south = rs["south"]; 
35144         var west = rs["west"];
35145         var east = rs["east"];
35146         var center = rs["center"];
35147         //if(this.hideOnLayout){ // not supported anymore
35148             //c.el.setStyle("display", "none");
35149         //}
35150         if(north && north.isVisible()){
35151             var b = north.getBox();
35152             var m = north.getMargins();
35153             b.width = w - (m.left+m.right);
35154             b.x = m.left;
35155             b.y = m.top;
35156             centerY = b.height + b.y + m.bottom;
35157             centerH -= centerY;
35158             north.updateBox(this.safeBox(b));
35159         }
35160         if(south && south.isVisible()){
35161             var b = south.getBox();
35162             var m = south.getMargins();
35163             b.width = w - (m.left+m.right);
35164             b.x = m.left;
35165             var totalHeight = (b.height + m.top + m.bottom);
35166             b.y = h - totalHeight + m.top;
35167             centerH -= totalHeight;
35168             south.updateBox(this.safeBox(b));
35169         }
35170         if(west && west.isVisible()){
35171             var b = west.getBox();
35172             var m = west.getMargins();
35173             b.height = centerH - (m.top+m.bottom);
35174             b.x = m.left;
35175             b.y = centerY + m.top;
35176             var totalWidth = (b.width + m.left + m.right);
35177             centerX += totalWidth;
35178             centerW -= totalWidth;
35179             west.updateBox(this.safeBox(b));
35180         }
35181         if(east && east.isVisible()){
35182             var b = east.getBox();
35183             var m = east.getMargins();
35184             b.height = centerH - (m.top+m.bottom);
35185             var totalWidth = (b.width + m.left + m.right);
35186             b.x = w - totalWidth + m.left;
35187             b.y = centerY + m.top;
35188             centerW -= totalWidth;
35189             east.updateBox(this.safeBox(b));
35190         }
35191         if(center){
35192             var m = center.getMargins();
35193             var centerBox = {
35194                 x: centerX + m.left,
35195                 y: centerY + m.top,
35196                 width: centerW - (m.left+m.right),
35197                 height: centerH - (m.top+m.bottom)
35198             };
35199             //if(this.hideOnLayout){
35200                 //center.el.setStyle("display", "block");
35201             //}
35202             center.updateBox(this.safeBox(centerBox));
35203         }
35204         this.el.repaint();
35205         this.fireEvent("layout", this);
35206     },
35207
35208     // private
35209     safeBox : function(box){
35210         box.width = Math.max(0, box.width);
35211         box.height = Math.max(0, box.height);
35212         return box;
35213     },
35214
35215     /**
35216      * Adds a ContentPanel (or subclass) to this layout.
35217      * @param {String} target The target region key (north, south, east, west or center).
35218      * @param {Roo.ContentPanel} panel The panel to add
35219      * @return {Roo.ContentPanel} The added panel
35220      */
35221     add : function(target, panel){
35222          
35223         target = target.toLowerCase();
35224         return this.regions[target].add(panel);
35225     },
35226
35227     /**
35228      * Remove a ContentPanel (or subclass) to this layout.
35229      * @param {String} target The target region key (north, south, east, west or center).
35230      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35231      * @return {Roo.ContentPanel} The removed panel
35232      */
35233     remove : function(target, panel){
35234         target = target.toLowerCase();
35235         return this.regions[target].remove(panel);
35236     },
35237
35238     /**
35239      * Searches all regions for a panel with the specified id
35240      * @param {String} panelId
35241      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35242      */
35243     findPanel : function(panelId){
35244         var rs = this.regions;
35245         for(var target in rs){
35246             if(typeof rs[target] != "function"){
35247                 var p = rs[target].getPanel(panelId);
35248                 if(p){
35249                     return p;
35250                 }
35251             }
35252         }
35253         return null;
35254     },
35255
35256     /**
35257      * Searches all regions for a panel with the specified id and activates (shows) it.
35258      * @param {String/ContentPanel} panelId The panels id or the panel itself
35259      * @return {Roo.ContentPanel} The shown panel or null
35260      */
35261     showPanel : function(panelId) {
35262       var rs = this.regions;
35263       for(var target in rs){
35264          var r = rs[target];
35265          if(typeof r != "function"){
35266             if(r.hasPanel(panelId)){
35267                return r.showPanel(panelId);
35268             }
35269          }
35270       }
35271       return null;
35272    },
35273
35274    /**
35275      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35276      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35277      */
35278    /*
35279     restoreState : function(provider){
35280         if(!provider){
35281             provider = Roo.state.Manager;
35282         }
35283         var sm = new Roo.LayoutStateManager();
35284         sm.init(this, provider);
35285     },
35286 */
35287  
35288  
35289     /**
35290      * Adds a xtype elements to the layout.
35291      * <pre><code>
35292
35293 layout.addxtype({
35294        xtype : 'ContentPanel',
35295        region: 'west',
35296        items: [ .... ]
35297    }
35298 );
35299
35300 layout.addxtype({
35301         xtype : 'NestedLayoutPanel',
35302         region: 'west',
35303         layout: {
35304            center: { },
35305            west: { }   
35306         },
35307         items : [ ... list of content panels or nested layout panels.. ]
35308    }
35309 );
35310 </code></pre>
35311      * @param {Object} cfg Xtype definition of item to add.
35312      */
35313     addxtype : function(cfg)
35314     {
35315         // basically accepts a pannel...
35316         // can accept a layout region..!?!?
35317         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35318         
35319         
35320         // theory?  children can only be panels??
35321         
35322         //if (!cfg.xtype.match(/Panel$/)) {
35323         //    return false;
35324         //}
35325         var ret = false;
35326         
35327         if (typeof(cfg.region) == 'undefined') {
35328             Roo.log("Failed to add Panel, region was not set");
35329             Roo.log(cfg);
35330             return false;
35331         }
35332         var region = cfg.region;
35333         delete cfg.region;
35334         
35335           
35336         var xitems = [];
35337         if (cfg.items) {
35338             xitems = cfg.items;
35339             delete cfg.items;
35340         }
35341         var nb = false;
35342         
35343         switch(cfg.xtype) 
35344         {
35345             case 'Content':  // ContentPanel (el, cfg)
35346             case 'Scroll':  // ContentPanel (el, cfg)
35347             case 'View': 
35348                 cfg.autoCreate = true;
35349                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35350                 //} else {
35351                 //    var el = this.el.createChild();
35352                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35353                 //}
35354                 
35355                 this.add(region, ret);
35356                 break;
35357             
35358             /*
35359             case 'TreePanel': // our new panel!
35360                 cfg.el = this.el.createChild();
35361                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35362                 this.add(region, ret);
35363                 break;
35364             */
35365             
35366             case 'Nest': 
35367                 // create a new Layout (which is  a Border Layout...
35368                 
35369                 var clayout = cfg.layout;
35370                 clayout.el  = this.el.createChild();
35371                 clayout.items   = clayout.items  || [];
35372                 
35373                 delete cfg.layout;
35374                 
35375                 // replace this exitems with the clayout ones..
35376                 xitems = clayout.items;
35377                  
35378                 // force background off if it's in center...
35379                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35380                     cfg.background = false;
35381                 }
35382                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35383                 
35384                 
35385                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35386                 //console.log('adding nested layout panel '  + cfg.toSource());
35387                 this.add(region, ret);
35388                 nb = {}; /// find first...
35389                 break;
35390             
35391             case 'Grid':
35392                 
35393                 // needs grid and region
35394                 
35395                 //var el = this.getRegion(region).el.createChild();
35396                 /*
35397                  *var el = this.el.createChild();
35398                 // create the grid first...
35399                 cfg.grid.container = el;
35400                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35401                 */
35402                 
35403                 if (region == 'center' && this.active ) {
35404                     cfg.background = false;
35405                 }
35406                 
35407                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35408                 
35409                 this.add(region, ret);
35410                 /*
35411                 if (cfg.background) {
35412                     // render grid on panel activation (if panel background)
35413                     ret.on('activate', function(gp) {
35414                         if (!gp.grid.rendered) {
35415                     //        gp.grid.render(el);
35416                         }
35417                     });
35418                 } else {
35419                   //  cfg.grid.render(el);
35420                 }
35421                 */
35422                 break;
35423            
35424            
35425             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35426                 // it was the old xcomponent building that caused this before.
35427                 // espeically if border is the top element in the tree.
35428                 ret = this;
35429                 break; 
35430                 
35431                     
35432                 
35433                 
35434                 
35435             default:
35436                 /*
35437                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35438                     
35439                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35440                     this.add(region, ret);
35441                 } else {
35442                 */
35443                     Roo.log(cfg);
35444                     throw "Can not add '" + cfg.xtype + "' to Border";
35445                     return null;
35446              
35447                                 
35448              
35449         }
35450         this.beginUpdate();
35451         // add children..
35452         var region = '';
35453         var abn = {};
35454         Roo.each(xitems, function(i)  {
35455             region = nb && i.region ? i.region : false;
35456             
35457             var add = ret.addxtype(i);
35458            
35459             if (region) {
35460                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35461                 if (!i.background) {
35462                     abn[region] = nb[region] ;
35463                 }
35464             }
35465             
35466         });
35467         this.endUpdate();
35468
35469         // make the last non-background panel active..
35470         //if (nb) { Roo.log(abn); }
35471         if (nb) {
35472             
35473             for(var r in abn) {
35474                 region = this.getRegion(r);
35475                 if (region) {
35476                     // tried using nb[r], but it does not work..
35477                      
35478                     region.showPanel(abn[r]);
35479                    
35480                 }
35481             }
35482         }
35483         return ret;
35484         
35485     },
35486     
35487     
35488 // private
35489     factory : function(cfg)
35490     {
35491         
35492         var validRegions = Roo.bootstrap.layout.Border.regions;
35493
35494         var target = cfg.region;
35495         cfg.mgr = this;
35496         
35497         var r = Roo.bootstrap.layout;
35498         Roo.log(target);
35499         switch(target){
35500             case "north":
35501                 return new r.North(cfg);
35502             case "south":
35503                 return new r.South(cfg);
35504             case "east":
35505                 return new r.East(cfg);
35506             case "west":
35507                 return new r.West(cfg);
35508             case "center":
35509                 return new r.Center(cfg);
35510         }
35511         throw 'Layout region "'+target+'" not supported.';
35512     }
35513     
35514     
35515 });
35516  /*
35517  * Based on:
35518  * Ext JS Library 1.1.1
35519  * Copyright(c) 2006-2007, Ext JS, LLC.
35520  *
35521  * Originally Released Under LGPL - original licence link has changed is not relivant.
35522  *
35523  * Fork - LGPL
35524  * <script type="text/javascript">
35525  */
35526  
35527 /**
35528  * @class Roo.bootstrap.layout.Basic
35529  * @extends Roo.util.Observable
35530  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35531  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35532  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35533  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35534  * @cfg {string}   region  the region that it inhabits..
35535  * @cfg {bool}   skipConfig skip config?
35536  * 
35537
35538  */
35539 Roo.bootstrap.layout.Basic = function(config){
35540     
35541     this.mgr = config.mgr;
35542     
35543     this.position = config.region;
35544     
35545     var skipConfig = config.skipConfig;
35546     
35547     this.events = {
35548         /**
35549          * @scope Roo.BasicLayoutRegion
35550          */
35551         
35552         /**
35553          * @event beforeremove
35554          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35555          * @param {Roo.LayoutRegion} this
35556          * @param {Roo.ContentPanel} panel The panel
35557          * @param {Object} e The cancel event object
35558          */
35559         "beforeremove" : true,
35560         /**
35561          * @event invalidated
35562          * Fires when the layout for this region is changed.
35563          * @param {Roo.LayoutRegion} this
35564          */
35565         "invalidated" : true,
35566         /**
35567          * @event visibilitychange
35568          * Fires when this region is shown or hidden 
35569          * @param {Roo.LayoutRegion} this
35570          * @param {Boolean} visibility true or false
35571          */
35572         "visibilitychange" : true,
35573         /**
35574          * @event paneladded
35575          * Fires when a panel is added. 
35576          * @param {Roo.LayoutRegion} this
35577          * @param {Roo.ContentPanel} panel The panel
35578          */
35579         "paneladded" : true,
35580         /**
35581          * @event panelremoved
35582          * Fires when a panel is removed. 
35583          * @param {Roo.LayoutRegion} this
35584          * @param {Roo.ContentPanel} panel The panel
35585          */
35586         "panelremoved" : true,
35587         /**
35588          * @event beforecollapse
35589          * Fires when this region before collapse.
35590          * @param {Roo.LayoutRegion} this
35591          */
35592         "beforecollapse" : true,
35593         /**
35594          * @event collapsed
35595          * Fires when this region is collapsed.
35596          * @param {Roo.LayoutRegion} this
35597          */
35598         "collapsed" : true,
35599         /**
35600          * @event expanded
35601          * Fires when this region is expanded.
35602          * @param {Roo.LayoutRegion} this
35603          */
35604         "expanded" : true,
35605         /**
35606          * @event slideshow
35607          * Fires when this region is slid into view.
35608          * @param {Roo.LayoutRegion} this
35609          */
35610         "slideshow" : true,
35611         /**
35612          * @event slidehide
35613          * Fires when this region slides out of view. 
35614          * @param {Roo.LayoutRegion} this
35615          */
35616         "slidehide" : true,
35617         /**
35618          * @event panelactivated
35619          * Fires when a panel is activated. 
35620          * @param {Roo.LayoutRegion} this
35621          * @param {Roo.ContentPanel} panel The activated panel
35622          */
35623         "panelactivated" : true,
35624         /**
35625          * @event resized
35626          * Fires when the user resizes this region. 
35627          * @param {Roo.LayoutRegion} this
35628          * @param {Number} newSize The new size (width for east/west, height for north/south)
35629          */
35630         "resized" : true
35631     };
35632     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35633     this.panels = new Roo.util.MixedCollection();
35634     this.panels.getKey = this.getPanelId.createDelegate(this);
35635     this.box = null;
35636     this.activePanel = null;
35637     // ensure listeners are added...
35638     
35639     if (config.listeners || config.events) {
35640         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35641             listeners : config.listeners || {},
35642             events : config.events || {}
35643         });
35644     }
35645     
35646     if(skipConfig !== true){
35647         this.applyConfig(config);
35648     }
35649 };
35650
35651 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35652 {
35653     getPanelId : function(p){
35654         return p.getId();
35655     },
35656     
35657     applyConfig : function(config){
35658         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35659         this.config = config;
35660         
35661     },
35662     
35663     /**
35664      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35665      * the width, for horizontal (north, south) the height.
35666      * @param {Number} newSize The new width or height
35667      */
35668     resizeTo : function(newSize){
35669         var el = this.el ? this.el :
35670                  (this.activePanel ? this.activePanel.getEl() : null);
35671         if(el){
35672             switch(this.position){
35673                 case "east":
35674                 case "west":
35675                     el.setWidth(newSize);
35676                     this.fireEvent("resized", this, newSize);
35677                 break;
35678                 case "north":
35679                 case "south":
35680                     el.setHeight(newSize);
35681                     this.fireEvent("resized", this, newSize);
35682                 break;                
35683             }
35684         }
35685     },
35686     
35687     getBox : function(){
35688         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35689     },
35690     
35691     getMargins : function(){
35692         return this.margins;
35693     },
35694     
35695     updateBox : function(box){
35696         this.box = box;
35697         var el = this.activePanel.getEl();
35698         el.dom.style.left = box.x + "px";
35699         el.dom.style.top = box.y + "px";
35700         this.activePanel.setSize(box.width, box.height);
35701     },
35702     
35703     /**
35704      * Returns the container element for this region.
35705      * @return {Roo.Element}
35706      */
35707     getEl : function(){
35708         return this.activePanel;
35709     },
35710     
35711     /**
35712      * Returns true if this region is currently visible.
35713      * @return {Boolean}
35714      */
35715     isVisible : function(){
35716         return this.activePanel ? true : false;
35717     },
35718     
35719     setActivePanel : function(panel){
35720         panel = this.getPanel(panel);
35721         if(this.activePanel && this.activePanel != panel){
35722             this.activePanel.setActiveState(false);
35723             this.activePanel.getEl().setLeftTop(-10000,-10000);
35724         }
35725         this.activePanel = panel;
35726         panel.setActiveState(true);
35727         if(this.box){
35728             panel.setSize(this.box.width, this.box.height);
35729         }
35730         this.fireEvent("panelactivated", this, panel);
35731         this.fireEvent("invalidated");
35732     },
35733     
35734     /**
35735      * Show the specified panel.
35736      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35737      * @return {Roo.ContentPanel} The shown panel or null
35738      */
35739     showPanel : function(panel){
35740         panel = this.getPanel(panel);
35741         if(panel){
35742             this.setActivePanel(panel);
35743         }
35744         return panel;
35745     },
35746     
35747     /**
35748      * Get the active panel for this region.
35749      * @return {Roo.ContentPanel} The active panel or null
35750      */
35751     getActivePanel : function(){
35752         return this.activePanel;
35753     },
35754     
35755     /**
35756      * Add the passed ContentPanel(s)
35757      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35758      * @return {Roo.ContentPanel} The panel added (if only one was added)
35759      */
35760     add : function(panel){
35761         if(arguments.length > 1){
35762             for(var i = 0, len = arguments.length; i < len; i++) {
35763                 this.add(arguments[i]);
35764             }
35765             return null;
35766         }
35767         if(this.hasPanel(panel)){
35768             this.showPanel(panel);
35769             return panel;
35770         }
35771         var el = panel.getEl();
35772         if(el.dom.parentNode != this.mgr.el.dom){
35773             this.mgr.el.dom.appendChild(el.dom);
35774         }
35775         if(panel.setRegion){
35776             panel.setRegion(this);
35777         }
35778         this.panels.add(panel);
35779         el.setStyle("position", "absolute");
35780         if(!panel.background){
35781             this.setActivePanel(panel);
35782             if(this.config.initialSize && this.panels.getCount()==1){
35783                 this.resizeTo(this.config.initialSize);
35784             }
35785         }
35786         this.fireEvent("paneladded", this, panel);
35787         return panel;
35788     },
35789     
35790     /**
35791      * Returns true if the panel is in this region.
35792      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35793      * @return {Boolean}
35794      */
35795     hasPanel : function(panel){
35796         if(typeof panel == "object"){ // must be panel obj
35797             panel = panel.getId();
35798         }
35799         return this.getPanel(panel) ? true : false;
35800     },
35801     
35802     /**
35803      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35804      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35805      * @param {Boolean} preservePanel Overrides the config preservePanel option
35806      * @return {Roo.ContentPanel} The panel that was removed
35807      */
35808     remove : function(panel, preservePanel){
35809         panel = this.getPanel(panel);
35810         if(!panel){
35811             return null;
35812         }
35813         var e = {};
35814         this.fireEvent("beforeremove", this, panel, e);
35815         if(e.cancel === true){
35816             return null;
35817         }
35818         var panelId = panel.getId();
35819         this.panels.removeKey(panelId);
35820         return panel;
35821     },
35822     
35823     /**
35824      * Returns the panel specified or null if it's not in this region.
35825      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35826      * @return {Roo.ContentPanel}
35827      */
35828     getPanel : function(id){
35829         if(typeof id == "object"){ // must be panel obj
35830             return id;
35831         }
35832         return this.panels.get(id);
35833     },
35834     
35835     /**
35836      * Returns this regions position (north/south/east/west/center).
35837      * @return {String} 
35838      */
35839     getPosition: function(){
35840         return this.position;    
35841     }
35842 });/*
35843  * Based on:
35844  * Ext JS Library 1.1.1
35845  * Copyright(c) 2006-2007, Ext JS, LLC.
35846  *
35847  * Originally Released Under LGPL - original licence link has changed is not relivant.
35848  *
35849  * Fork - LGPL
35850  * <script type="text/javascript">
35851  */
35852  
35853 /**
35854  * @class Roo.bootstrap.layout.Region
35855  * @extends Roo.bootstrap.layout.Basic
35856  * This class represents a region in a layout manager.
35857  
35858  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35859  * @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})
35860  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35861  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35862  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35863  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35864  * @cfg {String}    title           The title for the region (overrides panel titles)
35865  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35866  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35867  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35868  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35869  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35870  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35871  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35872  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35873  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35874  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35875
35876  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35877  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35878  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35879  * @cfg {Number}    width           For East/West panels
35880  * @cfg {Number}    height          For North/South panels
35881  * @cfg {Boolean}   split           To show the splitter
35882  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35883  * 
35884  * @cfg {string}   cls             Extra CSS classes to add to region
35885  * 
35886  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35887  * @cfg {string}   region  the region that it inhabits..
35888  *
35889
35890  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35891  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35892
35893  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35894  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35895  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35896  */
35897 Roo.bootstrap.layout.Region = function(config)
35898 {
35899     this.applyConfig(config);
35900
35901     var mgr = config.mgr;
35902     var pos = config.region;
35903     config.skipConfig = true;
35904     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35905     
35906     if (mgr.el) {
35907         this.onRender(mgr.el);   
35908     }
35909      
35910     this.visible = true;
35911     this.collapsed = false;
35912     this.unrendered_panels = [];
35913 };
35914
35915 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35916
35917     position: '', // set by wrapper (eg. north/south etc..)
35918     unrendered_panels : null,  // unrendered panels.
35919     createBody : function(){
35920         /** This region's body element 
35921         * @type Roo.Element */
35922         this.bodyEl = this.el.createChild({
35923                 tag: "div",
35924                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35925         });
35926     },
35927
35928     onRender: function(ctr, pos)
35929     {
35930         var dh = Roo.DomHelper;
35931         /** This region's container element 
35932         * @type Roo.Element */
35933         this.el = dh.append(ctr.dom, {
35934                 tag: "div",
35935                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35936             }, true);
35937         /** This region's title element 
35938         * @type Roo.Element */
35939     
35940         this.titleEl = dh.append(this.el.dom,
35941             {
35942                     tag: "div",
35943                     unselectable: "on",
35944                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35945                     children:[
35946                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35947                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35948                     ]}, true);
35949         
35950         this.titleEl.enableDisplayMode();
35951         /** This region's title text element 
35952         * @type HTMLElement */
35953         this.titleTextEl = this.titleEl.dom.firstChild;
35954         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35955         /*
35956         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35957         this.closeBtn.enableDisplayMode();
35958         this.closeBtn.on("click", this.closeClicked, this);
35959         this.closeBtn.hide();
35960     */
35961         this.createBody(this.config);
35962         if(this.config.hideWhenEmpty){
35963             this.hide();
35964             this.on("paneladded", this.validateVisibility, this);
35965             this.on("panelremoved", this.validateVisibility, this);
35966         }
35967         if(this.autoScroll){
35968             this.bodyEl.setStyle("overflow", "auto");
35969         }else{
35970             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35971         }
35972         //if(c.titlebar !== false){
35973             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35974                 this.titleEl.hide();
35975             }else{
35976                 this.titleEl.show();
35977                 if(this.config.title){
35978                     this.titleTextEl.innerHTML = this.config.title;
35979                 }
35980             }
35981         //}
35982         if(this.config.collapsed){
35983             this.collapse(true);
35984         }
35985         if(this.config.hidden){
35986             this.hide();
35987         }
35988         
35989         if (this.unrendered_panels && this.unrendered_panels.length) {
35990             for (var i =0;i< this.unrendered_panels.length; i++) {
35991                 this.add(this.unrendered_panels[i]);
35992             }
35993             this.unrendered_panels = null;
35994             
35995         }
35996         
35997     },
35998     
35999     applyConfig : function(c)
36000     {
36001         /*
36002          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36003             var dh = Roo.DomHelper;
36004             if(c.titlebar !== false){
36005                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36006                 this.collapseBtn.on("click", this.collapse, this);
36007                 this.collapseBtn.enableDisplayMode();
36008                 /*
36009                 if(c.showPin === true || this.showPin){
36010                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36011                     this.stickBtn.enableDisplayMode();
36012                     this.stickBtn.on("click", this.expand, this);
36013                     this.stickBtn.hide();
36014                 }
36015                 
36016             }
36017             */
36018             /** This region's collapsed element
36019             * @type Roo.Element */
36020             /*
36021              *
36022             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36023                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36024             ]}, true);
36025             
36026             if(c.floatable !== false){
36027                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36028                this.collapsedEl.on("click", this.collapseClick, this);
36029             }
36030
36031             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36032                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36033                    id: "message", unselectable: "on", style:{"float":"left"}});
36034                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36035              }
36036             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36037             this.expandBtn.on("click", this.expand, this);
36038             
36039         }
36040         
36041         if(this.collapseBtn){
36042             this.collapseBtn.setVisible(c.collapsible == true);
36043         }
36044         
36045         this.cmargins = c.cmargins || this.cmargins ||
36046                          (this.position == "west" || this.position == "east" ?
36047                              {top: 0, left: 2, right:2, bottom: 0} :
36048                              {top: 2, left: 0, right:0, bottom: 2});
36049         */
36050         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36051         
36052         
36053         this.bottomTabs = c.tabPosition != "top";
36054         
36055         this.autoScroll = c.autoScroll || false;
36056         
36057         
36058        
36059         
36060         this.duration = c.duration || .30;
36061         this.slideDuration = c.slideDuration || .45;
36062         this.config = c;
36063        
36064     },
36065     /**
36066      * Returns true if this region is currently visible.
36067      * @return {Boolean}
36068      */
36069     isVisible : function(){
36070         return this.visible;
36071     },
36072
36073     /**
36074      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36075      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36076      */
36077     //setCollapsedTitle : function(title){
36078     //    title = title || "&#160;";
36079      //   if(this.collapsedTitleTextEl){
36080       //      this.collapsedTitleTextEl.innerHTML = title;
36081        // }
36082     //},
36083
36084     getBox : function(){
36085         var b;
36086       //  if(!this.collapsed){
36087             b = this.el.getBox(false, true);
36088        // }else{
36089           //  b = this.collapsedEl.getBox(false, true);
36090         //}
36091         return b;
36092     },
36093
36094     getMargins : function(){
36095         return this.margins;
36096         //return this.collapsed ? this.cmargins : this.margins;
36097     },
36098 /*
36099     highlight : function(){
36100         this.el.addClass("x-layout-panel-dragover");
36101     },
36102
36103     unhighlight : function(){
36104         this.el.removeClass("x-layout-panel-dragover");
36105     },
36106 */
36107     updateBox : function(box)
36108     {
36109         if (!this.bodyEl) {
36110             return; // not rendered yet..
36111         }
36112         
36113         this.box = box;
36114         if(!this.collapsed){
36115             this.el.dom.style.left = box.x + "px";
36116             this.el.dom.style.top = box.y + "px";
36117             this.updateBody(box.width, box.height);
36118         }else{
36119             this.collapsedEl.dom.style.left = box.x + "px";
36120             this.collapsedEl.dom.style.top = box.y + "px";
36121             this.collapsedEl.setSize(box.width, box.height);
36122         }
36123         if(this.tabs){
36124             this.tabs.autoSizeTabs();
36125         }
36126     },
36127
36128     updateBody : function(w, h)
36129     {
36130         if(w !== null){
36131             this.el.setWidth(w);
36132             w -= this.el.getBorderWidth("rl");
36133             if(this.config.adjustments){
36134                 w += this.config.adjustments[0];
36135             }
36136         }
36137         if(h !== null && h > 0){
36138             this.el.setHeight(h);
36139             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36140             h -= this.el.getBorderWidth("tb");
36141             if(this.config.adjustments){
36142                 h += this.config.adjustments[1];
36143             }
36144             this.bodyEl.setHeight(h);
36145             if(this.tabs){
36146                 h = this.tabs.syncHeight(h);
36147             }
36148         }
36149         if(this.panelSize){
36150             w = w !== null ? w : this.panelSize.width;
36151             h = h !== null ? h : this.panelSize.height;
36152         }
36153         if(this.activePanel){
36154             var el = this.activePanel.getEl();
36155             w = w !== null ? w : el.getWidth();
36156             h = h !== null ? h : el.getHeight();
36157             this.panelSize = {width: w, height: h};
36158             this.activePanel.setSize(w, h);
36159         }
36160         if(Roo.isIE && this.tabs){
36161             this.tabs.el.repaint();
36162         }
36163     },
36164
36165     /**
36166      * Returns the container element for this region.
36167      * @return {Roo.Element}
36168      */
36169     getEl : function(){
36170         return this.el;
36171     },
36172
36173     /**
36174      * Hides this region.
36175      */
36176     hide : function(){
36177         //if(!this.collapsed){
36178             this.el.dom.style.left = "-2000px";
36179             this.el.hide();
36180         //}else{
36181          //   this.collapsedEl.dom.style.left = "-2000px";
36182          //   this.collapsedEl.hide();
36183        // }
36184         this.visible = false;
36185         this.fireEvent("visibilitychange", this, false);
36186     },
36187
36188     /**
36189      * Shows this region if it was previously hidden.
36190      */
36191     show : function(){
36192         //if(!this.collapsed){
36193             this.el.show();
36194         //}else{
36195         //    this.collapsedEl.show();
36196        // }
36197         this.visible = true;
36198         this.fireEvent("visibilitychange", this, true);
36199     },
36200 /*
36201     closeClicked : function(){
36202         if(this.activePanel){
36203             this.remove(this.activePanel);
36204         }
36205     },
36206
36207     collapseClick : function(e){
36208         if(this.isSlid){
36209            e.stopPropagation();
36210            this.slideIn();
36211         }else{
36212            e.stopPropagation();
36213            this.slideOut();
36214         }
36215     },
36216 */
36217     /**
36218      * Collapses this region.
36219      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36220      */
36221     /*
36222     collapse : function(skipAnim, skipCheck = false){
36223         if(this.collapsed) {
36224             return;
36225         }
36226         
36227         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36228             
36229             this.collapsed = true;
36230             if(this.split){
36231                 this.split.el.hide();
36232             }
36233             if(this.config.animate && skipAnim !== true){
36234                 this.fireEvent("invalidated", this);
36235                 this.animateCollapse();
36236             }else{
36237                 this.el.setLocation(-20000,-20000);
36238                 this.el.hide();
36239                 this.collapsedEl.show();
36240                 this.fireEvent("collapsed", this);
36241                 this.fireEvent("invalidated", this);
36242             }
36243         }
36244         
36245     },
36246 */
36247     animateCollapse : function(){
36248         // overridden
36249     },
36250
36251     /**
36252      * Expands this region if it was previously collapsed.
36253      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36254      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36255      */
36256     /*
36257     expand : function(e, skipAnim){
36258         if(e) {
36259             e.stopPropagation();
36260         }
36261         if(!this.collapsed || this.el.hasActiveFx()) {
36262             return;
36263         }
36264         if(this.isSlid){
36265             this.afterSlideIn();
36266             skipAnim = true;
36267         }
36268         this.collapsed = false;
36269         if(this.config.animate && skipAnim !== true){
36270             this.animateExpand();
36271         }else{
36272             this.el.show();
36273             if(this.split){
36274                 this.split.el.show();
36275             }
36276             this.collapsedEl.setLocation(-2000,-2000);
36277             this.collapsedEl.hide();
36278             this.fireEvent("invalidated", this);
36279             this.fireEvent("expanded", this);
36280         }
36281     },
36282 */
36283     animateExpand : function(){
36284         // overridden
36285     },
36286
36287     initTabs : function()
36288     {
36289         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36290         
36291         var ts = new Roo.bootstrap.panel.Tabs({
36292                 el: this.bodyEl.dom,
36293                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36294                 disableTooltips: this.config.disableTabTips,
36295                 toolbar : this.config.toolbar
36296             });
36297         
36298         if(this.config.hideTabs){
36299             ts.stripWrap.setDisplayed(false);
36300         }
36301         this.tabs = ts;
36302         ts.resizeTabs = this.config.resizeTabs === true;
36303         ts.minTabWidth = this.config.minTabWidth || 40;
36304         ts.maxTabWidth = this.config.maxTabWidth || 250;
36305         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36306         ts.monitorResize = false;
36307         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36308         ts.bodyEl.addClass('roo-layout-tabs-body');
36309         this.panels.each(this.initPanelAsTab, this);
36310     },
36311
36312     initPanelAsTab : function(panel){
36313         var ti = this.tabs.addTab(
36314             panel.getEl().id,
36315             panel.getTitle(),
36316             null,
36317             this.config.closeOnTab && panel.isClosable(),
36318             panel.tpl
36319         );
36320         if(panel.tabTip !== undefined){
36321             ti.setTooltip(panel.tabTip);
36322         }
36323         ti.on("activate", function(){
36324               this.setActivePanel(panel);
36325         }, this);
36326         
36327         if(this.config.closeOnTab){
36328             ti.on("beforeclose", function(t, e){
36329                 e.cancel = true;
36330                 this.remove(panel);
36331             }, this);
36332         }
36333         
36334         panel.tabItem = ti;
36335         
36336         return ti;
36337     },
36338
36339     updatePanelTitle : function(panel, title)
36340     {
36341         if(this.activePanel == panel){
36342             this.updateTitle(title);
36343         }
36344         if(this.tabs){
36345             var ti = this.tabs.getTab(panel.getEl().id);
36346             ti.setText(title);
36347             if(panel.tabTip !== undefined){
36348                 ti.setTooltip(panel.tabTip);
36349             }
36350         }
36351     },
36352
36353     updateTitle : function(title){
36354         if(this.titleTextEl && !this.config.title){
36355             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36356         }
36357     },
36358
36359     setActivePanel : function(panel)
36360     {
36361         panel = this.getPanel(panel);
36362         if(this.activePanel && this.activePanel != panel){
36363             if(this.activePanel.setActiveState(false) === false){
36364                 return;
36365             }
36366         }
36367         this.activePanel = panel;
36368         panel.setActiveState(true);
36369         if(this.panelSize){
36370             panel.setSize(this.panelSize.width, this.panelSize.height);
36371         }
36372         if(this.closeBtn){
36373             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36374         }
36375         this.updateTitle(panel.getTitle());
36376         if(this.tabs){
36377             this.fireEvent("invalidated", this);
36378         }
36379         this.fireEvent("panelactivated", this, panel);
36380     },
36381
36382     /**
36383      * Shows the specified panel.
36384      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36385      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36386      */
36387     showPanel : function(panel)
36388     {
36389         panel = this.getPanel(panel);
36390         if(panel){
36391             if(this.tabs){
36392                 var tab = this.tabs.getTab(panel.getEl().id);
36393                 if(tab.isHidden()){
36394                     this.tabs.unhideTab(tab.id);
36395                 }
36396                 tab.activate();
36397             }else{
36398                 this.setActivePanel(panel);
36399             }
36400         }
36401         return panel;
36402     },
36403
36404     /**
36405      * Get the active panel for this region.
36406      * @return {Roo.ContentPanel} The active panel or null
36407      */
36408     getActivePanel : function(){
36409         return this.activePanel;
36410     },
36411
36412     validateVisibility : function(){
36413         if(this.panels.getCount() < 1){
36414             this.updateTitle("&#160;");
36415             this.closeBtn.hide();
36416             this.hide();
36417         }else{
36418             if(!this.isVisible()){
36419                 this.show();
36420             }
36421         }
36422     },
36423
36424     /**
36425      * Adds the passed ContentPanel(s) to this region.
36426      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36427      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36428      */
36429     add : function(panel)
36430     {
36431         if(arguments.length > 1){
36432             for(var i = 0, len = arguments.length; i < len; i++) {
36433                 this.add(arguments[i]);
36434             }
36435             return null;
36436         }
36437         
36438         // if we have not been rendered yet, then we can not really do much of this..
36439         if (!this.bodyEl) {
36440             this.unrendered_panels.push(panel);
36441             return panel;
36442         }
36443         
36444         
36445         
36446         
36447         if(this.hasPanel(panel)){
36448             this.showPanel(panel);
36449             return panel;
36450         }
36451         panel.setRegion(this);
36452         this.panels.add(panel);
36453        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36454             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36455             // and hide them... ???
36456             this.bodyEl.dom.appendChild(panel.getEl().dom);
36457             if(panel.background !== true){
36458                 this.setActivePanel(panel);
36459             }
36460             this.fireEvent("paneladded", this, panel);
36461             return panel;
36462         }
36463         */
36464         if(!this.tabs){
36465             this.initTabs();
36466         }else{
36467             this.initPanelAsTab(panel);
36468         }
36469         
36470         
36471         if(panel.background !== true){
36472             this.tabs.activate(panel.getEl().id);
36473         }
36474         this.fireEvent("paneladded", this, panel);
36475         return panel;
36476     },
36477
36478     /**
36479      * Hides the tab for the specified panel.
36480      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36481      */
36482     hidePanel : function(panel){
36483         if(this.tabs && (panel = this.getPanel(panel))){
36484             this.tabs.hideTab(panel.getEl().id);
36485         }
36486     },
36487
36488     /**
36489      * Unhides the tab for a previously hidden panel.
36490      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36491      */
36492     unhidePanel : function(panel){
36493         if(this.tabs && (panel = this.getPanel(panel))){
36494             this.tabs.unhideTab(panel.getEl().id);
36495         }
36496     },
36497
36498     clearPanels : function(){
36499         while(this.panels.getCount() > 0){
36500              this.remove(this.panels.first());
36501         }
36502     },
36503
36504     /**
36505      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36506      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36507      * @param {Boolean} preservePanel Overrides the config preservePanel option
36508      * @return {Roo.ContentPanel} The panel that was removed
36509      */
36510     remove : function(panel, preservePanel)
36511     {
36512         panel = this.getPanel(panel);
36513         if(!panel){
36514             return null;
36515         }
36516         var e = {};
36517         this.fireEvent("beforeremove", this, panel, e);
36518         if(e.cancel === true){
36519             return null;
36520         }
36521         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36522         var panelId = panel.getId();
36523         this.panels.removeKey(panelId);
36524         if(preservePanel){
36525             document.body.appendChild(panel.getEl().dom);
36526         }
36527         if(this.tabs){
36528             this.tabs.removeTab(panel.getEl().id);
36529         }else if (!preservePanel){
36530             this.bodyEl.dom.removeChild(panel.getEl().dom);
36531         }
36532         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36533             var p = this.panels.first();
36534             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36535             tempEl.appendChild(p.getEl().dom);
36536             this.bodyEl.update("");
36537             this.bodyEl.dom.appendChild(p.getEl().dom);
36538             tempEl = null;
36539             this.updateTitle(p.getTitle());
36540             this.tabs = null;
36541             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36542             this.setActivePanel(p);
36543         }
36544         panel.setRegion(null);
36545         if(this.activePanel == panel){
36546             this.activePanel = null;
36547         }
36548         if(this.config.autoDestroy !== false && preservePanel !== true){
36549             try{panel.destroy();}catch(e){}
36550         }
36551         this.fireEvent("panelremoved", this, panel);
36552         return panel;
36553     },
36554
36555     /**
36556      * Returns the TabPanel component used by this region
36557      * @return {Roo.TabPanel}
36558      */
36559     getTabs : function(){
36560         return this.tabs;
36561     },
36562
36563     createTool : function(parentEl, className){
36564         var btn = Roo.DomHelper.append(parentEl, {
36565             tag: "div",
36566             cls: "x-layout-tools-button",
36567             children: [ {
36568                 tag: "div",
36569                 cls: "roo-layout-tools-button-inner " + className,
36570                 html: "&#160;"
36571             }]
36572         }, true);
36573         btn.addClassOnOver("roo-layout-tools-button-over");
36574         return btn;
36575     }
36576 });/*
36577  * Based on:
36578  * Ext JS Library 1.1.1
36579  * Copyright(c) 2006-2007, Ext JS, LLC.
36580  *
36581  * Originally Released Under LGPL - original licence link has changed is not relivant.
36582  *
36583  * Fork - LGPL
36584  * <script type="text/javascript">
36585  */
36586  
36587
36588
36589 /**
36590  * @class Roo.SplitLayoutRegion
36591  * @extends Roo.LayoutRegion
36592  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36593  */
36594 Roo.bootstrap.layout.Split = function(config){
36595     this.cursor = config.cursor;
36596     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36597 };
36598
36599 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36600 {
36601     splitTip : "Drag to resize.",
36602     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36603     useSplitTips : false,
36604
36605     applyConfig : function(config){
36606         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36607     },
36608     
36609     onRender : function(ctr,pos) {
36610         
36611         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36612         if(!this.config.split){
36613             return;
36614         }
36615         if(!this.split){
36616             
36617             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36618                             tag: "div",
36619                             id: this.el.id + "-split",
36620                             cls: "roo-layout-split roo-layout-split-"+this.position,
36621                             html: "&#160;"
36622             });
36623             /** The SplitBar for this region 
36624             * @type Roo.SplitBar */
36625             // does not exist yet...
36626             Roo.log([this.position, this.orientation]);
36627             
36628             this.split = new Roo.bootstrap.SplitBar({
36629                 dragElement : splitEl,
36630                 resizingElement: this.el,
36631                 orientation : this.orientation
36632             });
36633             
36634             this.split.on("moved", this.onSplitMove, this);
36635             this.split.useShim = this.config.useShim === true;
36636             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36637             if(this.useSplitTips){
36638                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36639             }
36640             //if(config.collapsible){
36641             //    this.split.el.on("dblclick", this.collapse,  this);
36642             //}
36643         }
36644         if(typeof this.config.minSize != "undefined"){
36645             this.split.minSize = this.config.minSize;
36646         }
36647         if(typeof this.config.maxSize != "undefined"){
36648             this.split.maxSize = this.config.maxSize;
36649         }
36650         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36651             this.hideSplitter();
36652         }
36653         
36654     },
36655
36656     getHMaxSize : function(){
36657          var cmax = this.config.maxSize || 10000;
36658          var center = this.mgr.getRegion("center");
36659          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36660     },
36661
36662     getVMaxSize : function(){
36663          var cmax = this.config.maxSize || 10000;
36664          var center = this.mgr.getRegion("center");
36665          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36666     },
36667
36668     onSplitMove : function(split, newSize){
36669         this.fireEvent("resized", this, newSize);
36670     },
36671     
36672     /** 
36673      * Returns the {@link Roo.SplitBar} for this region.
36674      * @return {Roo.SplitBar}
36675      */
36676     getSplitBar : function(){
36677         return this.split;
36678     },
36679     
36680     hide : function(){
36681         this.hideSplitter();
36682         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36683     },
36684
36685     hideSplitter : function(){
36686         if(this.split){
36687             this.split.el.setLocation(-2000,-2000);
36688             this.split.el.hide();
36689         }
36690     },
36691
36692     show : function(){
36693         if(this.split){
36694             this.split.el.show();
36695         }
36696         Roo.bootstrap.layout.Split.superclass.show.call(this);
36697     },
36698     
36699     beforeSlide: function(){
36700         if(Roo.isGecko){// firefox overflow auto bug workaround
36701             this.bodyEl.clip();
36702             if(this.tabs) {
36703                 this.tabs.bodyEl.clip();
36704             }
36705             if(this.activePanel){
36706                 this.activePanel.getEl().clip();
36707                 
36708                 if(this.activePanel.beforeSlide){
36709                     this.activePanel.beforeSlide();
36710                 }
36711             }
36712         }
36713     },
36714     
36715     afterSlide : function(){
36716         if(Roo.isGecko){// firefox overflow auto bug workaround
36717             this.bodyEl.unclip();
36718             if(this.tabs) {
36719                 this.tabs.bodyEl.unclip();
36720             }
36721             if(this.activePanel){
36722                 this.activePanel.getEl().unclip();
36723                 if(this.activePanel.afterSlide){
36724                     this.activePanel.afterSlide();
36725                 }
36726             }
36727         }
36728     },
36729
36730     initAutoHide : function(){
36731         if(this.autoHide !== false){
36732             if(!this.autoHideHd){
36733                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36734                 this.autoHideHd = {
36735                     "mouseout": function(e){
36736                         if(!e.within(this.el, true)){
36737                             st.delay(500);
36738                         }
36739                     },
36740                     "mouseover" : function(e){
36741                         st.cancel();
36742                     },
36743                     scope : this
36744                 };
36745             }
36746             this.el.on(this.autoHideHd);
36747         }
36748     },
36749
36750     clearAutoHide : function(){
36751         if(this.autoHide !== false){
36752             this.el.un("mouseout", this.autoHideHd.mouseout);
36753             this.el.un("mouseover", this.autoHideHd.mouseover);
36754         }
36755     },
36756
36757     clearMonitor : function(){
36758         Roo.get(document).un("click", this.slideInIf, this);
36759     },
36760
36761     // these names are backwards but not changed for compat
36762     slideOut : function(){
36763         if(this.isSlid || this.el.hasActiveFx()){
36764             return;
36765         }
36766         this.isSlid = true;
36767         if(this.collapseBtn){
36768             this.collapseBtn.hide();
36769         }
36770         this.closeBtnState = this.closeBtn.getStyle('display');
36771         this.closeBtn.hide();
36772         if(this.stickBtn){
36773             this.stickBtn.show();
36774         }
36775         this.el.show();
36776         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36777         this.beforeSlide();
36778         this.el.setStyle("z-index", 10001);
36779         this.el.slideIn(this.getSlideAnchor(), {
36780             callback: function(){
36781                 this.afterSlide();
36782                 this.initAutoHide();
36783                 Roo.get(document).on("click", this.slideInIf, this);
36784                 this.fireEvent("slideshow", this);
36785             },
36786             scope: this,
36787             block: true
36788         });
36789     },
36790
36791     afterSlideIn : function(){
36792         this.clearAutoHide();
36793         this.isSlid = false;
36794         this.clearMonitor();
36795         this.el.setStyle("z-index", "");
36796         if(this.collapseBtn){
36797             this.collapseBtn.show();
36798         }
36799         this.closeBtn.setStyle('display', this.closeBtnState);
36800         if(this.stickBtn){
36801             this.stickBtn.hide();
36802         }
36803         this.fireEvent("slidehide", this);
36804     },
36805
36806     slideIn : function(cb){
36807         if(!this.isSlid || this.el.hasActiveFx()){
36808             Roo.callback(cb);
36809             return;
36810         }
36811         this.isSlid = false;
36812         this.beforeSlide();
36813         this.el.slideOut(this.getSlideAnchor(), {
36814             callback: function(){
36815                 this.el.setLeftTop(-10000, -10000);
36816                 this.afterSlide();
36817                 this.afterSlideIn();
36818                 Roo.callback(cb);
36819             },
36820             scope: this,
36821             block: true
36822         });
36823     },
36824     
36825     slideInIf : function(e){
36826         if(!e.within(this.el)){
36827             this.slideIn();
36828         }
36829     },
36830
36831     animateCollapse : function(){
36832         this.beforeSlide();
36833         this.el.setStyle("z-index", 20000);
36834         var anchor = this.getSlideAnchor();
36835         this.el.slideOut(anchor, {
36836             callback : function(){
36837                 this.el.setStyle("z-index", "");
36838                 this.collapsedEl.slideIn(anchor, {duration:.3});
36839                 this.afterSlide();
36840                 this.el.setLocation(-10000,-10000);
36841                 this.el.hide();
36842                 this.fireEvent("collapsed", this);
36843             },
36844             scope: this,
36845             block: true
36846         });
36847     },
36848
36849     animateExpand : function(){
36850         this.beforeSlide();
36851         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36852         this.el.setStyle("z-index", 20000);
36853         this.collapsedEl.hide({
36854             duration:.1
36855         });
36856         this.el.slideIn(this.getSlideAnchor(), {
36857             callback : function(){
36858                 this.el.setStyle("z-index", "");
36859                 this.afterSlide();
36860                 if(this.split){
36861                     this.split.el.show();
36862                 }
36863                 this.fireEvent("invalidated", this);
36864                 this.fireEvent("expanded", this);
36865             },
36866             scope: this,
36867             block: true
36868         });
36869     },
36870
36871     anchors : {
36872         "west" : "left",
36873         "east" : "right",
36874         "north" : "top",
36875         "south" : "bottom"
36876     },
36877
36878     sanchors : {
36879         "west" : "l",
36880         "east" : "r",
36881         "north" : "t",
36882         "south" : "b"
36883     },
36884
36885     canchors : {
36886         "west" : "tl-tr",
36887         "east" : "tr-tl",
36888         "north" : "tl-bl",
36889         "south" : "bl-tl"
36890     },
36891
36892     getAnchor : function(){
36893         return this.anchors[this.position];
36894     },
36895
36896     getCollapseAnchor : function(){
36897         return this.canchors[this.position];
36898     },
36899
36900     getSlideAnchor : function(){
36901         return this.sanchors[this.position];
36902     },
36903
36904     getAlignAdj : function(){
36905         var cm = this.cmargins;
36906         switch(this.position){
36907             case "west":
36908                 return [0, 0];
36909             break;
36910             case "east":
36911                 return [0, 0];
36912             break;
36913             case "north":
36914                 return [0, 0];
36915             break;
36916             case "south":
36917                 return [0, 0];
36918             break;
36919         }
36920     },
36921
36922     getExpandAdj : function(){
36923         var c = this.collapsedEl, cm = this.cmargins;
36924         switch(this.position){
36925             case "west":
36926                 return [-(cm.right+c.getWidth()+cm.left), 0];
36927             break;
36928             case "east":
36929                 return [cm.right+c.getWidth()+cm.left, 0];
36930             break;
36931             case "north":
36932                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36933             break;
36934             case "south":
36935                 return [0, cm.top+cm.bottom+c.getHeight()];
36936             break;
36937         }
36938     }
36939 });/*
36940  * Based on:
36941  * Ext JS Library 1.1.1
36942  * Copyright(c) 2006-2007, Ext JS, LLC.
36943  *
36944  * Originally Released Under LGPL - original licence link has changed is not relivant.
36945  *
36946  * Fork - LGPL
36947  * <script type="text/javascript">
36948  */
36949 /*
36950  * These classes are private internal classes
36951  */
36952 Roo.bootstrap.layout.Center = function(config){
36953     config.region = "center";
36954     Roo.bootstrap.layout.Region.call(this, config);
36955     this.visible = true;
36956     this.minWidth = config.minWidth || 20;
36957     this.minHeight = config.minHeight || 20;
36958 };
36959
36960 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36961     hide : function(){
36962         // center panel can't be hidden
36963     },
36964     
36965     show : function(){
36966         // center panel can't be hidden
36967     },
36968     
36969     getMinWidth: function(){
36970         return this.minWidth;
36971     },
36972     
36973     getMinHeight: function(){
36974         return this.minHeight;
36975     }
36976 });
36977
36978
36979
36980
36981  
36982
36983
36984
36985
36986
36987 Roo.bootstrap.layout.North = function(config)
36988 {
36989     config.region = 'north';
36990     config.cursor = 'n-resize';
36991     
36992     Roo.bootstrap.layout.Split.call(this, config);
36993     
36994     
36995     if(this.split){
36996         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36997         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36998         this.split.el.addClass("roo-layout-split-v");
36999     }
37000     var size = config.initialSize || config.height;
37001     if(typeof size != "undefined"){
37002         this.el.setHeight(size);
37003     }
37004 };
37005 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37006 {
37007     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37008     
37009     
37010     
37011     getBox : function(){
37012         if(this.collapsed){
37013             return this.collapsedEl.getBox();
37014         }
37015         var box = this.el.getBox();
37016         if(this.split){
37017             box.height += this.split.el.getHeight();
37018         }
37019         return box;
37020     },
37021     
37022     updateBox : function(box){
37023         if(this.split && !this.collapsed){
37024             box.height -= this.split.el.getHeight();
37025             this.split.el.setLeft(box.x);
37026             this.split.el.setTop(box.y+box.height);
37027             this.split.el.setWidth(box.width);
37028         }
37029         if(this.collapsed){
37030             this.updateBody(box.width, null);
37031         }
37032         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37033     }
37034 });
37035
37036
37037
37038
37039
37040 Roo.bootstrap.layout.South = function(config){
37041     config.region = 'south';
37042     config.cursor = 's-resize';
37043     Roo.bootstrap.layout.Split.call(this, config);
37044     if(this.split){
37045         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37046         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37047         this.split.el.addClass("roo-layout-split-v");
37048     }
37049     var size = config.initialSize || config.height;
37050     if(typeof size != "undefined"){
37051         this.el.setHeight(size);
37052     }
37053 };
37054
37055 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37056     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37057     getBox : function(){
37058         if(this.collapsed){
37059             return this.collapsedEl.getBox();
37060         }
37061         var box = this.el.getBox();
37062         if(this.split){
37063             var sh = this.split.el.getHeight();
37064             box.height += sh;
37065             box.y -= sh;
37066         }
37067         return box;
37068     },
37069     
37070     updateBox : function(box){
37071         if(this.split && !this.collapsed){
37072             var sh = this.split.el.getHeight();
37073             box.height -= sh;
37074             box.y += sh;
37075             this.split.el.setLeft(box.x);
37076             this.split.el.setTop(box.y-sh);
37077             this.split.el.setWidth(box.width);
37078         }
37079         if(this.collapsed){
37080             this.updateBody(box.width, null);
37081         }
37082         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37083     }
37084 });
37085
37086 Roo.bootstrap.layout.East = function(config){
37087     config.region = "east";
37088     config.cursor = "e-resize";
37089     Roo.bootstrap.layout.Split.call(this, config);
37090     if(this.split){
37091         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37092         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37093         this.split.el.addClass("roo-layout-split-h");
37094     }
37095     var size = config.initialSize || config.width;
37096     if(typeof size != "undefined"){
37097         this.el.setWidth(size);
37098     }
37099 };
37100 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37101     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37102     getBox : function(){
37103         if(this.collapsed){
37104             return this.collapsedEl.getBox();
37105         }
37106         var box = this.el.getBox();
37107         if(this.split){
37108             var sw = this.split.el.getWidth();
37109             box.width += sw;
37110             box.x -= sw;
37111         }
37112         return box;
37113     },
37114
37115     updateBox : function(box){
37116         if(this.split && !this.collapsed){
37117             var sw = this.split.el.getWidth();
37118             box.width -= sw;
37119             this.split.el.setLeft(box.x);
37120             this.split.el.setTop(box.y);
37121             this.split.el.setHeight(box.height);
37122             box.x += sw;
37123         }
37124         if(this.collapsed){
37125             this.updateBody(null, box.height);
37126         }
37127         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37128     }
37129 });
37130
37131 Roo.bootstrap.layout.West = function(config){
37132     config.region = "west";
37133     config.cursor = "w-resize";
37134     
37135     Roo.bootstrap.layout.Split.call(this, config);
37136     if(this.split){
37137         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37138         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37139         this.split.el.addClass("roo-layout-split-h");
37140     }
37141     
37142 };
37143 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37144     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37145     
37146     onRender: function(ctr, pos)
37147     {
37148         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37149         var size = this.config.initialSize || this.config.width;
37150         if(typeof size != "undefined"){
37151             this.el.setWidth(size);
37152         }
37153     },
37154     
37155     getBox : function(){
37156         if(this.collapsed){
37157             return this.collapsedEl.getBox();
37158         }
37159         var box = this.el.getBox();
37160         if(this.split){
37161             box.width += this.split.el.getWidth();
37162         }
37163         return box;
37164     },
37165     
37166     updateBox : function(box){
37167         if(this.split && !this.collapsed){
37168             var sw = this.split.el.getWidth();
37169             box.width -= sw;
37170             this.split.el.setLeft(box.x+box.width);
37171             this.split.el.setTop(box.y);
37172             this.split.el.setHeight(box.height);
37173         }
37174         if(this.collapsed){
37175             this.updateBody(null, box.height);
37176         }
37177         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37178     }
37179 });
37180 Roo.namespace("Roo.bootstrap.panel");/*
37181  * Based on:
37182  * Ext JS Library 1.1.1
37183  * Copyright(c) 2006-2007, Ext JS, LLC.
37184  *
37185  * Originally Released Under LGPL - original licence link has changed is not relivant.
37186  *
37187  * Fork - LGPL
37188  * <script type="text/javascript">
37189  */
37190 /**
37191  * @class Roo.ContentPanel
37192  * @extends Roo.util.Observable
37193  * A basic ContentPanel element.
37194  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37195  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37196  * @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
37197  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37198  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37199  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37200  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37201  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37202  * @cfg {String} title          The title for this panel
37203  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37204  * @cfg {String} url            Calls {@link #setUrl} with this value
37205  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37206  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37207  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37208  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37209  * @cfg {Boolean} badges render the badges
37210
37211  * @constructor
37212  * Create a new ContentPanel.
37213  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37214  * @param {String/Object} config A string to set only the title or a config object
37215  * @param {String} content (optional) Set the HTML content for this panel
37216  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37217  */
37218 Roo.bootstrap.panel.Content = function( config){
37219     
37220     this.tpl = config.tpl || false;
37221     
37222     var el = config.el;
37223     var content = config.content;
37224
37225     if(config.autoCreate){ // xtype is available if this is called from factory
37226         el = Roo.id();
37227     }
37228     this.el = Roo.get(el);
37229     if(!this.el && config && config.autoCreate){
37230         if(typeof config.autoCreate == "object"){
37231             if(!config.autoCreate.id){
37232                 config.autoCreate.id = config.id||el;
37233             }
37234             this.el = Roo.DomHelper.append(document.body,
37235                         config.autoCreate, true);
37236         }else{
37237             var elcfg =  {   tag: "div",
37238                             cls: "roo-layout-inactive-content",
37239                             id: config.id||el
37240                             };
37241             if (config.html) {
37242                 elcfg.html = config.html;
37243                 
37244             }
37245                         
37246             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37247         }
37248     } 
37249     this.closable = false;
37250     this.loaded = false;
37251     this.active = false;
37252    
37253       
37254     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37255         
37256         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37257         
37258         this.wrapEl = this.el; //this.el.wrap();
37259         var ti = [];
37260         if (config.toolbar.items) {
37261             ti = config.toolbar.items ;
37262             delete config.toolbar.items ;
37263         }
37264         
37265         var nitems = [];
37266         this.toolbar.render(this.wrapEl, 'before');
37267         for(var i =0;i < ti.length;i++) {
37268           //  Roo.log(['add child', items[i]]);
37269             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37270         }
37271         this.toolbar.items = nitems;
37272         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37273         delete config.toolbar;
37274         
37275     }
37276     /*
37277     // xtype created footer. - not sure if will work as we normally have to render first..
37278     if (this.footer && !this.footer.el && this.footer.xtype) {
37279         if (!this.wrapEl) {
37280             this.wrapEl = this.el.wrap();
37281         }
37282     
37283         this.footer.container = this.wrapEl.createChild();
37284          
37285         this.footer = Roo.factory(this.footer, Roo);
37286         
37287     }
37288     */
37289     
37290      if(typeof config == "string"){
37291         this.title = config;
37292     }else{
37293         Roo.apply(this, config);
37294     }
37295     
37296     if(this.resizeEl){
37297         this.resizeEl = Roo.get(this.resizeEl, true);
37298     }else{
37299         this.resizeEl = this.el;
37300     }
37301     // handle view.xtype
37302     
37303  
37304     
37305     
37306     this.addEvents({
37307         /**
37308          * @event activate
37309          * Fires when this panel is activated. 
37310          * @param {Roo.ContentPanel} this
37311          */
37312         "activate" : true,
37313         /**
37314          * @event deactivate
37315          * Fires when this panel is activated. 
37316          * @param {Roo.ContentPanel} this
37317          */
37318         "deactivate" : true,
37319
37320         /**
37321          * @event resize
37322          * Fires when this panel is resized if fitToFrame is true.
37323          * @param {Roo.ContentPanel} this
37324          * @param {Number} width The width after any component adjustments
37325          * @param {Number} height The height after any component adjustments
37326          */
37327         "resize" : true,
37328         
37329          /**
37330          * @event render
37331          * Fires when this tab is created
37332          * @param {Roo.ContentPanel} this
37333          */
37334         "render" : true
37335         
37336         
37337         
37338     });
37339     
37340
37341     
37342     
37343     if(this.autoScroll){
37344         this.resizeEl.setStyle("overflow", "auto");
37345     } else {
37346         // fix randome scrolling
37347         //this.el.on('scroll', function() {
37348         //    Roo.log('fix random scolling');
37349         //    this.scrollTo('top',0); 
37350         //});
37351     }
37352     content = content || this.content;
37353     if(content){
37354         this.setContent(content);
37355     }
37356     if(config && config.url){
37357         this.setUrl(this.url, this.params, this.loadOnce);
37358     }
37359     
37360     
37361     
37362     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37363     
37364     if (this.view && typeof(this.view.xtype) != 'undefined') {
37365         this.view.el = this.el.appendChild(document.createElement("div"));
37366         this.view = Roo.factory(this.view); 
37367         this.view.render  &&  this.view.render(false, '');  
37368     }
37369     
37370     
37371     this.fireEvent('render', this);
37372 };
37373
37374 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37375     
37376     tabTip : '',
37377     
37378     setRegion : function(region){
37379         this.region = region;
37380         this.setActiveClass(region && !this.background);
37381     },
37382     
37383     
37384     setActiveClass: function(state)
37385     {
37386         if(state){
37387            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37388            this.el.setStyle('position','relative');
37389         }else{
37390            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37391            this.el.setStyle('position', 'absolute');
37392         } 
37393     },
37394     
37395     /**
37396      * Returns the toolbar for this Panel if one was configured. 
37397      * @return {Roo.Toolbar} 
37398      */
37399     getToolbar : function(){
37400         return this.toolbar;
37401     },
37402     
37403     setActiveState : function(active)
37404     {
37405         this.active = active;
37406         this.setActiveClass(active);
37407         if(!active){
37408             if(this.fireEvent("deactivate", this) === false){
37409                 return false;
37410             }
37411             return true;
37412         }
37413         this.fireEvent("activate", this);
37414         return true;
37415     },
37416     /**
37417      * Updates this panel's element
37418      * @param {String} content The new content
37419      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37420     */
37421     setContent : function(content, loadScripts){
37422         this.el.update(content, loadScripts);
37423     },
37424
37425     ignoreResize : function(w, h){
37426         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37427             return true;
37428         }else{
37429             this.lastSize = {width: w, height: h};
37430             return false;
37431         }
37432     },
37433     /**
37434      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37435      * @return {Roo.UpdateManager} The UpdateManager
37436      */
37437     getUpdateManager : function(){
37438         return this.el.getUpdateManager();
37439     },
37440      /**
37441      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37442      * @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:
37443 <pre><code>
37444 panel.load({
37445     url: "your-url.php",
37446     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37447     callback: yourFunction,
37448     scope: yourObject, //(optional scope)
37449     discardUrl: false,
37450     nocache: false,
37451     text: "Loading...",
37452     timeout: 30,
37453     scripts: false
37454 });
37455 </code></pre>
37456      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37457      * 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.
37458      * @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}
37459      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37460      * @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.
37461      * @return {Roo.ContentPanel} this
37462      */
37463     load : function(){
37464         var um = this.el.getUpdateManager();
37465         um.update.apply(um, arguments);
37466         return this;
37467     },
37468
37469
37470     /**
37471      * 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.
37472      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37473      * @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)
37474      * @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)
37475      * @return {Roo.UpdateManager} The UpdateManager
37476      */
37477     setUrl : function(url, params, loadOnce){
37478         if(this.refreshDelegate){
37479             this.removeListener("activate", this.refreshDelegate);
37480         }
37481         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37482         this.on("activate", this.refreshDelegate);
37483         return this.el.getUpdateManager();
37484     },
37485     
37486     _handleRefresh : function(url, params, loadOnce){
37487         if(!loadOnce || !this.loaded){
37488             var updater = this.el.getUpdateManager();
37489             updater.update(url, params, this._setLoaded.createDelegate(this));
37490         }
37491     },
37492     
37493     _setLoaded : function(){
37494         this.loaded = true;
37495     }, 
37496     
37497     /**
37498      * Returns this panel's id
37499      * @return {String} 
37500      */
37501     getId : function(){
37502         return this.el.id;
37503     },
37504     
37505     /** 
37506      * Returns this panel's element - used by regiosn to add.
37507      * @return {Roo.Element} 
37508      */
37509     getEl : function(){
37510         return this.wrapEl || this.el;
37511     },
37512     
37513    
37514     
37515     adjustForComponents : function(width, height)
37516     {
37517         //Roo.log('adjustForComponents ');
37518         if(this.resizeEl != this.el){
37519             width -= this.el.getFrameWidth('lr');
37520             height -= this.el.getFrameWidth('tb');
37521         }
37522         if(this.toolbar){
37523             var te = this.toolbar.getEl();
37524             te.setWidth(width);
37525             height -= te.getHeight();
37526         }
37527         if(this.footer){
37528             var te = this.footer.getEl();
37529             te.setWidth(width);
37530             height -= te.getHeight();
37531         }
37532         
37533         
37534         if(this.adjustments){
37535             width += this.adjustments[0];
37536             height += this.adjustments[1];
37537         }
37538         return {"width": width, "height": height};
37539     },
37540     
37541     setSize : function(width, height){
37542         if(this.fitToFrame && !this.ignoreResize(width, height)){
37543             if(this.fitContainer && this.resizeEl != this.el){
37544                 this.el.setSize(width, height);
37545             }
37546             var size = this.adjustForComponents(width, height);
37547             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37548             this.fireEvent('resize', this, size.width, size.height);
37549         }
37550     },
37551     
37552     /**
37553      * Returns this panel's title
37554      * @return {String} 
37555      */
37556     getTitle : function(){
37557         
37558         if (typeof(this.title) != 'object') {
37559             return this.title;
37560         }
37561         
37562         var t = '';
37563         for (var k in this.title) {
37564             if (!this.title.hasOwnProperty(k)) {
37565                 continue;
37566             }
37567             
37568             if (k.indexOf('-') >= 0) {
37569                 var s = k.split('-');
37570                 for (var i = 0; i<s.length; i++) {
37571                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37572                 }
37573             } else {
37574                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37575             }
37576         }
37577         return t;
37578     },
37579     
37580     /**
37581      * Set this panel's title
37582      * @param {String} title
37583      */
37584     setTitle : function(title){
37585         this.title = title;
37586         if(this.region){
37587             this.region.updatePanelTitle(this, title);
37588         }
37589     },
37590     
37591     /**
37592      * Returns true is this panel was configured to be closable
37593      * @return {Boolean} 
37594      */
37595     isClosable : function(){
37596         return this.closable;
37597     },
37598     
37599     beforeSlide : function(){
37600         this.el.clip();
37601         this.resizeEl.clip();
37602     },
37603     
37604     afterSlide : function(){
37605         this.el.unclip();
37606         this.resizeEl.unclip();
37607     },
37608     
37609     /**
37610      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37611      *   Will fail silently if the {@link #setUrl} method has not been called.
37612      *   This does not activate the panel, just updates its content.
37613      */
37614     refresh : function(){
37615         if(this.refreshDelegate){
37616            this.loaded = false;
37617            this.refreshDelegate();
37618         }
37619     },
37620     
37621     /**
37622      * Destroys this panel
37623      */
37624     destroy : function(){
37625         this.el.removeAllListeners();
37626         var tempEl = document.createElement("span");
37627         tempEl.appendChild(this.el.dom);
37628         tempEl.innerHTML = "";
37629         this.el.remove();
37630         this.el = null;
37631     },
37632     
37633     /**
37634      * form - if the content panel contains a form - this is a reference to it.
37635      * @type {Roo.form.Form}
37636      */
37637     form : false,
37638     /**
37639      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37640      *    This contains a reference to it.
37641      * @type {Roo.View}
37642      */
37643     view : false,
37644     
37645       /**
37646      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37647      * <pre><code>
37648
37649 layout.addxtype({
37650        xtype : 'Form',
37651        items: [ .... ]
37652    }
37653 );
37654
37655 </code></pre>
37656      * @param {Object} cfg Xtype definition of item to add.
37657      */
37658     
37659     
37660     getChildContainer: function () {
37661         return this.getEl();
37662     }
37663     
37664     
37665     /*
37666         var  ret = new Roo.factory(cfg);
37667         return ret;
37668         
37669         
37670         // add form..
37671         if (cfg.xtype.match(/^Form$/)) {
37672             
37673             var el;
37674             //if (this.footer) {
37675             //    el = this.footer.container.insertSibling(false, 'before');
37676             //} else {
37677                 el = this.el.createChild();
37678             //}
37679
37680             this.form = new  Roo.form.Form(cfg);
37681             
37682             
37683             if ( this.form.allItems.length) {
37684                 this.form.render(el.dom);
37685             }
37686             return this.form;
37687         }
37688         // should only have one of theses..
37689         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37690             // views.. should not be just added - used named prop 'view''
37691             
37692             cfg.el = this.el.appendChild(document.createElement("div"));
37693             // factory?
37694             
37695             var ret = new Roo.factory(cfg);
37696              
37697              ret.render && ret.render(false, ''); // render blank..
37698             this.view = ret;
37699             return ret;
37700         }
37701         return false;
37702     }
37703     \*/
37704 });
37705  
37706 /**
37707  * @class Roo.bootstrap.panel.Grid
37708  * @extends Roo.bootstrap.panel.Content
37709  * @constructor
37710  * Create a new GridPanel.
37711  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37712  * @param {Object} config A the config object
37713   
37714  */
37715
37716
37717
37718 Roo.bootstrap.panel.Grid = function(config)
37719 {
37720     
37721       
37722     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37723         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37724
37725     config.el = this.wrapper;
37726     //this.el = this.wrapper;
37727     
37728       if (config.container) {
37729         // ctor'ed from a Border/panel.grid
37730         
37731         
37732         this.wrapper.setStyle("overflow", "hidden");
37733         this.wrapper.addClass('roo-grid-container');
37734
37735     }
37736     
37737     
37738     if(config.toolbar){
37739         var tool_el = this.wrapper.createChild();    
37740         this.toolbar = Roo.factory(config.toolbar);
37741         var ti = [];
37742         if (config.toolbar.items) {
37743             ti = config.toolbar.items ;
37744             delete config.toolbar.items ;
37745         }
37746         
37747         var nitems = [];
37748         this.toolbar.render(tool_el);
37749         for(var i =0;i < ti.length;i++) {
37750           //  Roo.log(['add child', items[i]]);
37751             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37752         }
37753         this.toolbar.items = nitems;
37754         
37755         delete config.toolbar;
37756     }
37757     
37758     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37759     config.grid.scrollBody = true;;
37760     config.grid.monitorWindowResize = false; // turn off autosizing
37761     config.grid.autoHeight = false;
37762     config.grid.autoWidth = false;
37763     
37764     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37765     
37766     if (config.background) {
37767         // render grid on panel activation (if panel background)
37768         this.on('activate', function(gp) {
37769             if (!gp.grid.rendered) {
37770                 gp.grid.render(this.wrapper);
37771                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37772             }
37773         });
37774             
37775     } else {
37776         this.grid.render(this.wrapper);
37777         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37778
37779     }
37780     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37781     // ??? needed ??? config.el = this.wrapper;
37782     
37783     
37784     
37785   
37786     // xtype created footer. - not sure if will work as we normally have to render first..
37787     if (this.footer && !this.footer.el && this.footer.xtype) {
37788         
37789         var ctr = this.grid.getView().getFooterPanel(true);
37790         this.footer.dataSource = this.grid.dataSource;
37791         this.footer = Roo.factory(this.footer, Roo);
37792         this.footer.render(ctr);
37793         
37794     }
37795     
37796     
37797     
37798     
37799      
37800 };
37801
37802 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37803     getId : function(){
37804         return this.grid.id;
37805     },
37806     
37807     /**
37808      * Returns the grid for this panel
37809      * @return {Roo.bootstrap.Table} 
37810      */
37811     getGrid : function(){
37812         return this.grid;    
37813     },
37814     
37815     setSize : function(width, height){
37816         if(!this.ignoreResize(width, height)){
37817             var grid = this.grid;
37818             var size = this.adjustForComponents(width, height);
37819             var gridel = grid.getGridEl();
37820             gridel.setSize(size.width, size.height);
37821             /*
37822             var thd = grid.getGridEl().select('thead',true).first();
37823             var tbd = grid.getGridEl().select('tbody', true).first();
37824             if (tbd) {
37825                 tbd.setSize(width, height - thd.getHeight());
37826             }
37827             */
37828             grid.autoSize();
37829         }
37830     },
37831      
37832     
37833     
37834     beforeSlide : function(){
37835         this.grid.getView().scroller.clip();
37836     },
37837     
37838     afterSlide : function(){
37839         this.grid.getView().scroller.unclip();
37840     },
37841     
37842     destroy : function(){
37843         this.grid.destroy();
37844         delete this.grid;
37845         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37846     }
37847 });
37848
37849 /**
37850  * @class Roo.bootstrap.panel.Nest
37851  * @extends Roo.bootstrap.panel.Content
37852  * @constructor
37853  * Create a new Panel, that can contain a layout.Border.
37854  * 
37855  * 
37856  * @param {Roo.BorderLayout} layout The layout for this panel
37857  * @param {String/Object} config A string to set only the title or a config object
37858  */
37859 Roo.bootstrap.panel.Nest = function(config)
37860 {
37861     // construct with only one argument..
37862     /* FIXME - implement nicer consturctors
37863     if (layout.layout) {
37864         config = layout;
37865         layout = config.layout;
37866         delete config.layout;
37867     }
37868     if (layout.xtype && !layout.getEl) {
37869         // then layout needs constructing..
37870         layout = Roo.factory(layout, Roo);
37871     }
37872     */
37873     
37874     config.el =  config.layout.getEl();
37875     
37876     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37877     
37878     config.layout.monitorWindowResize = false; // turn off autosizing
37879     this.layout = config.layout;
37880     this.layout.getEl().addClass("roo-layout-nested-layout");
37881     
37882     
37883     
37884     
37885 };
37886
37887 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37888
37889     setSize : function(width, height){
37890         if(!this.ignoreResize(width, height)){
37891             var size = this.adjustForComponents(width, height);
37892             var el = this.layout.getEl();
37893             if (size.height < 1) {
37894                 el.setWidth(size.width);   
37895             } else {
37896                 el.setSize(size.width, size.height);
37897             }
37898             var touch = el.dom.offsetWidth;
37899             this.layout.layout();
37900             // ie requires a double layout on the first pass
37901             if(Roo.isIE && !this.initialized){
37902                 this.initialized = true;
37903                 this.layout.layout();
37904             }
37905         }
37906     },
37907     
37908     // activate all subpanels if not currently active..
37909     
37910     setActiveState : function(active){
37911         this.active = active;
37912         this.setActiveClass(active);
37913         
37914         if(!active){
37915             this.fireEvent("deactivate", this);
37916             return;
37917         }
37918         
37919         this.fireEvent("activate", this);
37920         // not sure if this should happen before or after..
37921         if (!this.layout) {
37922             return; // should not happen..
37923         }
37924         var reg = false;
37925         for (var r in this.layout.regions) {
37926             reg = this.layout.getRegion(r);
37927             if (reg.getActivePanel()) {
37928                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37929                 reg.setActivePanel(reg.getActivePanel());
37930                 continue;
37931             }
37932             if (!reg.panels.length) {
37933                 continue;
37934             }
37935             reg.showPanel(reg.getPanel(0));
37936         }
37937         
37938         
37939         
37940         
37941     },
37942     
37943     /**
37944      * Returns the nested BorderLayout for this panel
37945      * @return {Roo.BorderLayout} 
37946      */
37947     getLayout : function(){
37948         return this.layout;
37949     },
37950     
37951      /**
37952      * Adds a xtype elements to the layout of the nested panel
37953      * <pre><code>
37954
37955 panel.addxtype({
37956        xtype : 'ContentPanel',
37957        region: 'west',
37958        items: [ .... ]
37959    }
37960 );
37961
37962 panel.addxtype({
37963         xtype : 'NestedLayoutPanel',
37964         region: 'west',
37965         layout: {
37966            center: { },
37967            west: { }   
37968         },
37969         items : [ ... list of content panels or nested layout panels.. ]
37970    }
37971 );
37972 </code></pre>
37973      * @param {Object} cfg Xtype definition of item to add.
37974      */
37975     addxtype : function(cfg) {
37976         return this.layout.addxtype(cfg);
37977     
37978     }
37979 });        /*
37980  * Based on:
37981  * Ext JS Library 1.1.1
37982  * Copyright(c) 2006-2007, Ext JS, LLC.
37983  *
37984  * Originally Released Under LGPL - original licence link has changed is not relivant.
37985  *
37986  * Fork - LGPL
37987  * <script type="text/javascript">
37988  */
37989 /**
37990  * @class Roo.TabPanel
37991  * @extends Roo.util.Observable
37992  * A lightweight tab container.
37993  * <br><br>
37994  * Usage:
37995  * <pre><code>
37996 // basic tabs 1, built from existing content
37997 var tabs = new Roo.TabPanel("tabs1");
37998 tabs.addTab("script", "View Script");
37999 tabs.addTab("markup", "View Markup");
38000 tabs.activate("script");
38001
38002 // more advanced tabs, built from javascript
38003 var jtabs = new Roo.TabPanel("jtabs");
38004 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38005
38006 // set up the UpdateManager
38007 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38008 var updater = tab2.getUpdateManager();
38009 updater.setDefaultUrl("ajax1.htm");
38010 tab2.on('activate', updater.refresh, updater, true);
38011
38012 // Use setUrl for Ajax loading
38013 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38014 tab3.setUrl("ajax2.htm", null, true);
38015
38016 // Disabled tab
38017 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38018 tab4.disable();
38019
38020 jtabs.activate("jtabs-1");
38021  * </code></pre>
38022  * @constructor
38023  * Create a new TabPanel.
38024  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38025  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38026  */
38027 Roo.bootstrap.panel.Tabs = function(config){
38028     /**
38029     * The container element for this TabPanel.
38030     * @type Roo.Element
38031     */
38032     this.el = Roo.get(config.el);
38033     delete config.el;
38034     if(config){
38035         if(typeof config == "boolean"){
38036             this.tabPosition = config ? "bottom" : "top";
38037         }else{
38038             Roo.apply(this, config);
38039         }
38040     }
38041     
38042     if(this.tabPosition == "bottom"){
38043         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38044         this.el.addClass("roo-tabs-bottom");
38045     }
38046     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38047     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38048     this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38049     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38050     if(Roo.isIE){
38051         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38052     }
38053     if(this.tabPosition != "bottom"){
38054         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38055          * @type Roo.Element
38056          */
38057         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38058         this.el.addClass("roo-tabs-top");
38059     }
38060     this.items = [];
38061
38062     this.bodyEl.setStyle("position", "relative");
38063
38064     this.active = null;
38065     this.activateDelegate = this.activate.createDelegate(this);
38066
38067     this.addEvents({
38068         /**
38069          * @event tabchange
38070          * Fires when the active tab changes
38071          * @param {Roo.TabPanel} this
38072          * @param {Roo.TabPanelItem} activePanel The new active tab
38073          */
38074         "tabchange": true,
38075         /**
38076          * @event beforetabchange
38077          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38078          * @param {Roo.TabPanel} this
38079          * @param {Object} e Set cancel to true on this object to cancel the tab change
38080          * @param {Roo.TabPanelItem} tab The tab being changed to
38081          */
38082         "beforetabchange" : true
38083     });
38084
38085     Roo.EventManager.onWindowResize(this.onResize, this);
38086     this.cpad = this.el.getPadding("lr");
38087     this.hiddenCount = 0;
38088
38089
38090     // toolbar on the tabbar support...
38091     if (this.toolbar) {
38092         alert("no toolbar support yet");
38093         this.toolbar  = false;
38094         /*
38095         var tcfg = this.toolbar;
38096         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38097         this.toolbar = new Roo.Toolbar(tcfg);
38098         if (Roo.isSafari) {
38099             var tbl = tcfg.container.child('table', true);
38100             tbl.setAttribute('width', '100%');
38101         }
38102         */
38103         
38104     }
38105    
38106
38107
38108     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38109 };
38110
38111 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38112     /*
38113      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38114      */
38115     tabPosition : "top",
38116     /*
38117      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38118      */
38119     currentTabWidth : 0,
38120     /*
38121      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38122      */
38123     minTabWidth : 40,
38124     /*
38125      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38126      */
38127     maxTabWidth : 250,
38128     /*
38129      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38130      */
38131     preferredTabWidth : 175,
38132     /*
38133      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38134      */
38135     resizeTabs : false,
38136     /*
38137      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38138      */
38139     monitorResize : true,
38140     /*
38141      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38142      */
38143     toolbar : false,
38144
38145     /**
38146      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38147      * @param {String} id The id of the div to use <b>or create</b>
38148      * @param {String} text The text for the tab
38149      * @param {String} content (optional) Content to put in the TabPanelItem body
38150      * @param {Boolean} closable (optional) True to create a close icon on the tab
38151      * @return {Roo.TabPanelItem} The created TabPanelItem
38152      */
38153     addTab : function(id, text, content, closable, tpl)
38154     {
38155         var item = new Roo.bootstrap.panel.TabItem({
38156             panel: this,
38157             id : id,
38158             text : text,
38159             closable : closable,
38160             tpl : tpl
38161         });
38162         this.addTabItem(item);
38163         if(content){
38164             item.setContent(content);
38165         }
38166         return item;
38167     },
38168
38169     /**
38170      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38171      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38172      * @return {Roo.TabPanelItem}
38173      */
38174     getTab : function(id){
38175         return this.items[id];
38176     },
38177
38178     /**
38179      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38180      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38181      */
38182     hideTab : function(id){
38183         var t = this.items[id];
38184         if(!t.isHidden()){
38185            t.setHidden(true);
38186            this.hiddenCount++;
38187            this.autoSizeTabs();
38188         }
38189     },
38190
38191     /**
38192      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38193      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38194      */
38195     unhideTab : function(id){
38196         var t = this.items[id];
38197         if(t.isHidden()){
38198            t.setHidden(false);
38199            this.hiddenCount--;
38200            this.autoSizeTabs();
38201         }
38202     },
38203
38204     /**
38205      * Adds an existing {@link Roo.TabPanelItem}.
38206      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38207      */
38208     addTabItem : function(item)
38209     {
38210         this.items[item.id] = item;
38211         this.items.push(item);
38212         this.autoSizeTabs();
38213       //  if(this.resizeTabs){
38214     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38215   //         this.autoSizeTabs();
38216 //        }else{
38217 //            item.autoSize();
38218        // }
38219     },
38220
38221     /**
38222      * Removes a {@link Roo.TabPanelItem}.
38223      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38224      */
38225     removeTab : function(id){
38226         var items = this.items;
38227         var tab = items[id];
38228         if(!tab) { return; }
38229         var index = items.indexOf(tab);
38230         if(this.active == tab && items.length > 1){
38231             var newTab = this.getNextAvailable(index);
38232             if(newTab) {
38233                 newTab.activate();
38234             }
38235         }
38236         this.stripEl.dom.removeChild(tab.pnode.dom);
38237         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38238             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38239         }
38240         items.splice(index, 1);
38241         delete this.items[tab.id];
38242         tab.fireEvent("close", tab);
38243         tab.purgeListeners();
38244         this.autoSizeTabs();
38245     },
38246
38247     getNextAvailable : function(start){
38248         var items = this.items;
38249         var index = start;
38250         // look for a next tab that will slide over to
38251         // replace the one being removed
38252         while(index < items.length){
38253             var item = items[++index];
38254             if(item && !item.isHidden()){
38255                 return item;
38256             }
38257         }
38258         // if one isn't found select the previous tab (on the left)
38259         index = start;
38260         while(index >= 0){
38261             var item = items[--index];
38262             if(item && !item.isHidden()){
38263                 return item;
38264             }
38265         }
38266         return null;
38267     },
38268
38269     /**
38270      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38271      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38272      */
38273     disableTab : function(id){
38274         var tab = this.items[id];
38275         if(tab && this.active != tab){
38276             tab.disable();
38277         }
38278     },
38279
38280     /**
38281      * Enables a {@link Roo.TabPanelItem} that is disabled.
38282      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38283      */
38284     enableTab : function(id){
38285         var tab = this.items[id];
38286         tab.enable();
38287     },
38288
38289     /**
38290      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38291      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38292      * @return {Roo.TabPanelItem} The TabPanelItem.
38293      */
38294     activate : function(id)
38295     {
38296         var tab = this.items[id];
38297         if(!tab){
38298             return null;
38299         }
38300         if(tab == this.active || tab.disabled){
38301             return tab;
38302         }
38303         var e = {};
38304         this.fireEvent("beforetabchange", this, e, tab);
38305         if(e.cancel !== true && !tab.disabled){
38306             if(this.active){
38307                 this.active.hide();
38308             }
38309             this.active = this.items[id];
38310             this.active.show();
38311             this.fireEvent("tabchange", this, this.active);
38312         }
38313         return tab;
38314     },
38315
38316     /**
38317      * Gets the active {@link Roo.TabPanelItem}.
38318      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38319      */
38320     getActiveTab : function(){
38321         return this.active;
38322     },
38323
38324     /**
38325      * Updates the tab body element to fit the height of the container element
38326      * for overflow scrolling
38327      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38328      */
38329     syncHeight : function(targetHeight){
38330         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38331         var bm = this.bodyEl.getMargins();
38332         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38333         this.bodyEl.setHeight(newHeight);
38334         return newHeight;
38335     },
38336
38337     onResize : function(){
38338         if(this.monitorResize){
38339             this.autoSizeTabs();
38340         }
38341     },
38342
38343     /**
38344      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38345      */
38346     beginUpdate : function(){
38347         this.updating = true;
38348     },
38349
38350     /**
38351      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38352      */
38353     endUpdate : function(){
38354         this.updating = false;
38355         this.autoSizeTabs();
38356     },
38357
38358     /**
38359      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38360      */
38361     autoSizeTabs : function()
38362     {
38363         var count = this.items.length;
38364         var vcount = count - this.hiddenCount;
38365         
38366         if (vcount < 2) {
38367             this.stripEl.hide();
38368         } else {
38369             this.stripEl.show();
38370         }
38371         
38372         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38373             return;
38374         }
38375         
38376         
38377         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38378         var availWidth = Math.floor(w / vcount);
38379         var b = this.stripBody;
38380         if(b.getWidth() > w){
38381             var tabs = this.items;
38382             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38383             if(availWidth < this.minTabWidth){
38384                 /*if(!this.sleft){    // incomplete scrolling code
38385                     this.createScrollButtons();
38386                 }
38387                 this.showScroll();
38388                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38389             }
38390         }else{
38391             if(this.currentTabWidth < this.preferredTabWidth){
38392                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38393             }
38394         }
38395     },
38396
38397     /**
38398      * Returns the number of tabs in this TabPanel.
38399      * @return {Number}
38400      */
38401      getCount : function(){
38402          return this.items.length;
38403      },
38404
38405     /**
38406      * Resizes all the tabs to the passed width
38407      * @param {Number} The new width
38408      */
38409     setTabWidth : function(width){
38410         this.currentTabWidth = width;
38411         for(var i = 0, len = this.items.length; i < len; i++) {
38412                 if(!this.items[i].isHidden()) {
38413                 this.items[i].setWidth(width);
38414             }
38415         }
38416     },
38417
38418     /**
38419      * Destroys this TabPanel
38420      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38421      */
38422     destroy : function(removeEl){
38423         Roo.EventManager.removeResizeListener(this.onResize, this);
38424         for(var i = 0, len = this.items.length; i < len; i++){
38425             this.items[i].purgeListeners();
38426         }
38427         if(removeEl === true){
38428             this.el.update("");
38429             this.el.remove();
38430         }
38431     },
38432     
38433     createStrip : function(container)
38434     {
38435         var strip = document.createElement("nav");
38436         strip.className = Roo.bootstrap.version == 4 ?
38437             "navbar-light bg-light" : 
38438             "navbar navbar-default"; //"x-tabs-wrap";
38439         container.appendChild(strip);
38440         return strip;
38441     },
38442     
38443     createStripList : function(strip)
38444     {
38445         // div wrapper for retard IE
38446         // returns the "tr" element.
38447         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38448         //'<div class="x-tabs-strip-wrap">'+
38449           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38450           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38451         return strip.firstChild; //.firstChild.firstChild.firstChild;
38452     },
38453     createBody : function(container)
38454     {
38455         var body = document.createElement("div");
38456         Roo.id(body, "tab-body");
38457         //Roo.fly(body).addClass("x-tabs-body");
38458         Roo.fly(body).addClass("tab-content");
38459         container.appendChild(body);
38460         return body;
38461     },
38462     createItemBody :function(bodyEl, id){
38463         var body = Roo.getDom(id);
38464         if(!body){
38465             body = document.createElement("div");
38466             body.id = id;
38467         }
38468         //Roo.fly(body).addClass("x-tabs-item-body");
38469         Roo.fly(body).addClass("tab-pane");
38470          bodyEl.insertBefore(body, bodyEl.firstChild);
38471         return body;
38472     },
38473     /** @private */
38474     createStripElements :  function(stripEl, text, closable, tpl)
38475     {
38476         var td = document.createElement("li"); // was td..
38477         td.className = 'nav-item';
38478         
38479         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38480         
38481         
38482         stripEl.appendChild(td);
38483         /*if(closable){
38484             td.className = "x-tabs-closable";
38485             if(!this.closeTpl){
38486                 this.closeTpl = new Roo.Template(
38487                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38488                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38489                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38490                 );
38491             }
38492             var el = this.closeTpl.overwrite(td, {"text": text});
38493             var close = el.getElementsByTagName("div")[0];
38494             var inner = el.getElementsByTagName("em")[0];
38495             return {"el": el, "close": close, "inner": inner};
38496         } else {
38497         */
38498         // not sure what this is..
38499 //            if(!this.tabTpl){
38500                 //this.tabTpl = new Roo.Template(
38501                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38502                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38503                 //);
38504 //                this.tabTpl = new Roo.Template(
38505 //                   '<a href="#">' +
38506 //                   '<span unselectable="on"' +
38507 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38508 //                            ' >{text}</span></a>'
38509 //                );
38510 //                
38511 //            }
38512
38513
38514             var template = tpl || this.tabTpl || false;
38515             
38516             if(!template){
38517                 template =  new Roo.Template(
38518                         Roo.bootstrap.version == 4 ? 
38519                             (
38520                                 '<a class="nav-link" href="#" unselectable="on"' +
38521                                      (this.disableTooltips ? '' : ' title="{text}"') +
38522                                      ' >{text}</a>'
38523                             ) : (
38524                                 '<a class="nav-link" href="#">' +
38525                                 '<span unselectable="on"' +
38526                                          (this.disableTooltips ? '' : ' title="{text}"') +
38527                                     ' >{text}</span></a>'
38528                             )
38529                 );
38530             }
38531             
38532             switch (typeof(template)) {
38533                 case 'object' :
38534                     break;
38535                 case 'string' :
38536                     template = new Roo.Template(template);
38537                     break;
38538                 default :
38539                     break;
38540             }
38541             
38542             var el = template.overwrite(td, {"text": text});
38543             
38544             var inner = el.getElementsByTagName("span")[0];
38545             
38546             return {"el": el, "inner": inner};
38547             
38548     }
38549         
38550     
38551 });
38552
38553 /**
38554  * @class Roo.TabPanelItem
38555  * @extends Roo.util.Observable
38556  * Represents an individual item (tab plus body) in a TabPanel.
38557  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38558  * @param {String} id The id of this TabPanelItem
38559  * @param {String} text The text for the tab of this TabPanelItem
38560  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38561  */
38562 Roo.bootstrap.panel.TabItem = function(config){
38563     /**
38564      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38565      * @type Roo.TabPanel
38566      */
38567     this.tabPanel = config.panel;
38568     /**
38569      * The id for this TabPanelItem
38570      * @type String
38571      */
38572     this.id = config.id;
38573     /** @private */
38574     this.disabled = false;
38575     /** @private */
38576     this.text = config.text;
38577     /** @private */
38578     this.loaded = false;
38579     this.closable = config.closable;
38580
38581     /**
38582      * The body element for this TabPanelItem.
38583      * @type Roo.Element
38584      */
38585     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38586     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38587     this.bodyEl.setStyle("display", "block");
38588     this.bodyEl.setStyle("zoom", "1");
38589     //this.hideAction();
38590
38591     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38592     /** @private */
38593     this.el = Roo.get(els.el);
38594     this.inner = Roo.get(els.inner, true);
38595      this.textEl = Roo.bootstrap.version == 4 ?
38596         this.el : Roo.get(this.el.dom.firstChild, true);
38597
38598     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38599     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38600
38601     
38602 //    this.el.on("mousedown", this.onTabMouseDown, this);
38603     this.el.on("click", this.onTabClick, this);
38604     /** @private */
38605     if(config.closable){
38606         var c = Roo.get(els.close, true);
38607         c.dom.title = this.closeText;
38608         c.addClassOnOver("close-over");
38609         c.on("click", this.closeClick, this);
38610      }
38611
38612     this.addEvents({
38613          /**
38614          * @event activate
38615          * Fires when this tab becomes the active tab.
38616          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38617          * @param {Roo.TabPanelItem} this
38618          */
38619         "activate": true,
38620         /**
38621          * @event beforeclose
38622          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38623          * @param {Roo.TabPanelItem} this
38624          * @param {Object} e Set cancel to true on this object to cancel the close.
38625          */
38626         "beforeclose": true,
38627         /**
38628          * @event close
38629          * Fires when this tab is closed.
38630          * @param {Roo.TabPanelItem} this
38631          */
38632          "close": true,
38633         /**
38634          * @event deactivate
38635          * Fires when this tab is no longer the active tab.
38636          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38637          * @param {Roo.TabPanelItem} this
38638          */
38639          "deactivate" : true
38640     });
38641     this.hidden = false;
38642
38643     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38644 };
38645
38646 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38647            {
38648     purgeListeners : function(){
38649        Roo.util.Observable.prototype.purgeListeners.call(this);
38650        this.el.removeAllListeners();
38651     },
38652     /**
38653      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38654      */
38655     show : function(){
38656         this.status_node.addClass("active");
38657         this.showAction();
38658         if(Roo.isOpera){
38659             this.tabPanel.stripWrap.repaint();
38660         }
38661         this.fireEvent("activate", this.tabPanel, this);
38662     },
38663
38664     /**
38665      * Returns true if this tab is the active tab.
38666      * @return {Boolean}
38667      */
38668     isActive : function(){
38669         return this.tabPanel.getActiveTab() == this;
38670     },
38671
38672     /**
38673      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38674      */
38675     hide : function(){
38676         this.status_node.removeClass("active");
38677         this.hideAction();
38678         this.fireEvent("deactivate", this.tabPanel, this);
38679     },
38680
38681     hideAction : function(){
38682         this.bodyEl.hide();
38683         this.bodyEl.setStyle("position", "absolute");
38684         this.bodyEl.setLeft("-20000px");
38685         this.bodyEl.setTop("-20000px");
38686     },
38687
38688     showAction : function(){
38689         this.bodyEl.setStyle("position", "relative");
38690         this.bodyEl.setTop("");
38691         this.bodyEl.setLeft("");
38692         this.bodyEl.show();
38693     },
38694
38695     /**
38696      * Set the tooltip for the tab.
38697      * @param {String} tooltip The tab's tooltip
38698      */
38699     setTooltip : function(text){
38700         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38701             this.textEl.dom.qtip = text;
38702             this.textEl.dom.removeAttribute('title');
38703         }else{
38704             this.textEl.dom.title = text;
38705         }
38706     },
38707
38708     onTabClick : function(e){
38709         e.preventDefault();
38710         this.tabPanel.activate(this.id);
38711     },
38712
38713     onTabMouseDown : function(e){
38714         e.preventDefault();
38715         this.tabPanel.activate(this.id);
38716     },
38717 /*
38718     getWidth : function(){
38719         return this.inner.getWidth();
38720     },
38721
38722     setWidth : function(width){
38723         var iwidth = width - this.linode.getPadding("lr");
38724         this.inner.setWidth(iwidth);
38725         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38726         this.linode.setWidth(width);
38727     },
38728 */
38729     /**
38730      * Show or hide the tab
38731      * @param {Boolean} hidden True to hide or false to show.
38732      */
38733     setHidden : function(hidden){
38734         this.hidden = hidden;
38735         this.linode.setStyle("display", hidden ? "none" : "");
38736     },
38737
38738     /**
38739      * Returns true if this tab is "hidden"
38740      * @return {Boolean}
38741      */
38742     isHidden : function(){
38743         return this.hidden;
38744     },
38745
38746     /**
38747      * Returns the text for this tab
38748      * @return {String}
38749      */
38750     getText : function(){
38751         return this.text;
38752     },
38753     /*
38754     autoSize : function(){
38755         //this.el.beginMeasure();
38756         this.textEl.setWidth(1);
38757         /*
38758          *  #2804 [new] Tabs in Roojs
38759          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38760          */
38761         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38762         //this.el.endMeasure();
38763     //},
38764
38765     /**
38766      * Sets the text for the tab (Note: this also sets the tooltip text)
38767      * @param {String} text The tab's text and tooltip
38768      */
38769     setText : function(text){
38770         this.text = text;
38771         this.textEl.update(text);
38772         this.setTooltip(text);
38773         //if(!this.tabPanel.resizeTabs){
38774         //    this.autoSize();
38775         //}
38776     },
38777     /**
38778      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38779      */
38780     activate : function(){
38781         this.tabPanel.activate(this.id);
38782     },
38783
38784     /**
38785      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38786      */
38787     disable : function(){
38788         if(this.tabPanel.active != this){
38789             this.disabled = true;
38790             this.status_node.addClass("disabled");
38791         }
38792     },
38793
38794     /**
38795      * Enables this TabPanelItem if it was previously disabled.
38796      */
38797     enable : function(){
38798         this.disabled = false;
38799         this.status_node.removeClass("disabled");
38800     },
38801
38802     /**
38803      * Sets the content for this TabPanelItem.
38804      * @param {String} content The content
38805      * @param {Boolean} loadScripts true to look for and load scripts
38806      */
38807     setContent : function(content, loadScripts){
38808         this.bodyEl.update(content, loadScripts);
38809     },
38810
38811     /**
38812      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38813      * @return {Roo.UpdateManager} The UpdateManager
38814      */
38815     getUpdateManager : function(){
38816         return this.bodyEl.getUpdateManager();
38817     },
38818
38819     /**
38820      * Set a URL to be used to load the content for this TabPanelItem.
38821      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38822      * @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)
38823      * @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)
38824      * @return {Roo.UpdateManager} The UpdateManager
38825      */
38826     setUrl : function(url, params, loadOnce){
38827         if(this.refreshDelegate){
38828             this.un('activate', this.refreshDelegate);
38829         }
38830         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38831         this.on("activate", this.refreshDelegate);
38832         return this.bodyEl.getUpdateManager();
38833     },
38834
38835     /** @private */
38836     _handleRefresh : function(url, params, loadOnce){
38837         if(!loadOnce || !this.loaded){
38838             var updater = this.bodyEl.getUpdateManager();
38839             updater.update(url, params, this._setLoaded.createDelegate(this));
38840         }
38841     },
38842
38843     /**
38844      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38845      *   Will fail silently if the setUrl method has not been called.
38846      *   This does not activate the panel, just updates its content.
38847      */
38848     refresh : function(){
38849         if(this.refreshDelegate){
38850            this.loaded = false;
38851            this.refreshDelegate();
38852         }
38853     },
38854
38855     /** @private */
38856     _setLoaded : function(){
38857         this.loaded = true;
38858     },
38859
38860     /** @private */
38861     closeClick : function(e){
38862         var o = {};
38863         e.stopEvent();
38864         this.fireEvent("beforeclose", this, o);
38865         if(o.cancel !== true){
38866             this.tabPanel.removeTab(this.id);
38867         }
38868     },
38869     /**
38870      * The text displayed in the tooltip for the close icon.
38871      * @type String
38872      */
38873     closeText : "Close this tab"
38874 });
38875 /**
38876 *    This script refer to:
38877 *    Title: International Telephone Input
38878 *    Author: Jack O'Connor
38879 *    Code version:  v12.1.12
38880 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38881 **/
38882
38883 Roo.bootstrap.PhoneInputData = function() {
38884     var d = [
38885       [
38886         "Afghanistan (‫افغانستان‬‎)",
38887         "af",
38888         "93"
38889       ],
38890       [
38891         "Albania (Shqipëri)",
38892         "al",
38893         "355"
38894       ],
38895       [
38896         "Algeria (‫الجزائر‬‎)",
38897         "dz",
38898         "213"
38899       ],
38900       [
38901         "American Samoa",
38902         "as",
38903         "1684"
38904       ],
38905       [
38906         "Andorra",
38907         "ad",
38908         "376"
38909       ],
38910       [
38911         "Angola",
38912         "ao",
38913         "244"
38914       ],
38915       [
38916         "Anguilla",
38917         "ai",
38918         "1264"
38919       ],
38920       [
38921         "Antigua and Barbuda",
38922         "ag",
38923         "1268"
38924       ],
38925       [
38926         "Argentina",
38927         "ar",
38928         "54"
38929       ],
38930       [
38931         "Armenia (Հայաստան)",
38932         "am",
38933         "374"
38934       ],
38935       [
38936         "Aruba",
38937         "aw",
38938         "297"
38939       ],
38940       [
38941         "Australia",
38942         "au",
38943         "61",
38944         0
38945       ],
38946       [
38947         "Austria (Österreich)",
38948         "at",
38949         "43"
38950       ],
38951       [
38952         "Azerbaijan (Azərbaycan)",
38953         "az",
38954         "994"
38955       ],
38956       [
38957         "Bahamas",
38958         "bs",
38959         "1242"
38960       ],
38961       [
38962         "Bahrain (‫البحرين‬‎)",
38963         "bh",
38964         "973"
38965       ],
38966       [
38967         "Bangladesh (বাংলাদেশ)",
38968         "bd",
38969         "880"
38970       ],
38971       [
38972         "Barbados",
38973         "bb",
38974         "1246"
38975       ],
38976       [
38977         "Belarus (Беларусь)",
38978         "by",
38979         "375"
38980       ],
38981       [
38982         "Belgium (België)",
38983         "be",
38984         "32"
38985       ],
38986       [
38987         "Belize",
38988         "bz",
38989         "501"
38990       ],
38991       [
38992         "Benin (Bénin)",
38993         "bj",
38994         "229"
38995       ],
38996       [
38997         "Bermuda",
38998         "bm",
38999         "1441"
39000       ],
39001       [
39002         "Bhutan (འབྲུག)",
39003         "bt",
39004         "975"
39005       ],
39006       [
39007         "Bolivia",
39008         "bo",
39009         "591"
39010       ],
39011       [
39012         "Bosnia and Herzegovina (Босна и Херцеговина)",
39013         "ba",
39014         "387"
39015       ],
39016       [
39017         "Botswana",
39018         "bw",
39019         "267"
39020       ],
39021       [
39022         "Brazil (Brasil)",
39023         "br",
39024         "55"
39025       ],
39026       [
39027         "British Indian Ocean Territory",
39028         "io",
39029         "246"
39030       ],
39031       [
39032         "British Virgin Islands",
39033         "vg",
39034         "1284"
39035       ],
39036       [
39037         "Brunei",
39038         "bn",
39039         "673"
39040       ],
39041       [
39042         "Bulgaria (България)",
39043         "bg",
39044         "359"
39045       ],
39046       [
39047         "Burkina Faso",
39048         "bf",
39049         "226"
39050       ],
39051       [
39052         "Burundi (Uburundi)",
39053         "bi",
39054         "257"
39055       ],
39056       [
39057         "Cambodia (កម្ពុជា)",
39058         "kh",
39059         "855"
39060       ],
39061       [
39062         "Cameroon (Cameroun)",
39063         "cm",
39064         "237"
39065       ],
39066       [
39067         "Canada",
39068         "ca",
39069         "1",
39070         1,
39071         ["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"]
39072       ],
39073       [
39074         "Cape Verde (Kabu Verdi)",
39075         "cv",
39076         "238"
39077       ],
39078       [
39079         "Caribbean Netherlands",
39080         "bq",
39081         "599",
39082         1
39083       ],
39084       [
39085         "Cayman Islands",
39086         "ky",
39087         "1345"
39088       ],
39089       [
39090         "Central African Republic (République centrafricaine)",
39091         "cf",
39092         "236"
39093       ],
39094       [
39095         "Chad (Tchad)",
39096         "td",
39097         "235"
39098       ],
39099       [
39100         "Chile",
39101         "cl",
39102         "56"
39103       ],
39104       [
39105         "China (中国)",
39106         "cn",
39107         "86"
39108       ],
39109       [
39110         "Christmas Island",
39111         "cx",
39112         "61",
39113         2
39114       ],
39115       [
39116         "Cocos (Keeling) Islands",
39117         "cc",
39118         "61",
39119         1
39120       ],
39121       [
39122         "Colombia",
39123         "co",
39124         "57"
39125       ],
39126       [
39127         "Comoros (‫جزر القمر‬‎)",
39128         "km",
39129         "269"
39130       ],
39131       [
39132         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39133         "cd",
39134         "243"
39135       ],
39136       [
39137         "Congo (Republic) (Congo-Brazzaville)",
39138         "cg",
39139         "242"
39140       ],
39141       [
39142         "Cook Islands",
39143         "ck",
39144         "682"
39145       ],
39146       [
39147         "Costa Rica",
39148         "cr",
39149         "506"
39150       ],
39151       [
39152         "Côte d’Ivoire",
39153         "ci",
39154         "225"
39155       ],
39156       [
39157         "Croatia (Hrvatska)",
39158         "hr",
39159         "385"
39160       ],
39161       [
39162         "Cuba",
39163         "cu",
39164         "53"
39165       ],
39166       [
39167         "Curaçao",
39168         "cw",
39169         "599",
39170         0
39171       ],
39172       [
39173         "Cyprus (Κύπρος)",
39174         "cy",
39175         "357"
39176       ],
39177       [
39178         "Czech Republic (Česká republika)",
39179         "cz",
39180         "420"
39181       ],
39182       [
39183         "Denmark (Danmark)",
39184         "dk",
39185         "45"
39186       ],
39187       [
39188         "Djibouti",
39189         "dj",
39190         "253"
39191       ],
39192       [
39193         "Dominica",
39194         "dm",
39195         "1767"
39196       ],
39197       [
39198         "Dominican Republic (República Dominicana)",
39199         "do",
39200         "1",
39201         2,
39202         ["809", "829", "849"]
39203       ],
39204       [
39205         "Ecuador",
39206         "ec",
39207         "593"
39208       ],
39209       [
39210         "Egypt (‫مصر‬‎)",
39211         "eg",
39212         "20"
39213       ],
39214       [
39215         "El Salvador",
39216         "sv",
39217         "503"
39218       ],
39219       [
39220         "Equatorial Guinea (Guinea Ecuatorial)",
39221         "gq",
39222         "240"
39223       ],
39224       [
39225         "Eritrea",
39226         "er",
39227         "291"
39228       ],
39229       [
39230         "Estonia (Eesti)",
39231         "ee",
39232         "372"
39233       ],
39234       [
39235         "Ethiopia",
39236         "et",
39237         "251"
39238       ],
39239       [
39240         "Falkland Islands (Islas Malvinas)",
39241         "fk",
39242         "500"
39243       ],
39244       [
39245         "Faroe Islands (Føroyar)",
39246         "fo",
39247         "298"
39248       ],
39249       [
39250         "Fiji",
39251         "fj",
39252         "679"
39253       ],
39254       [
39255         "Finland (Suomi)",
39256         "fi",
39257         "358",
39258         0
39259       ],
39260       [
39261         "France",
39262         "fr",
39263         "33"
39264       ],
39265       [
39266         "French Guiana (Guyane française)",
39267         "gf",
39268         "594"
39269       ],
39270       [
39271         "French Polynesia (Polynésie française)",
39272         "pf",
39273         "689"
39274       ],
39275       [
39276         "Gabon",
39277         "ga",
39278         "241"
39279       ],
39280       [
39281         "Gambia",
39282         "gm",
39283         "220"
39284       ],
39285       [
39286         "Georgia (საქართველო)",
39287         "ge",
39288         "995"
39289       ],
39290       [
39291         "Germany (Deutschland)",
39292         "de",
39293         "49"
39294       ],
39295       [
39296         "Ghana (Gaana)",
39297         "gh",
39298         "233"
39299       ],
39300       [
39301         "Gibraltar",
39302         "gi",
39303         "350"
39304       ],
39305       [
39306         "Greece (Ελλάδα)",
39307         "gr",
39308         "30"
39309       ],
39310       [
39311         "Greenland (Kalaallit Nunaat)",
39312         "gl",
39313         "299"
39314       ],
39315       [
39316         "Grenada",
39317         "gd",
39318         "1473"
39319       ],
39320       [
39321         "Guadeloupe",
39322         "gp",
39323         "590",
39324         0
39325       ],
39326       [
39327         "Guam",
39328         "gu",
39329         "1671"
39330       ],
39331       [
39332         "Guatemala",
39333         "gt",
39334         "502"
39335       ],
39336       [
39337         "Guernsey",
39338         "gg",
39339         "44",
39340         1
39341       ],
39342       [
39343         "Guinea (Guinée)",
39344         "gn",
39345         "224"
39346       ],
39347       [
39348         "Guinea-Bissau (Guiné Bissau)",
39349         "gw",
39350         "245"
39351       ],
39352       [
39353         "Guyana",
39354         "gy",
39355         "592"
39356       ],
39357       [
39358         "Haiti",
39359         "ht",
39360         "509"
39361       ],
39362       [
39363         "Honduras",
39364         "hn",
39365         "504"
39366       ],
39367       [
39368         "Hong Kong (香港)",
39369         "hk",
39370         "852"
39371       ],
39372       [
39373         "Hungary (Magyarország)",
39374         "hu",
39375         "36"
39376       ],
39377       [
39378         "Iceland (Ísland)",
39379         "is",
39380         "354"
39381       ],
39382       [
39383         "India (भारत)",
39384         "in",
39385         "91"
39386       ],
39387       [
39388         "Indonesia",
39389         "id",
39390         "62"
39391       ],
39392       [
39393         "Iran (‫ایران‬‎)",
39394         "ir",
39395         "98"
39396       ],
39397       [
39398         "Iraq (‫العراق‬‎)",
39399         "iq",
39400         "964"
39401       ],
39402       [
39403         "Ireland",
39404         "ie",
39405         "353"
39406       ],
39407       [
39408         "Isle of Man",
39409         "im",
39410         "44",
39411         2
39412       ],
39413       [
39414         "Israel (‫ישראל‬‎)",
39415         "il",
39416         "972"
39417       ],
39418       [
39419         "Italy (Italia)",
39420         "it",
39421         "39",
39422         0
39423       ],
39424       [
39425         "Jamaica",
39426         "jm",
39427         "1876"
39428       ],
39429       [
39430         "Japan (日本)",
39431         "jp",
39432         "81"
39433       ],
39434       [
39435         "Jersey",
39436         "je",
39437         "44",
39438         3
39439       ],
39440       [
39441         "Jordan (‫الأردن‬‎)",
39442         "jo",
39443         "962"
39444       ],
39445       [
39446         "Kazakhstan (Казахстан)",
39447         "kz",
39448         "7",
39449         1
39450       ],
39451       [
39452         "Kenya",
39453         "ke",
39454         "254"
39455       ],
39456       [
39457         "Kiribati",
39458         "ki",
39459         "686"
39460       ],
39461       [
39462         "Kosovo",
39463         "xk",
39464         "383"
39465       ],
39466       [
39467         "Kuwait (‫الكويت‬‎)",
39468         "kw",
39469         "965"
39470       ],
39471       [
39472         "Kyrgyzstan (Кыргызстан)",
39473         "kg",
39474         "996"
39475       ],
39476       [
39477         "Laos (ລາວ)",
39478         "la",
39479         "856"
39480       ],
39481       [
39482         "Latvia (Latvija)",
39483         "lv",
39484         "371"
39485       ],
39486       [
39487         "Lebanon (‫لبنان‬‎)",
39488         "lb",
39489         "961"
39490       ],
39491       [
39492         "Lesotho",
39493         "ls",
39494         "266"
39495       ],
39496       [
39497         "Liberia",
39498         "lr",
39499         "231"
39500       ],
39501       [
39502         "Libya (‫ليبيا‬‎)",
39503         "ly",
39504         "218"
39505       ],
39506       [
39507         "Liechtenstein",
39508         "li",
39509         "423"
39510       ],
39511       [
39512         "Lithuania (Lietuva)",
39513         "lt",
39514         "370"
39515       ],
39516       [
39517         "Luxembourg",
39518         "lu",
39519         "352"
39520       ],
39521       [
39522         "Macau (澳門)",
39523         "mo",
39524         "853"
39525       ],
39526       [
39527         "Macedonia (FYROM) (Македонија)",
39528         "mk",
39529         "389"
39530       ],
39531       [
39532         "Madagascar (Madagasikara)",
39533         "mg",
39534         "261"
39535       ],
39536       [
39537         "Malawi",
39538         "mw",
39539         "265"
39540       ],
39541       [
39542         "Malaysia",
39543         "my",
39544         "60"
39545       ],
39546       [
39547         "Maldives",
39548         "mv",
39549         "960"
39550       ],
39551       [
39552         "Mali",
39553         "ml",
39554         "223"
39555       ],
39556       [
39557         "Malta",
39558         "mt",
39559         "356"
39560       ],
39561       [
39562         "Marshall Islands",
39563         "mh",
39564         "692"
39565       ],
39566       [
39567         "Martinique",
39568         "mq",
39569         "596"
39570       ],
39571       [
39572         "Mauritania (‫موريتانيا‬‎)",
39573         "mr",
39574         "222"
39575       ],
39576       [
39577         "Mauritius (Moris)",
39578         "mu",
39579         "230"
39580       ],
39581       [
39582         "Mayotte",
39583         "yt",
39584         "262",
39585         1
39586       ],
39587       [
39588         "Mexico (México)",
39589         "mx",
39590         "52"
39591       ],
39592       [
39593         "Micronesia",
39594         "fm",
39595         "691"
39596       ],
39597       [
39598         "Moldova (Republica Moldova)",
39599         "md",
39600         "373"
39601       ],
39602       [
39603         "Monaco",
39604         "mc",
39605         "377"
39606       ],
39607       [
39608         "Mongolia (Монгол)",
39609         "mn",
39610         "976"
39611       ],
39612       [
39613         "Montenegro (Crna Gora)",
39614         "me",
39615         "382"
39616       ],
39617       [
39618         "Montserrat",
39619         "ms",
39620         "1664"
39621       ],
39622       [
39623         "Morocco (‫المغرب‬‎)",
39624         "ma",
39625         "212",
39626         0
39627       ],
39628       [
39629         "Mozambique (Moçambique)",
39630         "mz",
39631         "258"
39632       ],
39633       [
39634         "Myanmar (Burma) (မြန်မာ)",
39635         "mm",
39636         "95"
39637       ],
39638       [
39639         "Namibia (Namibië)",
39640         "na",
39641         "264"
39642       ],
39643       [
39644         "Nauru",
39645         "nr",
39646         "674"
39647       ],
39648       [
39649         "Nepal (नेपाल)",
39650         "np",
39651         "977"
39652       ],
39653       [
39654         "Netherlands (Nederland)",
39655         "nl",
39656         "31"
39657       ],
39658       [
39659         "New Caledonia (Nouvelle-Calédonie)",
39660         "nc",
39661         "687"
39662       ],
39663       [
39664         "New Zealand",
39665         "nz",
39666         "64"
39667       ],
39668       [
39669         "Nicaragua",
39670         "ni",
39671         "505"
39672       ],
39673       [
39674         "Niger (Nijar)",
39675         "ne",
39676         "227"
39677       ],
39678       [
39679         "Nigeria",
39680         "ng",
39681         "234"
39682       ],
39683       [
39684         "Niue",
39685         "nu",
39686         "683"
39687       ],
39688       [
39689         "Norfolk Island",
39690         "nf",
39691         "672"
39692       ],
39693       [
39694         "North Korea (조선 민주주의 인민 공화국)",
39695         "kp",
39696         "850"
39697       ],
39698       [
39699         "Northern Mariana Islands",
39700         "mp",
39701         "1670"
39702       ],
39703       [
39704         "Norway (Norge)",
39705         "no",
39706         "47",
39707         0
39708       ],
39709       [
39710         "Oman (‫عُمان‬‎)",
39711         "om",
39712         "968"
39713       ],
39714       [
39715         "Pakistan (‫پاکستان‬‎)",
39716         "pk",
39717         "92"
39718       ],
39719       [
39720         "Palau",
39721         "pw",
39722         "680"
39723       ],
39724       [
39725         "Palestine (‫فلسطين‬‎)",
39726         "ps",
39727         "970"
39728       ],
39729       [
39730         "Panama (Panamá)",
39731         "pa",
39732         "507"
39733       ],
39734       [
39735         "Papua New Guinea",
39736         "pg",
39737         "675"
39738       ],
39739       [
39740         "Paraguay",
39741         "py",
39742         "595"
39743       ],
39744       [
39745         "Peru (Perú)",
39746         "pe",
39747         "51"
39748       ],
39749       [
39750         "Philippines",
39751         "ph",
39752         "63"
39753       ],
39754       [
39755         "Poland (Polska)",
39756         "pl",
39757         "48"
39758       ],
39759       [
39760         "Portugal",
39761         "pt",
39762         "351"
39763       ],
39764       [
39765         "Puerto Rico",
39766         "pr",
39767         "1",
39768         3,
39769         ["787", "939"]
39770       ],
39771       [
39772         "Qatar (‫قطر‬‎)",
39773         "qa",
39774         "974"
39775       ],
39776       [
39777         "Réunion (La Réunion)",
39778         "re",
39779         "262",
39780         0
39781       ],
39782       [
39783         "Romania (România)",
39784         "ro",
39785         "40"
39786       ],
39787       [
39788         "Russia (Россия)",
39789         "ru",
39790         "7",
39791         0
39792       ],
39793       [
39794         "Rwanda",
39795         "rw",
39796         "250"
39797       ],
39798       [
39799         "Saint Barthélemy",
39800         "bl",
39801         "590",
39802         1
39803       ],
39804       [
39805         "Saint Helena",
39806         "sh",
39807         "290"
39808       ],
39809       [
39810         "Saint Kitts and Nevis",
39811         "kn",
39812         "1869"
39813       ],
39814       [
39815         "Saint Lucia",
39816         "lc",
39817         "1758"
39818       ],
39819       [
39820         "Saint Martin (Saint-Martin (partie française))",
39821         "mf",
39822         "590",
39823         2
39824       ],
39825       [
39826         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39827         "pm",
39828         "508"
39829       ],
39830       [
39831         "Saint Vincent and the Grenadines",
39832         "vc",
39833         "1784"
39834       ],
39835       [
39836         "Samoa",
39837         "ws",
39838         "685"
39839       ],
39840       [
39841         "San Marino",
39842         "sm",
39843         "378"
39844       ],
39845       [
39846         "São Tomé and Príncipe (São Tomé e Príncipe)",
39847         "st",
39848         "239"
39849       ],
39850       [
39851         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39852         "sa",
39853         "966"
39854       ],
39855       [
39856         "Senegal (Sénégal)",
39857         "sn",
39858         "221"
39859       ],
39860       [
39861         "Serbia (Србија)",
39862         "rs",
39863         "381"
39864       ],
39865       [
39866         "Seychelles",
39867         "sc",
39868         "248"
39869       ],
39870       [
39871         "Sierra Leone",
39872         "sl",
39873         "232"
39874       ],
39875       [
39876         "Singapore",
39877         "sg",
39878         "65"
39879       ],
39880       [
39881         "Sint Maarten",
39882         "sx",
39883         "1721"
39884       ],
39885       [
39886         "Slovakia (Slovensko)",
39887         "sk",
39888         "421"
39889       ],
39890       [
39891         "Slovenia (Slovenija)",
39892         "si",
39893         "386"
39894       ],
39895       [
39896         "Solomon Islands",
39897         "sb",
39898         "677"
39899       ],
39900       [
39901         "Somalia (Soomaaliya)",
39902         "so",
39903         "252"
39904       ],
39905       [
39906         "South Africa",
39907         "za",
39908         "27"
39909       ],
39910       [
39911         "South Korea (대한민국)",
39912         "kr",
39913         "82"
39914       ],
39915       [
39916         "South Sudan (‫جنوب السودان‬‎)",
39917         "ss",
39918         "211"
39919       ],
39920       [
39921         "Spain (España)",
39922         "es",
39923         "34"
39924       ],
39925       [
39926         "Sri Lanka (ශ්‍රී ලංකාව)",
39927         "lk",
39928         "94"
39929       ],
39930       [
39931         "Sudan (‫السودان‬‎)",
39932         "sd",
39933         "249"
39934       ],
39935       [
39936         "Suriname",
39937         "sr",
39938         "597"
39939       ],
39940       [
39941         "Svalbard and Jan Mayen",
39942         "sj",
39943         "47",
39944         1
39945       ],
39946       [
39947         "Swaziland",
39948         "sz",
39949         "268"
39950       ],
39951       [
39952         "Sweden (Sverige)",
39953         "se",
39954         "46"
39955       ],
39956       [
39957         "Switzerland (Schweiz)",
39958         "ch",
39959         "41"
39960       ],
39961       [
39962         "Syria (‫سوريا‬‎)",
39963         "sy",
39964         "963"
39965       ],
39966       [
39967         "Taiwan (台灣)",
39968         "tw",
39969         "886"
39970       ],
39971       [
39972         "Tajikistan",
39973         "tj",
39974         "992"
39975       ],
39976       [
39977         "Tanzania",
39978         "tz",
39979         "255"
39980       ],
39981       [
39982         "Thailand (ไทย)",
39983         "th",
39984         "66"
39985       ],
39986       [
39987         "Timor-Leste",
39988         "tl",
39989         "670"
39990       ],
39991       [
39992         "Togo",
39993         "tg",
39994         "228"
39995       ],
39996       [
39997         "Tokelau",
39998         "tk",
39999         "690"
40000       ],
40001       [
40002         "Tonga",
40003         "to",
40004         "676"
40005       ],
40006       [
40007         "Trinidad and Tobago",
40008         "tt",
40009         "1868"
40010       ],
40011       [
40012         "Tunisia (‫تونس‬‎)",
40013         "tn",
40014         "216"
40015       ],
40016       [
40017         "Turkey (Türkiye)",
40018         "tr",
40019         "90"
40020       ],
40021       [
40022         "Turkmenistan",
40023         "tm",
40024         "993"
40025       ],
40026       [
40027         "Turks and Caicos Islands",
40028         "tc",
40029         "1649"
40030       ],
40031       [
40032         "Tuvalu",
40033         "tv",
40034         "688"
40035       ],
40036       [
40037         "U.S. Virgin Islands",
40038         "vi",
40039         "1340"
40040       ],
40041       [
40042         "Uganda",
40043         "ug",
40044         "256"
40045       ],
40046       [
40047         "Ukraine (Україна)",
40048         "ua",
40049         "380"
40050       ],
40051       [
40052         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
40053         "ae",
40054         "971"
40055       ],
40056       [
40057         "United Kingdom",
40058         "gb",
40059         "44",
40060         0
40061       ],
40062       [
40063         "United States",
40064         "us",
40065         "1",
40066         0
40067       ],
40068       [
40069         "Uruguay",
40070         "uy",
40071         "598"
40072       ],
40073       [
40074         "Uzbekistan (Oʻzbekiston)",
40075         "uz",
40076         "998"
40077       ],
40078       [
40079         "Vanuatu",
40080         "vu",
40081         "678"
40082       ],
40083       [
40084         "Vatican City (Città del Vaticano)",
40085         "va",
40086         "39",
40087         1
40088       ],
40089       [
40090         "Venezuela",
40091         "ve",
40092         "58"
40093       ],
40094       [
40095         "Vietnam (Việt Nam)",
40096         "vn",
40097         "84"
40098       ],
40099       [
40100         "Wallis and Futuna (Wallis-et-Futuna)",
40101         "wf",
40102         "681"
40103       ],
40104       [
40105         "Western Sahara (‫الصحراء الغربية‬‎)",
40106         "eh",
40107         "212",
40108         1
40109       ],
40110       [
40111         "Yemen (‫اليمن‬‎)",
40112         "ye",
40113         "967"
40114       ],
40115       [
40116         "Zambia",
40117         "zm",
40118         "260"
40119       ],
40120       [
40121         "Zimbabwe",
40122         "zw",
40123         "263"
40124       ],
40125       [
40126         "Åland Islands",
40127         "ax",
40128         "358",
40129         1
40130       ]
40131   ];
40132   
40133   return d;
40134 }/**
40135 *    This script refer to:
40136 *    Title: International Telephone Input
40137 *    Author: Jack O'Connor
40138 *    Code version:  v12.1.12
40139 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40140 **/
40141
40142 /**
40143  * @class Roo.bootstrap.PhoneInput
40144  * @extends Roo.bootstrap.TriggerField
40145  * An input with International dial-code selection
40146  
40147  * @cfg {String} defaultDialCode default '+852'
40148  * @cfg {Array} preferedCountries default []
40149   
40150  * @constructor
40151  * Create a new PhoneInput.
40152  * @param {Object} config Configuration options
40153  */
40154
40155 Roo.bootstrap.PhoneInput = function(config) {
40156     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40157 };
40158
40159 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40160         
40161         listWidth: undefined,
40162         
40163         selectedClass: 'active',
40164         
40165         invalidClass : "has-warning",
40166         
40167         validClass: 'has-success',
40168         
40169         allowed: '0123456789',
40170         
40171         max_length: 15,
40172         
40173         /**
40174          * @cfg {String} defaultDialCode The default dial code when initializing the input
40175          */
40176         defaultDialCode: '+852',
40177         
40178         /**
40179          * @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
40180          */
40181         preferedCountries: false,
40182         
40183         getAutoCreate : function()
40184         {
40185             var data = Roo.bootstrap.PhoneInputData();
40186             var align = this.labelAlign || this.parentLabelAlign();
40187             var id = Roo.id();
40188             
40189             this.allCountries = [];
40190             this.dialCodeMapping = [];
40191             
40192             for (var i = 0; i < data.length; i++) {
40193               var c = data[i];
40194               this.allCountries[i] = {
40195                 name: c[0],
40196                 iso2: c[1],
40197                 dialCode: c[2],
40198                 priority: c[3] || 0,
40199                 areaCodes: c[4] || null
40200               };
40201               this.dialCodeMapping[c[2]] = {
40202                   name: c[0],
40203                   iso2: c[1],
40204                   priority: c[3] || 0,
40205                   areaCodes: c[4] || null
40206               };
40207             }
40208             
40209             var cfg = {
40210                 cls: 'form-group',
40211                 cn: []
40212             };
40213             
40214             var input =  {
40215                 tag: 'input',
40216                 id : id,
40217                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40218                 maxlength: this.max_length,
40219                 cls : 'form-control tel-input',
40220                 autocomplete: 'new-password'
40221             };
40222             
40223             var hiddenInput = {
40224                 tag: 'input',
40225                 type: 'hidden',
40226                 cls: 'hidden-tel-input'
40227             };
40228             
40229             if (this.name) {
40230                 hiddenInput.name = this.name;
40231             }
40232             
40233             if (this.disabled) {
40234                 input.disabled = true;
40235             }
40236             
40237             var flag_container = {
40238                 tag: 'div',
40239                 cls: 'flag-box',
40240                 cn: [
40241                     {
40242                         tag: 'div',
40243                         cls: 'flag'
40244                     },
40245                     {
40246                         tag: 'div',
40247                         cls: 'caret'
40248                     }
40249                 ]
40250             };
40251             
40252             var box = {
40253                 tag: 'div',
40254                 cls: this.hasFeedback ? 'has-feedback' : '',
40255                 cn: [
40256                     hiddenInput,
40257                     input,
40258                     {
40259                         tag: 'input',
40260                         cls: 'dial-code-holder',
40261                         disabled: true
40262                     }
40263                 ]
40264             };
40265             
40266             var container = {
40267                 cls: 'roo-select2-container input-group',
40268                 cn: [
40269                     flag_container,
40270                     box
40271                 ]
40272             };
40273             
40274             if (this.fieldLabel.length) {
40275                 var indicator = {
40276                     tag: 'i',
40277                     tooltip: 'This field is required'
40278                 };
40279                 
40280                 var label = {
40281                     tag: 'label',
40282                     'for':  id,
40283                     cls: 'control-label',
40284                     cn: []
40285                 };
40286                 
40287                 var label_text = {
40288                     tag: 'span',
40289                     html: this.fieldLabel
40290                 };
40291                 
40292                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40293                 label.cn = [
40294                     indicator,
40295                     label_text
40296                 ];
40297                 
40298                 if(this.indicatorpos == 'right') {
40299                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40300                     label.cn = [
40301                         label_text,
40302                         indicator
40303                     ];
40304                 }
40305                 
40306                 if(align == 'left') {
40307                     container = {
40308                         tag: 'div',
40309                         cn: [
40310                             container
40311                         ]
40312                     };
40313                     
40314                     if(this.labelWidth > 12){
40315                         label.style = "width: " + this.labelWidth + 'px';
40316                     }
40317                     if(this.labelWidth < 13 && this.labelmd == 0){
40318                         this.labelmd = this.labelWidth;
40319                     }
40320                     if(this.labellg > 0){
40321                         label.cls += ' col-lg-' + this.labellg;
40322                         input.cls += ' col-lg-' + (12 - this.labellg);
40323                     }
40324                     if(this.labelmd > 0){
40325                         label.cls += ' col-md-' + this.labelmd;
40326                         container.cls += ' col-md-' + (12 - this.labelmd);
40327                     }
40328                     if(this.labelsm > 0){
40329                         label.cls += ' col-sm-' + this.labelsm;
40330                         container.cls += ' col-sm-' + (12 - this.labelsm);
40331                     }
40332                     if(this.labelxs > 0){
40333                         label.cls += ' col-xs-' + this.labelxs;
40334                         container.cls += ' col-xs-' + (12 - this.labelxs);
40335                     }
40336                 }
40337             }
40338             
40339             cfg.cn = [
40340                 label,
40341                 container
40342             ];
40343             
40344             var settings = this;
40345             
40346             ['xs','sm','md','lg'].map(function(size){
40347                 if (settings[size]) {
40348                     cfg.cls += ' col-' + size + '-' + settings[size];
40349                 }
40350             });
40351             
40352             this.store = new Roo.data.Store({
40353                 proxy : new Roo.data.MemoryProxy({}),
40354                 reader : new Roo.data.JsonReader({
40355                     fields : [
40356                         {
40357                             'name' : 'name',
40358                             'type' : 'string'
40359                         },
40360                         {
40361                             'name' : 'iso2',
40362                             'type' : 'string'
40363                         },
40364                         {
40365                             'name' : 'dialCode',
40366                             'type' : 'string'
40367                         },
40368                         {
40369                             'name' : 'priority',
40370                             'type' : 'string'
40371                         },
40372                         {
40373                             'name' : 'areaCodes',
40374                             'type' : 'string'
40375                         }
40376                     ]
40377                 })
40378             });
40379             
40380             if(!this.preferedCountries) {
40381                 this.preferedCountries = [
40382                     'hk',
40383                     'gb',
40384                     'us'
40385                 ];
40386             }
40387             
40388             var p = this.preferedCountries.reverse();
40389             
40390             if(p) {
40391                 for (var i = 0; i < p.length; i++) {
40392                     for (var j = 0; j < this.allCountries.length; j++) {
40393                         if(this.allCountries[j].iso2 == p[i]) {
40394                             var t = this.allCountries[j];
40395                             this.allCountries.splice(j,1);
40396                             this.allCountries.unshift(t);
40397                         }
40398                     } 
40399                 }
40400             }
40401             
40402             this.store.proxy.data = {
40403                 success: true,
40404                 data: this.allCountries
40405             };
40406             
40407             return cfg;
40408         },
40409         
40410         initEvents : function()
40411         {
40412             this.createList();
40413             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40414             
40415             this.indicator = this.indicatorEl();
40416             this.flag = this.flagEl();
40417             this.dialCodeHolder = this.dialCodeHolderEl();
40418             
40419             this.trigger = this.el.select('div.flag-box',true).first();
40420             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40421             
40422             var _this = this;
40423             
40424             (function(){
40425                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40426                 _this.list.setWidth(lw);
40427             }).defer(100);
40428             
40429             this.list.on('mouseover', this.onViewOver, this);
40430             this.list.on('mousemove', this.onViewMove, this);
40431             this.inputEl().on("keyup", this.onKeyUp, this);
40432             this.inputEl().on("keypress", this.onKeyPress, this);
40433             
40434             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40435
40436             this.view = new Roo.View(this.list, this.tpl, {
40437                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40438             });
40439             
40440             this.view.on('click', this.onViewClick, this);
40441             this.setValue(this.defaultDialCode);
40442         },
40443         
40444         onTriggerClick : function(e)
40445         {
40446             Roo.log('trigger click');
40447             if(this.disabled){
40448                 return;
40449             }
40450             
40451             if(this.isExpanded()){
40452                 this.collapse();
40453                 this.hasFocus = false;
40454             }else {
40455                 this.store.load({});
40456                 this.hasFocus = true;
40457                 this.expand();
40458             }
40459         },
40460         
40461         isExpanded : function()
40462         {
40463             return this.list.isVisible();
40464         },
40465         
40466         collapse : function()
40467         {
40468             if(!this.isExpanded()){
40469                 return;
40470             }
40471             this.list.hide();
40472             Roo.get(document).un('mousedown', this.collapseIf, this);
40473             Roo.get(document).un('mousewheel', this.collapseIf, this);
40474             this.fireEvent('collapse', this);
40475             this.validate();
40476         },
40477         
40478         expand : function()
40479         {
40480             Roo.log('expand');
40481
40482             if(this.isExpanded() || !this.hasFocus){
40483                 return;
40484             }
40485             
40486             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40487             this.list.setWidth(lw);
40488             
40489             this.list.show();
40490             this.restrictHeight();
40491             
40492             Roo.get(document).on('mousedown', this.collapseIf, this);
40493             Roo.get(document).on('mousewheel', this.collapseIf, this);
40494             
40495             this.fireEvent('expand', this);
40496         },
40497         
40498         restrictHeight : function()
40499         {
40500             this.list.alignTo(this.inputEl(), this.listAlign);
40501             this.list.alignTo(this.inputEl(), this.listAlign);
40502         },
40503         
40504         onViewOver : function(e, t)
40505         {
40506             if(this.inKeyMode){
40507                 return;
40508             }
40509             var item = this.view.findItemFromChild(t);
40510             
40511             if(item){
40512                 var index = this.view.indexOf(item);
40513                 this.select(index, false);
40514             }
40515         },
40516
40517         // private
40518         onViewClick : function(view, doFocus, el, e)
40519         {
40520             var index = this.view.getSelectedIndexes()[0];
40521             
40522             var r = this.store.getAt(index);
40523             
40524             if(r){
40525                 this.onSelect(r, index);
40526             }
40527             if(doFocus !== false && !this.blockFocus){
40528                 this.inputEl().focus();
40529             }
40530         },
40531         
40532         onViewMove : function(e, t)
40533         {
40534             this.inKeyMode = false;
40535         },
40536         
40537         select : function(index, scrollIntoView)
40538         {
40539             this.selectedIndex = index;
40540             this.view.select(index);
40541             if(scrollIntoView !== false){
40542                 var el = this.view.getNode(index);
40543                 if(el){
40544                     this.list.scrollChildIntoView(el, false);
40545                 }
40546             }
40547         },
40548         
40549         createList : function()
40550         {
40551             this.list = Roo.get(document.body).createChild({
40552                 tag: 'ul',
40553                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40554                 style: 'display:none'
40555             });
40556             
40557             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40558         },
40559         
40560         collapseIf : function(e)
40561         {
40562             var in_combo  = e.within(this.el);
40563             var in_list =  e.within(this.list);
40564             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40565             
40566             if (in_combo || in_list || is_list) {
40567                 return;
40568             }
40569             this.collapse();
40570         },
40571         
40572         onSelect : function(record, index)
40573         {
40574             if(this.fireEvent('beforeselect', this, record, index) !== false){
40575                 
40576                 this.setFlagClass(record.data.iso2);
40577                 this.setDialCode(record.data.dialCode);
40578                 this.hasFocus = false;
40579                 this.collapse();
40580                 this.fireEvent('select', this, record, index);
40581             }
40582         },
40583         
40584         flagEl : function()
40585         {
40586             var flag = this.el.select('div.flag',true).first();
40587             if(!flag){
40588                 return false;
40589             }
40590             return flag;
40591         },
40592         
40593         dialCodeHolderEl : function()
40594         {
40595             var d = this.el.select('input.dial-code-holder',true).first();
40596             if(!d){
40597                 return false;
40598             }
40599             return d;
40600         },
40601         
40602         setDialCode : function(v)
40603         {
40604             this.dialCodeHolder.dom.value = '+'+v;
40605         },
40606         
40607         setFlagClass : function(n)
40608         {
40609             this.flag.dom.className = 'flag '+n;
40610         },
40611         
40612         getValue : function()
40613         {
40614             var v = this.inputEl().getValue();
40615             if(this.dialCodeHolder) {
40616                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40617             }
40618             return v;
40619         },
40620         
40621         setValue : function(v)
40622         {
40623             var d = this.getDialCode(v);
40624             
40625             //invalid dial code
40626             if(v.length == 0 || !d || d.length == 0) {
40627                 if(this.rendered){
40628                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40629                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40630                 }
40631                 return;
40632             }
40633             
40634             //valid dial code
40635             this.setFlagClass(this.dialCodeMapping[d].iso2);
40636             this.setDialCode(d);
40637             this.inputEl().dom.value = v.replace('+'+d,'');
40638             this.hiddenEl().dom.value = this.getValue();
40639             
40640             this.validate();
40641         },
40642         
40643         getDialCode : function(v)
40644         {
40645             v = v ||  '';
40646             
40647             if (v.length == 0) {
40648                 return this.dialCodeHolder.dom.value;
40649             }
40650             
40651             var dialCode = "";
40652             if (v.charAt(0) != "+") {
40653                 return false;
40654             }
40655             var numericChars = "";
40656             for (var i = 1; i < v.length; i++) {
40657               var c = v.charAt(i);
40658               if (!isNaN(c)) {
40659                 numericChars += c;
40660                 if (this.dialCodeMapping[numericChars]) {
40661                   dialCode = v.substr(1, i);
40662                 }
40663                 if (numericChars.length == 4) {
40664                   break;
40665                 }
40666               }
40667             }
40668             return dialCode;
40669         },
40670         
40671         reset : function()
40672         {
40673             this.setValue(this.defaultDialCode);
40674             this.validate();
40675         },
40676         
40677         hiddenEl : function()
40678         {
40679             return this.el.select('input.hidden-tel-input',true).first();
40680         },
40681         
40682         // after setting val
40683         onKeyUp : function(e){
40684             this.setValue(this.getValue());
40685         },
40686         
40687         onKeyPress : function(e){
40688             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40689                 e.stopEvent();
40690             }
40691         }
40692         
40693 });
40694 /**
40695  * @class Roo.bootstrap.MoneyField
40696  * @extends Roo.bootstrap.ComboBox
40697  * Bootstrap MoneyField class
40698  * 
40699  * @constructor
40700  * Create a new MoneyField.
40701  * @param {Object} config Configuration options
40702  */
40703
40704 Roo.bootstrap.MoneyField = function(config) {
40705     
40706     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40707     
40708 };
40709
40710 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40711     
40712     /**
40713      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40714      */
40715     allowDecimals : true,
40716     /**
40717      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40718      */
40719     decimalSeparator : ".",
40720     /**
40721      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40722      */
40723     decimalPrecision : 0,
40724     /**
40725      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40726      */
40727     allowNegative : true,
40728     /**
40729      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40730      */
40731     allowZero: true,
40732     /**
40733      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40734      */
40735     minValue : Number.NEGATIVE_INFINITY,
40736     /**
40737      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40738      */
40739     maxValue : Number.MAX_VALUE,
40740     /**
40741      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40742      */
40743     minText : "The minimum value for this field is {0}",
40744     /**
40745      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40746      */
40747     maxText : "The maximum value for this field is {0}",
40748     /**
40749      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40750      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40751      */
40752     nanText : "{0} is not a valid number",
40753     /**
40754      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40755      */
40756     castInt : true,
40757     /**
40758      * @cfg {String} defaults currency of the MoneyField
40759      * value should be in lkey
40760      */
40761     defaultCurrency : false,
40762     /**
40763      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40764      */
40765     thousandsDelimiter : false,
40766     /**
40767      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40768      */
40769     max_length: false,
40770     
40771     inputlg : 9,
40772     inputmd : 9,
40773     inputsm : 9,
40774     inputxs : 6,
40775     
40776     store : false,
40777     
40778     getAutoCreate : function()
40779     {
40780         var align = this.labelAlign || this.parentLabelAlign();
40781         
40782         var id = Roo.id();
40783
40784         var cfg = {
40785             cls: 'form-group',
40786             cn: []
40787         };
40788
40789         var input =  {
40790             tag: 'input',
40791             id : id,
40792             cls : 'form-control roo-money-amount-input',
40793             autocomplete: 'new-password'
40794         };
40795         
40796         var hiddenInput = {
40797             tag: 'input',
40798             type: 'hidden',
40799             id: Roo.id(),
40800             cls: 'hidden-number-input'
40801         };
40802         
40803         if(this.max_length) {
40804             input.maxlength = this.max_length; 
40805         }
40806         
40807         if (this.name) {
40808             hiddenInput.name = this.name;
40809         }
40810
40811         if (this.disabled) {
40812             input.disabled = true;
40813         }
40814
40815         var clg = 12 - this.inputlg;
40816         var cmd = 12 - this.inputmd;
40817         var csm = 12 - this.inputsm;
40818         var cxs = 12 - this.inputxs;
40819         
40820         var container = {
40821             tag : 'div',
40822             cls : 'row roo-money-field',
40823             cn : [
40824                 {
40825                     tag : 'div',
40826                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40827                     cn : [
40828                         {
40829                             tag : 'div',
40830                             cls: 'roo-select2-container input-group',
40831                             cn: [
40832                                 {
40833                                     tag : 'input',
40834                                     cls : 'form-control roo-money-currency-input',
40835                                     autocomplete: 'new-password',
40836                                     readOnly : 1,
40837                                     name : this.currencyName
40838                                 },
40839                                 {
40840                                     tag :'span',
40841                                     cls : 'input-group-addon',
40842                                     cn : [
40843                                         {
40844                                             tag: 'span',
40845                                             cls: 'caret'
40846                                         }
40847                                     ]
40848                                 }
40849                             ]
40850                         }
40851                     ]
40852                 },
40853                 {
40854                     tag : 'div',
40855                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40856                     cn : [
40857                         {
40858                             tag: 'div',
40859                             cls: this.hasFeedback ? 'has-feedback' : '',
40860                             cn: [
40861                                 input
40862                             ]
40863                         }
40864                     ]
40865                 }
40866             ]
40867             
40868         };
40869         
40870         if (this.fieldLabel.length) {
40871             var indicator = {
40872                 tag: 'i',
40873                 tooltip: 'This field is required'
40874             };
40875
40876             var label = {
40877                 tag: 'label',
40878                 'for':  id,
40879                 cls: 'control-label',
40880                 cn: []
40881             };
40882
40883             var label_text = {
40884                 tag: 'span',
40885                 html: this.fieldLabel
40886             };
40887
40888             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40889             label.cn = [
40890                 indicator,
40891                 label_text
40892             ];
40893
40894             if(this.indicatorpos == 'right') {
40895                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40896                 label.cn = [
40897                     label_text,
40898                     indicator
40899                 ];
40900             }
40901
40902             if(align == 'left') {
40903                 container = {
40904                     tag: 'div',
40905                     cn: [
40906                         container
40907                     ]
40908                 };
40909
40910                 if(this.labelWidth > 12){
40911                     label.style = "width: " + this.labelWidth + 'px';
40912                 }
40913                 if(this.labelWidth < 13 && this.labelmd == 0){
40914                     this.labelmd = this.labelWidth;
40915                 }
40916                 if(this.labellg > 0){
40917                     label.cls += ' col-lg-' + this.labellg;
40918                     input.cls += ' col-lg-' + (12 - this.labellg);
40919                 }
40920                 if(this.labelmd > 0){
40921                     label.cls += ' col-md-' + this.labelmd;
40922                     container.cls += ' col-md-' + (12 - this.labelmd);
40923                 }
40924                 if(this.labelsm > 0){
40925                     label.cls += ' col-sm-' + this.labelsm;
40926                     container.cls += ' col-sm-' + (12 - this.labelsm);
40927                 }
40928                 if(this.labelxs > 0){
40929                     label.cls += ' col-xs-' + this.labelxs;
40930                     container.cls += ' col-xs-' + (12 - this.labelxs);
40931                 }
40932             }
40933         }
40934
40935         cfg.cn = [
40936             label,
40937             container,
40938             hiddenInput
40939         ];
40940         
40941         var settings = this;
40942
40943         ['xs','sm','md','lg'].map(function(size){
40944             if (settings[size]) {
40945                 cfg.cls += ' col-' + size + '-' + settings[size];
40946             }
40947         });
40948         
40949         return cfg;
40950     },
40951     
40952     initEvents : function()
40953     {
40954         this.indicator = this.indicatorEl();
40955         
40956         this.initCurrencyEvent();
40957         
40958         this.initNumberEvent();
40959     },
40960     
40961     initCurrencyEvent : function()
40962     {
40963         if (!this.store) {
40964             throw "can not find store for combo";
40965         }
40966         
40967         this.store = Roo.factory(this.store, Roo.data);
40968         this.store.parent = this;
40969         
40970         this.createList();
40971         
40972         this.triggerEl = this.el.select('.input-group-addon', true).first();
40973         
40974         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40975         
40976         var _this = this;
40977         
40978         (function(){
40979             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40980             _this.list.setWidth(lw);
40981         }).defer(100);
40982         
40983         this.list.on('mouseover', this.onViewOver, this);
40984         this.list.on('mousemove', this.onViewMove, this);
40985         this.list.on('scroll', this.onViewScroll, this);
40986         
40987         if(!this.tpl){
40988             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40989         }
40990         
40991         this.view = new Roo.View(this.list, this.tpl, {
40992             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40993         });
40994         
40995         this.view.on('click', this.onViewClick, this);
40996         
40997         this.store.on('beforeload', this.onBeforeLoad, this);
40998         this.store.on('load', this.onLoad, this);
40999         this.store.on('loadexception', this.onLoadException, this);
41000         
41001         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41002             "up" : function(e){
41003                 this.inKeyMode = true;
41004                 this.selectPrev();
41005             },
41006
41007             "down" : function(e){
41008                 if(!this.isExpanded()){
41009                     this.onTriggerClick();
41010                 }else{
41011                     this.inKeyMode = true;
41012                     this.selectNext();
41013                 }
41014             },
41015
41016             "enter" : function(e){
41017                 this.collapse();
41018                 
41019                 if(this.fireEvent("specialkey", this, e)){
41020                     this.onViewClick(false);
41021                 }
41022                 
41023                 return true;
41024             },
41025
41026             "esc" : function(e){
41027                 this.collapse();
41028             },
41029
41030             "tab" : function(e){
41031                 this.collapse();
41032                 
41033                 if(this.fireEvent("specialkey", this, e)){
41034                     this.onViewClick(false);
41035                 }
41036                 
41037                 return true;
41038             },
41039
41040             scope : this,
41041
41042             doRelay : function(foo, bar, hname){
41043                 if(hname == 'down' || this.scope.isExpanded()){
41044                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41045                 }
41046                 return true;
41047             },
41048
41049             forceKeyDown: true
41050         });
41051         
41052         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41053         
41054     },
41055     
41056     initNumberEvent : function(e)
41057     {
41058         this.inputEl().on("keydown" , this.fireKey,  this);
41059         this.inputEl().on("focus", this.onFocus,  this);
41060         this.inputEl().on("blur", this.onBlur,  this);
41061         
41062         this.inputEl().relayEvent('keyup', this);
41063         
41064         if(this.indicator){
41065             this.indicator.addClass('invisible');
41066         }
41067  
41068         this.originalValue = this.getValue();
41069         
41070         if(this.validationEvent == 'keyup'){
41071             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41072             this.inputEl().on('keyup', this.filterValidation, this);
41073         }
41074         else if(this.validationEvent !== false){
41075             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41076         }
41077         
41078         if(this.selectOnFocus){
41079             this.on("focus", this.preFocus, this);
41080             
41081         }
41082         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41083             this.inputEl().on("keypress", this.filterKeys, this);
41084         } else {
41085             this.inputEl().relayEvent('keypress', this);
41086         }
41087         
41088         var allowed = "0123456789";
41089         
41090         if(this.allowDecimals){
41091             allowed += this.decimalSeparator;
41092         }
41093         
41094         if(this.allowNegative){
41095             allowed += "-";
41096         }
41097         
41098         if(this.thousandsDelimiter) {
41099             allowed += ",";
41100         }
41101         
41102         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41103         
41104         var keyPress = function(e){
41105             
41106             var k = e.getKey();
41107             
41108             var c = e.getCharCode();
41109             
41110             if(
41111                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41112                     allowed.indexOf(String.fromCharCode(c)) === -1
41113             ){
41114                 e.stopEvent();
41115                 return;
41116             }
41117             
41118             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41119                 return;
41120             }
41121             
41122             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41123                 e.stopEvent();
41124             }
41125         };
41126         
41127         this.inputEl().on("keypress", keyPress, this);
41128         
41129     },
41130     
41131     onTriggerClick : function(e)
41132     {   
41133         if(this.disabled){
41134             return;
41135         }
41136         
41137         this.page = 0;
41138         this.loadNext = false;
41139         
41140         if(this.isExpanded()){
41141             this.collapse();
41142             return;
41143         }
41144         
41145         this.hasFocus = true;
41146         
41147         if(this.triggerAction == 'all') {
41148             this.doQuery(this.allQuery, true);
41149             return;
41150         }
41151         
41152         this.doQuery(this.getRawValue());
41153     },
41154     
41155     getCurrency : function()
41156     {   
41157         var v = this.currencyEl().getValue();
41158         
41159         return v;
41160     },
41161     
41162     restrictHeight : function()
41163     {
41164         this.list.alignTo(this.currencyEl(), this.listAlign);
41165         this.list.alignTo(this.currencyEl(), this.listAlign);
41166     },
41167     
41168     onViewClick : function(view, doFocus, el, e)
41169     {
41170         var index = this.view.getSelectedIndexes()[0];
41171         
41172         var r = this.store.getAt(index);
41173         
41174         if(r){
41175             this.onSelect(r, index);
41176         }
41177     },
41178     
41179     onSelect : function(record, index){
41180         
41181         if(this.fireEvent('beforeselect', this, record, index) !== false){
41182         
41183             this.setFromCurrencyData(index > -1 ? record.data : false);
41184             
41185             this.collapse();
41186             
41187             this.fireEvent('select', this, record, index);
41188         }
41189     },
41190     
41191     setFromCurrencyData : function(o)
41192     {
41193         var currency = '';
41194         
41195         this.lastCurrency = o;
41196         
41197         if (this.currencyField) {
41198             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41199         } else {
41200             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41201         }
41202         
41203         this.lastSelectionText = currency;
41204         
41205         //setting default currency
41206         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41207             this.setCurrency(this.defaultCurrency);
41208             return;
41209         }
41210         
41211         this.setCurrency(currency);
41212     },
41213     
41214     setFromData : function(o)
41215     {
41216         var c = {};
41217         
41218         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41219         
41220         this.setFromCurrencyData(c);
41221         
41222         var value = '';
41223         
41224         if (this.name) {
41225             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41226         } else {
41227             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41228         }
41229         
41230         this.setValue(value);
41231         
41232     },
41233     
41234     setCurrency : function(v)
41235     {   
41236         this.currencyValue = v;
41237         
41238         if(this.rendered){
41239             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41240             this.validate();
41241         }
41242     },
41243     
41244     setValue : function(v)
41245     {
41246         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41247         
41248         this.value = v;
41249         
41250         if(this.rendered){
41251             
41252             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41253             
41254             this.inputEl().dom.value = (v == '') ? '' :
41255                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41256             
41257             if(!this.allowZero && v === '0') {
41258                 this.hiddenEl().dom.value = '';
41259                 this.inputEl().dom.value = '';
41260             }
41261             
41262             this.validate();
41263         }
41264     },
41265     
41266     getRawValue : function()
41267     {
41268         var v = this.inputEl().getValue();
41269         
41270         return v;
41271     },
41272     
41273     getValue : function()
41274     {
41275         return this.fixPrecision(this.parseValue(this.getRawValue()));
41276     },
41277     
41278     parseValue : function(value)
41279     {
41280         if(this.thousandsDelimiter) {
41281             value += "";
41282             r = new RegExp(",", "g");
41283             value = value.replace(r, "");
41284         }
41285         
41286         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41287         return isNaN(value) ? '' : value;
41288         
41289     },
41290     
41291     fixPrecision : function(value)
41292     {
41293         if(this.thousandsDelimiter) {
41294             value += "";
41295             r = new RegExp(",", "g");
41296             value = value.replace(r, "");
41297         }
41298         
41299         var nan = isNaN(value);
41300         
41301         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41302             return nan ? '' : value;
41303         }
41304         return parseFloat(value).toFixed(this.decimalPrecision);
41305     },
41306     
41307     decimalPrecisionFcn : function(v)
41308     {
41309         return Math.floor(v);
41310     },
41311     
41312     validateValue : function(value)
41313     {
41314         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41315             return false;
41316         }
41317         
41318         var num = this.parseValue(value);
41319         
41320         if(isNaN(num)){
41321             this.markInvalid(String.format(this.nanText, value));
41322             return false;
41323         }
41324         
41325         if(num < this.minValue){
41326             this.markInvalid(String.format(this.minText, this.minValue));
41327             return false;
41328         }
41329         
41330         if(num > this.maxValue){
41331             this.markInvalid(String.format(this.maxText, this.maxValue));
41332             return false;
41333         }
41334         
41335         return true;
41336     },
41337     
41338     validate : function()
41339     {
41340         if(this.disabled || this.allowBlank){
41341             this.markValid();
41342             return true;
41343         }
41344         
41345         var currency = this.getCurrency();
41346         
41347         if(this.validateValue(this.getRawValue()) && currency.length){
41348             this.markValid();
41349             return true;
41350         }
41351         
41352         this.markInvalid();
41353         return false;
41354     },
41355     
41356     getName: function()
41357     {
41358         return this.name;
41359     },
41360     
41361     beforeBlur : function()
41362     {
41363         if(!this.castInt){
41364             return;
41365         }
41366         
41367         var v = this.parseValue(this.getRawValue());
41368         
41369         if(v || v == 0){
41370             this.setValue(v);
41371         }
41372     },
41373     
41374     onBlur : function()
41375     {
41376         this.beforeBlur();
41377         
41378         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41379             //this.el.removeClass(this.focusClass);
41380         }
41381         
41382         this.hasFocus = false;
41383         
41384         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41385             this.validate();
41386         }
41387         
41388         var v = this.getValue();
41389         
41390         if(String(v) !== String(this.startValue)){
41391             this.fireEvent('change', this, v, this.startValue);
41392         }
41393         
41394         this.fireEvent("blur", this);
41395     },
41396     
41397     inputEl : function()
41398     {
41399         return this.el.select('.roo-money-amount-input', true).first();
41400     },
41401     
41402     currencyEl : function()
41403     {
41404         return this.el.select('.roo-money-currency-input', true).first();
41405     },
41406     
41407     hiddenEl : function()
41408     {
41409         return this.el.select('input.hidden-number-input',true).first();
41410     }
41411     
41412 });