Roo/bootstrap/TextArea.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets[0], function(s) {
10                     if (s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })();/*
16  * - LGPL
17  *
18  * base class for bootstrap elements.
19  * 
20  */
21
22 Roo.bootstrap = Roo.bootstrap || {};
23 /**
24  * @class Roo.bootstrap.Component
25  * @extends Roo.Component
26  * Bootstrap Component base class
27  * @cfg {String} cls css class
28  * @cfg {String} style any extra css
29  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
31  * @cfg {string} dataId cutomer id
32  * @cfg {string} name Specifies name attribute
33  * @cfg {string} tooltip  Text for the tooltip
34  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
35  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
36  
37  * @constructor
38  * Do not use directly - it does not do anything..
39  * @param {Object} config The config object
40  */
41
42
43
44 Roo.bootstrap.Component = function(config){
45     Roo.bootstrap.Component.superclass.constructor.call(this, config);
46        
47     this.addEvents({
48         /**
49          * @event childrenrendered
50          * Fires when the children have been rendered..
51          * @param {Roo.bootstrap.Component} this
52          */
53         "childrenrendered" : true
54         
55         
56         
57     });
58     
59     
60 };
61
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
63     
64     
65     allowDomMove : false, // to stop relocations in parent onRender...
66     
67     cls : false,
68     
69     style : false,
70     
71     autoCreate : false,
72     
73     tooltip : null,
74     /**
75      * Initialize Events for the element
76      */
77     initEvents : function() { },
78     
79     xattr : false,
80     
81     parentId : false,
82     
83     can_build_overlaid : true,
84     
85     container_method : false,
86     
87     dataId : false,
88     
89     name : false,
90     
91     parent: function() {
92         // returns the parent component..
93         return Roo.ComponentMgr.get(this.parentId)
94         
95         
96     },
97     
98     // private
99     onRender : function(ct, position)
100     {
101        // Roo.log("Call onRender: " + this.xtype);
102         
103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
104         
105         if(this.el){
106             if (this.el.attr('xtype')) {
107                 this.el.attr('xtypex', this.el.attr('xtype'));
108                 this.el.dom.removeAttribute('xtype');
109                 
110                 this.initEvents();
111             }
112             
113             return;
114         }
115         
116          
117         
118         var cfg = Roo.apply({},  this.getAutoCreate());
119         
120         cfg.id = this.id || Roo.id();
121         
122         // fill in the extra attributes 
123         if (this.xattr && typeof(this.xattr) =='object') {
124             for (var i in this.xattr) {
125                 cfg[i] = this.xattr[i];
126             }
127         }
128         
129         if(this.dataId){
130             cfg.dataId = this.dataId;
131         }
132         
133         if (this.cls) {
134             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
135         }
136         
137         if (this.style) { // fixme needs to support more complex style data.
138             cfg.style = this.style;
139         }
140         
141         if(this.name){
142             cfg.name = this.name;
143         }
144         
145         this.el = ct.createChild(cfg, position);
146         
147         if (this.tooltip) {
148             this.tooltipEl().attr('tooltip', this.tooltip);
149         }
150         
151         if(this.tabIndex !== undefined){
152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
153         }
154         
155         this.initEvents();
156         
157     },
158     /**
159      * Fetch the element to add children to
160      * @return {Roo.Element} defaults to this.el
161      */
162     getChildContainer : function()
163     {
164         return this.el;
165     },
166     /**
167      * Fetch the element to display the tooltip on.
168      * @return {Roo.Element} defaults to this.el
169      */
170     tooltipEl : function()
171     {
172         return this.el;
173     },
174         
175     addxtype  : function(tree,cntr)
176     {
177         var cn = this;
178         
179         cn = Roo.factory(tree);
180         //Roo.log(['addxtype', cn]);
181            
182         cn.parentType = this.xtype; //??
183         cn.parentId = this.id;
184         
185         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186         if (typeof(cn.container_method) == 'string') {
187             cntr = cn.container_method;
188         }
189         
190         
191         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
192         
193         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
194         
195         var build_from_html =  Roo.XComponent.build_from_html;
196           
197         var is_body  = (tree.xtype == 'Body') ;
198           
199         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
200           
201         var self_cntr_el = Roo.get(this[cntr](false));
202         
203         // do not try and build conditional elements 
204         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
205             return false;
206         }
207         
208         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210                 return this.addxtypeChild(tree,cntr, is_body);
211             }
212             
213             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
214                 
215             if(echild){
216                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
217             }
218             
219             Roo.log('skipping render');
220             return cn;
221             
222         }
223         
224         var ret = false;
225         if (!build_from_html) {
226             return false;
227         }
228         
229         // this i think handles overlaying multiple children of the same type
230         // with the sam eelement.. - which might be buggy..
231         while (true) {
232             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
233             
234             if (!echild) {
235                 break;
236             }
237             
238             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
239                 break;
240             }
241             
242             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
243         }
244        
245         return ret;
246     },
247     
248     
249     addxtypeChild : function (tree, cntr, is_body)
250     {
251         Roo.debug && Roo.log('addxtypeChild:' + cntr);
252         var cn = this;
253         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
254         
255         
256         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257                     (typeof(tree['flexy:foreach']) != 'undefined');
258           
259     
260         
261         skip_children = false;
262         // render the element if it's not BODY.
263         if (!is_body) {
264             
265             // if parent was disabled, then do not try and create the children..
266             if(!this[cntr](true)){
267                 tree.items = [];
268                 return tree;
269             }
270            
271             cn = Roo.factory(tree);
272            
273             cn.parentType = this.xtype; //??
274             cn.parentId = this.id;
275             
276             var build_from_html =  Roo.XComponent.build_from_html;
277             
278             
279             // does the container contain child eleemnts with 'xtype' attributes.
280             // that match this xtype..
281             // note - when we render we create these as well..
282             // so we should check to see if body has xtype set.
283             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
284                
285                 var self_cntr_el = Roo.get(this[cntr](false));
286                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
287                 if (echild) { 
288                     //Roo.log(Roo.XComponent.build_from_html);
289                     //Roo.log("got echild:");
290                     //Roo.log(echild);
291                 }
292                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293                 // and are not displayed -this causes this to use up the wrong element when matching.
294                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
295                 
296                 
297                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
299                   
300                   
301                   
302                     cn.el = echild;
303                   //  Roo.log("GOT");
304                     //echild.dom.removeAttribute('xtype');
305                 } else {
306                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307                     Roo.debug && Roo.log(self_cntr_el);
308                     Roo.debug && Roo.log(echild);
309                     Roo.debug && Roo.log(cn);
310                 }
311             }
312            
313             
314            
315             // if object has flexy:if - then it may or may not be rendered.
316             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
317                 // skip a flexy if element.
318                 Roo.debug && Roo.log('skipping render');
319                 Roo.debug && Roo.log(tree);
320                 if (!cn.el) {
321                     Roo.debug && Roo.log('skipping all children');
322                     skip_children = true;
323                 }
324                 
325              } else {
326                  
327                 // actually if flexy:foreach is found, we really want to create 
328                 // multiple copies here...
329                 //Roo.log('render');
330                 //Roo.log(this[cntr]());
331                 // some elements do not have render methods.. like the layouts...
332                 /*
333                 if(this[cntr](true) === false){
334                     cn.items = [];
335                     return cn;
336                 }
337                 */
338                 cn.render && cn.render(this[cntr](true));
339                 
340              }
341             // then add the element..
342         }
343          
344         // handle the kids..
345         
346         var nitems = [];
347         /*
348         if (typeof (tree.menu) != 'undefined') {
349             tree.menu.parentType = cn.xtype;
350             tree.menu.triggerEl = cn.el;
351             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
352             
353         }
354         */
355         if (!tree.items || !tree.items.length) {
356             cn.items = nitems;
357             //Roo.log(["no children", this]);
358             
359             return cn;
360         }
361          
362         var items = tree.items;
363         delete tree.items;
364         
365         //Roo.log(items.length);
366             // add the items..
367         if (!skip_children) {    
368             for(var i =0;i < items.length;i++) {
369               //  Roo.log(['add child', items[i]]);
370                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
371             }
372         }
373         
374         cn.items = nitems;
375         
376         //Roo.log("fire childrenrendered");
377         
378         cn.fireEvent('childrenrendered', this);
379         
380         return cn;
381     },
382     
383     /**
384      * Set the element that will be used to show or hide
385      */
386     setVisibilityEl : function(el)
387     {
388         this.visibilityEl = el;
389     },
390     
391      /**
392      * Get the element that will be used to show or hide
393      */
394     getVisibilityEl : function()
395     {
396         if (typeof(this.visibilityEl) == 'object') {
397             return this.visibilityEl;
398         }
399         
400         if (typeof(this.visibilityEl) == 'string') {
401             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
402         }
403         
404         return this.getEl();
405     },
406     
407     /**
408      * Show a component - removes 'hidden' class
409      */
410     show : function()
411     {
412         if(!this.getVisibilityEl()){
413             return;
414         }
415          
416         this.getVisibilityEl().removeClass(['hidden','d-none']);
417         
418         this.fireEvent('show', this);
419         
420         
421     },
422     /**
423      * Hide a component - adds 'hidden' class
424      */
425     hide: function()
426     {
427         if(!this.getVisibilityEl()){
428             return;
429         }
430         
431         this.getVisibilityEl().addClass(['hidden','d-none']);
432         
433         this.fireEvent('hide', this);
434         
435     }
436 });
437
438  /*
439  * - LGPL
440  *
441  * Body
442  *
443  */
444
445 /**
446  * @class Roo.bootstrap.Body
447  * @extends Roo.bootstrap.Component
448  * Bootstrap Body class
449  *
450  * @constructor
451  * Create a new body
452  * @param {Object} config The config object
453  */
454
455 Roo.bootstrap.Body = function(config){
456
457     config = config || {};
458
459     Roo.bootstrap.Body.superclass.constructor.call(this, config);
460     this.el = Roo.get(config.el ? config.el : document.body );
461     if (this.cls && this.cls.length) {
462         Roo.get(document.body).addClass(this.cls);
463     }
464 };
465
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
467
468     is_body : true,// just to make sure it's constructed?
469
470         autoCreate : {
471         cls: 'container'
472     },
473     onRender : function(ct, position)
474     {
475        /* Roo.log("Roo.bootstrap.Body - onRender");
476         if (this.cls && this.cls.length) {
477             Roo.get(document.body).addClass(this.cls);
478         }
479         // style??? xttr???
480         */
481     }
482
483
484
485
486 });
487 /*
488  * - LGPL
489  *
490  * button group
491  * 
492  */
493
494
495 /**
496  * @class Roo.bootstrap.ButtonGroup
497  * @extends Roo.bootstrap.Component
498  * Bootstrap ButtonGroup class
499  * @cfg {String} size lg | sm | xs (default empty normal)
500  * @cfg {String} align vertical | justified  (default none)
501  * @cfg {String} direction up | down (default down)
502  * @cfg {Boolean} toolbar false | true
503  * @cfg {Boolean} btn true | false
504  * 
505  * 
506  * @constructor
507  * Create a new Input
508  * @param {Object} config The config object
509  */
510
511 Roo.bootstrap.ButtonGroup = function(config){
512     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
513 };
514
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
516     
517     size: '',
518     align: '',
519     direction: '',
520     toolbar: false,
521     btn: true,
522
523     getAutoCreate : function(){
524         var cfg = {
525             cls: 'btn-group',
526             html : null
527         };
528         
529         cfg.html = this.html || cfg.html;
530         
531         if (this.toolbar) {
532             cfg = {
533                 cls: 'btn-toolbar',
534                 html: null
535             };
536             
537             return cfg;
538         }
539         
540         if (['vertical','justified'].indexOf(this.align)!==-1) {
541             cfg.cls = 'btn-group-' + this.align;
542             
543             if (this.align == 'justified') {
544                 console.log(this.items);
545             }
546         }
547         
548         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549             cfg.cls += ' btn-group-' + this.size;
550         }
551         
552         if (this.direction == 'up') {
553             cfg.cls += ' dropup' ;
554         }
555         
556         return cfg;
557     },
558     /**
559      * Add a button to the group (similar to NavItem API.)
560      */
561     addItem : function(cfg)
562     {
563         var cn = new Roo.bootstrap.Button(cfg);
564         //this.register(cn);
565         cn.parentId = this.id;
566         cn.onRender(this.el, null);
567         return cn;
568     }
569    
570 });
571
572  /*
573  * - LGPL
574  *
575  * button
576  * 
577  */
578
579 /**
580  * @class Roo.bootstrap.Button
581  * @extends Roo.bootstrap.Component
582  * Bootstrap Button class
583  * @cfg {String} html The button content
584  * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
585  * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
586  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
587  * @cfg {String} size ( lg | sm | xs)
588  * @cfg {String} tag ( a | input | submit)
589  * @cfg {String} href empty or href
590  * @cfg {Boolean} disabled default false;
591  * @cfg {Boolean} isClose default false;
592  * @cfg {String} glyphicon depricated - use fa
593  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
594  * @cfg {String} badge text for badge
595  * @cfg {String} theme (default|glow)  
596  * @cfg {Boolean} inverse dark themed version
597  * @cfg {Boolean} toggle is it a slidy toggle button
598  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
599  * @cfg {String} ontext text for on slidy toggle state
600  * @cfg {String} offtext text for off slidy toggle state
601  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
602  * @cfg {Boolean} removeClass remove the standard class..
603  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
604  * 
605  * @constructor
606  * Create a new button
607  * @param {Object} config The config object
608  */
609
610
611 Roo.bootstrap.Button = function(config){
612     Roo.bootstrap.Button.superclass.constructor.call(this, config);
613     this.weightClass = ["btn-default btn-outline-secondary", 
614                        "btn-primary", 
615                        "btn-success", 
616                        "btn-info", 
617                        "btn-warning",
618                        "btn-danger",
619                        "btn-link"
620                       ],  
621     this.addEvents({
622         // raw events
623         /**
624          * @event click
625          * When a butotn is pressed
626          * @param {Roo.bootstrap.Button} btn
627          * @param {Roo.EventObject} e
628          */
629         "click" : true,
630          /**
631          * @event toggle
632          * After the button has been toggles
633          * @param {Roo.bootstrap.Button} btn
634          * @param {Roo.EventObject} e
635          * @param {boolean} pressed (also available as button.pressed)
636          */
637         "toggle" : true
638     });
639 };
640
641 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
642     html: false,
643     active: false,
644     weight: '',
645     badge_weight: '',
646     outline : false,
647     size: '',
648     tag: 'button',
649     href: '',
650     disabled: false,
651     isClose: false,
652     glyphicon: '',
653     fa: '',
654     badge: '',
655     theme: 'default',
656     inverse: false,
657     
658     toggle: false,
659     ontext: 'ON',
660     offtext: 'OFF',
661     defaulton: true,
662     preventDefault: true,
663     removeClass: false,
664     name: false,
665     target: false,
666      
667     pressed : null,
668      
669     
670     getAutoCreate : function(){
671         
672         var cfg = {
673             tag : 'button',
674             cls : 'roo-button',
675             html: ''
676         };
677         
678         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
679             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
680             this.tag = 'button';
681         } else {
682             cfg.tag = this.tag;
683         }
684         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
685         
686         if (this.toggle == true) {
687             cfg={
688                 tag: 'div',
689                 cls: 'slider-frame roo-button',
690                 cn: [
691                     {
692                         tag: 'span',
693                         'data-on-text':'ON',
694                         'data-off-text':'OFF',
695                         cls: 'slider-button',
696                         html: this.offtext
697                     }
698                 ]
699             };
700             
701             if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
702                 cfg.cls += ' '+this.weight;
703             }
704             
705             return cfg;
706         }
707         
708         if (this.isClose) {
709             cfg.cls += ' close';
710             
711             cfg["aria-hidden"] = true;
712             
713             cfg.html = "&times;";
714             
715             return cfg;
716         }
717         
718          
719         if (this.theme==='default') {
720             cfg.cls = 'btn roo-button';
721             
722             //if (this.parentType != 'Navbar') {
723             this.weight = this.weight.length ?  this.weight : 'default';
724             //}
725             if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
726                 
727                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
728                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
729                 cfg.cls += ' btn-' + outline + weight;
730                 if (this.weight == 'default') {
731                     // BC
732                     cfg.cls += ' btn-' + this.weight;
733                 }
734             }
735         } else if (this.theme==='glow') {
736             
737             cfg.tag = 'a';
738             cfg.cls = 'btn-glow roo-button';
739             
740             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
741                 
742                 cfg.cls += ' ' + this.weight;
743             }
744         }
745    
746         
747         if (this.inverse) {
748             this.cls += ' inverse';
749         }
750         
751         
752         if (this.active || this.pressed === true) {
753             cfg.cls += ' active';
754         }
755         
756         if (this.disabled) {
757             cfg.disabled = 'disabled';
758         }
759         
760         if (this.items) {
761             Roo.log('changing to ul' );
762             cfg.tag = 'ul';
763             this.glyphicon = 'caret';
764             if (Roo.bootstrap.version == 4) {
765                 this.fa = 'caret-down';
766             }
767             
768         }
769         
770         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
771          
772         //gsRoo.log(this.parentType);
773         if (this.parentType === 'Navbar' && !this.parent().bar) {
774             Roo.log('changing to li?');
775             
776             cfg.tag = 'li';
777             
778             cfg.cls = '';
779             cfg.cn =  [{
780                 tag : 'a',
781                 cls : 'roo-button',
782                 html : this.html,
783                 href : this.href || '#'
784             }];
785             if (this.menu) {
786                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
787                 cfg.cls += ' dropdown';
788             }   
789             
790             delete cfg.html;
791             
792         }
793         
794        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
795         
796         if (this.glyphicon) {
797             cfg.html = ' ' + cfg.html;
798             
799             cfg.cn = [
800                 {
801                     tag: 'span',
802                     cls: 'glyphicon glyphicon-' + this.glyphicon
803                 }
804             ];
805         }
806         if (this.fa) {
807             cfg.html = ' ' + cfg.html;
808             
809             cfg.cn = [
810                 {
811                     tag: 'i',
812                     cls: 'fa fas fa-' + this.fa
813                 }
814             ];
815         }
816         
817         if (this.badge) {
818             cfg.html += ' ';
819             
820             cfg.tag = 'a';
821             
822 //            cfg.cls='btn roo-button';
823             
824             cfg.href=this.href;
825             
826             var value = cfg.html;
827             
828             if(this.glyphicon){
829                 value = {
830                     tag: 'span',
831                     cls: 'glyphicon glyphicon-' + this.glyphicon,
832                     html: this.html
833                 };
834             }
835             if(this.fa){
836                 value = {
837                     tag: 'i',
838                     cls: 'fa fas fa-' + this.fa,
839                     html: this.html
840                 };
841             }
842             
843             var bw = this.badge_weight.length ? this.badge_weight :
844                 (this.weight.length ? this.weight : 'secondary');
845             bw = bw == 'default' ? 'secondary' : bw;
846             
847             cfg.cn = [
848                 value,
849                 {
850                     tag: 'span',
851                     cls: 'badge badge-' + bw,
852                     html: this.badge
853                 }
854             ];
855             
856             cfg.html='';
857         }
858         
859         if (this.menu) {
860             cfg.cls += ' dropdown';
861             cfg.html = typeof(cfg.html) != 'undefined' ?
862                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
863         }
864         
865         if (cfg.tag !== 'a' && this.href !== '') {
866             throw "Tag must be a to set href.";
867         } else if (this.href.length > 0) {
868             cfg.href = this.href;
869         }
870         
871         if(this.removeClass){
872             cfg.cls = '';
873         }
874         
875         if(this.target){
876             cfg.target = this.target;
877         }
878         
879         return cfg;
880     },
881     initEvents: function() {
882        // Roo.log('init events?');
883 //        Roo.log(this.el.dom);
884         // add the menu...
885         
886         if (typeof (this.menu) != 'undefined') {
887             this.menu.parentType = this.xtype;
888             this.menu.triggerEl = this.el;
889             this.addxtype(Roo.apply({}, this.menu));
890         }
891
892
893        if (this.el.hasClass('roo-button')) {
894             this.el.on('click', this.onClick, this);
895        } else {
896             this.el.select('.roo-button').on('click', this.onClick, this);
897        }
898        
899        if(this.removeClass){
900            this.el.on('click', this.onClick, this);
901        }
902        
903        this.el.enableDisplayMode();
904         
905     },
906     onClick : function(e)
907     {
908         if (this.disabled) {
909             return;
910         }
911         
912         Roo.log('button on click ');
913         if(this.preventDefault){
914             e.preventDefault();
915         }
916         
917         if (this.pressed === true || this.pressed === false) {
918             this.toggleActive(e);
919         }
920         
921         
922         this.fireEvent('click', this, e);
923     },
924     
925     /**
926      * Enables this button
927      */
928     enable : function()
929     {
930         this.disabled = false;
931         this.el.removeClass('disabled');
932     },
933     
934     /**
935      * Disable this button
936      */
937     disable : function()
938     {
939         this.disabled = true;
940         this.el.addClass('disabled');
941     },
942      /**
943      * sets the active state on/off, 
944      * @param {Boolean} state (optional) Force a particular state
945      */
946     setActive : function(v) {
947         
948         this.el[v ? 'addClass' : 'removeClass']('active');
949         this.pressed = v;
950     },
951      /**
952      * toggles the current active state 
953      */
954     toggleActive : function(e)
955     {
956         this.setActive(!this.pressed);
957         this.fireEvent('toggle', this, e, !this.pressed);
958     },
959      /**
960      * get the current active state
961      * @return {boolean} true if it's active
962      */
963     isActive : function()
964     {
965         return this.el.hasClass('active');
966     },
967     /**
968      * set the text of the first selected button
969      */
970     setText : function(str)
971     {
972         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
973     },
974     /**
975      * get the text of the first selected button
976      */
977     getText : function()
978     {
979         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
980     },
981     
982     setWeight : function(str)
983     {
984         this.el.removeClass(this.weightClass);
985         this.weight = str;
986         var outline = this.outline ? 'outline-' : '';
987         if (str == 'default') {
988             this.el.addClass('btn-default btn-outline-secondary');        
989             return;
990         }
991         this.el.addClass('btn-' + outline + str);        
992     }
993     
994     
995 });
996
997  /*
998  * - LGPL
999  *
1000  * column
1001  * 
1002  */
1003
1004 /**
1005  * @class Roo.bootstrap.Column
1006  * @extends Roo.bootstrap.Component
1007  * Bootstrap Column class
1008  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1009  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1010  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1011  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1012  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1013  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1014  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1015  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1016  *
1017  * 
1018  * @cfg {Boolean} hidden (true|false) hide the element
1019  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1020  * @cfg {String} fa (ban|check|...) font awesome icon
1021  * @cfg {Number} fasize (1|2|....) font awsome size
1022
1023  * @cfg {String} icon (info-sign|check|...) glyphicon name
1024
1025  * @cfg {String} html content of column.
1026  * 
1027  * @constructor
1028  * Create a new Column
1029  * @param {Object} config The config object
1030  */
1031
1032 Roo.bootstrap.Column = function(config){
1033     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1034 };
1035
1036 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1037     
1038     xs: false,
1039     sm: false,
1040     md: false,
1041     lg: false,
1042     xsoff: false,
1043     smoff: false,
1044     mdoff: false,
1045     lgoff: false,
1046     html: '',
1047     offset: 0,
1048     alert: false,
1049     fa: false,
1050     icon : false,
1051     hidden : false,
1052     fasize : 1,
1053     
1054     getAutoCreate : function(){
1055         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1056         
1057         cfg = {
1058             tag: 'div',
1059             cls: 'column'
1060         };
1061         
1062         var settings=this;
1063         ['xs','sm','md','lg'].map(function(size){
1064             //Roo.log( size + ':' + settings[size]);
1065             
1066             if (settings[size+'off'] !== false) {
1067                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1068             }
1069             
1070             if (settings[size] === false) {
1071                 return;
1072             }
1073             
1074             if (!settings[size]) { // 0 = hidden
1075                 cfg.cls += ' hidden-' + size;
1076                 return;
1077             }
1078             cfg.cls += ' col-' + size + '-' + settings[size];
1079             
1080         });
1081         
1082         if (this.hidden) {
1083             cfg.cls += ' hidden';
1084         }
1085         
1086         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1087             cfg.cls +=' alert alert-' + this.alert;
1088         }
1089         
1090         
1091         if (this.html.length) {
1092             cfg.html = this.html;
1093         }
1094         if (this.fa) {
1095             var fasize = '';
1096             if (this.fasize > 1) {
1097                 fasize = ' fa-' + this.fasize + 'x';
1098             }
1099             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1100             
1101             
1102         }
1103         if (this.icon) {
1104             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1105         }
1106         
1107         return cfg;
1108     }
1109    
1110 });
1111
1112  
1113
1114  /*
1115  * - LGPL
1116  *
1117  * page container.
1118  * 
1119  */
1120
1121
1122 /**
1123  * @class Roo.bootstrap.Container
1124  * @extends Roo.bootstrap.Component
1125  * Bootstrap Container class
1126  * @cfg {Boolean} jumbotron is it a jumbotron element
1127  * @cfg {String} html content of element
1128  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1129  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1130  * @cfg {String} header content of header (for panel)
1131  * @cfg {String} footer content of footer (for panel)
1132  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1133  * @cfg {String} tag (header|aside|section) type of HTML tag.
1134  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1135  * @cfg {String} fa font awesome icon
1136  * @cfg {String} icon (info-sign|check|...) glyphicon name
1137  * @cfg {Boolean} hidden (true|false) hide the element
1138  * @cfg {Boolean} expandable (true|false) default false
1139  * @cfg {Boolean} expanded (true|false) default true
1140  * @cfg {String} rheader contet on the right of header
1141  * @cfg {Boolean} clickable (true|false) default false
1142
1143  *     
1144  * @constructor
1145  * Create a new Container
1146  * @param {Object} config The config object
1147  */
1148
1149 Roo.bootstrap.Container = function(config){
1150     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1151     
1152     this.addEvents({
1153         // raw events
1154          /**
1155          * @event expand
1156          * After the panel has been expand
1157          * 
1158          * @param {Roo.bootstrap.Container} this
1159          */
1160         "expand" : true,
1161         /**
1162          * @event collapse
1163          * After the panel has been collapsed
1164          * 
1165          * @param {Roo.bootstrap.Container} this
1166          */
1167         "collapse" : true,
1168         /**
1169          * @event click
1170          * When a element is chick
1171          * @param {Roo.bootstrap.Container} this
1172          * @param {Roo.EventObject} e
1173          */
1174         "click" : true
1175     });
1176 };
1177
1178 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1179     
1180     jumbotron : false,
1181     well: '',
1182     panel : '',
1183     header: '',
1184     footer : '',
1185     sticky: '',
1186     tag : false,
1187     alert : false,
1188     fa: false,
1189     icon : false,
1190     expandable : false,
1191     rheader : '',
1192     expanded : true,
1193     clickable: false,
1194   
1195      
1196     getChildContainer : function() {
1197         
1198         if(!this.el){
1199             return false;
1200         }
1201         
1202         if (this.panel.length) {
1203             return this.el.select('.panel-body',true).first();
1204         }
1205         
1206         return this.el;
1207     },
1208     
1209     
1210     getAutoCreate : function(){
1211         
1212         var cfg = {
1213             tag : this.tag || 'div',
1214             html : '',
1215             cls : ''
1216         };
1217         if (this.jumbotron) {
1218             cfg.cls = 'jumbotron';
1219         }
1220         
1221         
1222         
1223         // - this is applied by the parent..
1224         //if (this.cls) {
1225         //    cfg.cls = this.cls + '';
1226         //}
1227         
1228         if (this.sticky.length) {
1229             
1230             var bd = Roo.get(document.body);
1231             if (!bd.hasClass('bootstrap-sticky')) {
1232                 bd.addClass('bootstrap-sticky');
1233                 Roo.select('html',true).setStyle('height', '100%');
1234             }
1235              
1236             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1237         }
1238         
1239         
1240         if (this.well.length) {
1241             switch (this.well) {
1242                 case 'lg':
1243                 case 'sm':
1244                     cfg.cls +=' well well-' +this.well;
1245                     break;
1246                 default:
1247                     cfg.cls +=' well';
1248                     break;
1249             }
1250         }
1251         
1252         if (this.hidden) {
1253             cfg.cls += ' hidden';
1254         }
1255         
1256         
1257         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1258             cfg.cls +=' alert alert-' + this.alert;
1259         }
1260         
1261         var body = cfg;
1262         
1263         if (this.panel.length) {
1264             cfg.cls += ' panel panel-' + this.panel;
1265             cfg.cn = [];
1266             if (this.header.length) {
1267                 
1268                 var h = [];
1269                 
1270                 if(this.expandable){
1271                     
1272                     cfg.cls = cfg.cls + ' expandable';
1273                     
1274                     h.push({
1275                         tag: 'i',
1276                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1277                     });
1278                     
1279                 }
1280                 
1281                 h.push(
1282                     {
1283                         tag: 'span',
1284                         cls : 'panel-title',
1285                         html : (this.expandable ? '&nbsp;' : '') + this.header
1286                     },
1287                     {
1288                         tag: 'span',
1289                         cls: 'panel-header-right',
1290                         html: this.rheader
1291                     }
1292                 );
1293                 
1294                 cfg.cn.push({
1295                     cls : 'panel-heading',
1296                     style : this.expandable ? 'cursor: pointer' : '',
1297                     cn : h
1298                 });
1299                 
1300             }
1301             
1302             body = false;
1303             cfg.cn.push({
1304                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1305                 html : this.html
1306             });
1307             
1308             
1309             if (this.footer.length) {
1310                 cfg.cn.push({
1311                     cls : 'panel-footer',
1312                     html : this.footer
1313                     
1314                 });
1315             }
1316             
1317         }
1318         
1319         if (body) {
1320             body.html = this.html || cfg.html;
1321             // prefix with the icons..
1322             if (this.fa) {
1323                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1324             }
1325             if (this.icon) {
1326                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1327             }
1328             
1329             
1330         }
1331         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1332             cfg.cls =  'container';
1333         }
1334         
1335         return cfg;
1336     },
1337     
1338     initEvents: function() 
1339     {
1340         if(this.expandable){
1341             var headerEl = this.headerEl();
1342         
1343             if(headerEl){
1344                 headerEl.on('click', this.onToggleClick, this);
1345             }
1346         }
1347         
1348         if(this.clickable){
1349             this.el.on('click', this.onClick, this);
1350         }
1351         
1352     },
1353     
1354     onToggleClick : function()
1355     {
1356         var headerEl = this.headerEl();
1357         
1358         if(!headerEl){
1359             return;
1360         }
1361         
1362         if(this.expanded){
1363             this.collapse();
1364             return;
1365         }
1366         
1367         this.expand();
1368     },
1369     
1370     expand : function()
1371     {
1372         if(this.fireEvent('expand', this)) {
1373             
1374             this.expanded = true;
1375             
1376             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1377             
1378             this.el.select('.panel-body',true).first().removeClass('hide');
1379             
1380             var toggleEl = this.toggleEl();
1381
1382             if(!toggleEl){
1383                 return;
1384             }
1385
1386             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1387         }
1388         
1389     },
1390     
1391     collapse : function()
1392     {
1393         if(this.fireEvent('collapse', this)) {
1394             
1395             this.expanded = false;
1396             
1397             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1398             this.el.select('.panel-body',true).first().addClass('hide');
1399         
1400             var toggleEl = this.toggleEl();
1401
1402             if(!toggleEl){
1403                 return;
1404             }
1405
1406             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1407         }
1408     },
1409     
1410     toggleEl : function()
1411     {
1412         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1413             return;
1414         }
1415         
1416         return this.el.select('.panel-heading .fa',true).first();
1417     },
1418     
1419     headerEl : function()
1420     {
1421         if(!this.el || !this.panel.length || !this.header.length){
1422             return;
1423         }
1424         
1425         return this.el.select('.panel-heading',true).first()
1426     },
1427     
1428     bodyEl : function()
1429     {
1430         if(!this.el || !this.panel.length){
1431             return;
1432         }
1433         
1434         return this.el.select('.panel-body',true).first()
1435     },
1436     
1437     titleEl : function()
1438     {
1439         if(!this.el || !this.panel.length || !this.header.length){
1440             return;
1441         }
1442         
1443         return this.el.select('.panel-title',true).first();
1444     },
1445     
1446     setTitle : function(v)
1447     {
1448         var titleEl = this.titleEl();
1449         
1450         if(!titleEl){
1451             return;
1452         }
1453         
1454         titleEl.dom.innerHTML = v;
1455     },
1456     
1457     getTitle : function()
1458     {
1459         
1460         var titleEl = this.titleEl();
1461         
1462         if(!titleEl){
1463             return '';
1464         }
1465         
1466         return titleEl.dom.innerHTML;
1467     },
1468     
1469     setRightTitle : function(v)
1470     {
1471         var t = this.el.select('.panel-header-right',true).first();
1472         
1473         if(!t){
1474             return;
1475         }
1476         
1477         t.dom.innerHTML = v;
1478     },
1479     
1480     onClick : function(e)
1481     {
1482         e.preventDefault();
1483         
1484         this.fireEvent('click', this, e);
1485     }
1486 });
1487
1488  /*
1489  * - LGPL
1490  *
1491  * image
1492  * 
1493  */
1494
1495
1496 /**
1497  * @class Roo.bootstrap.Img
1498  * @extends Roo.bootstrap.Component
1499  * Bootstrap Img class
1500  * @cfg {Boolean} imgResponsive false | true
1501  * @cfg {String} border rounded | circle | thumbnail
1502  * @cfg {String} src image source
1503  * @cfg {String} alt image alternative text
1504  * @cfg {String} href a tag href
1505  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1506  * @cfg {String} xsUrl xs image source
1507  * @cfg {String} smUrl sm image source
1508  * @cfg {String} mdUrl md image source
1509  * @cfg {String} lgUrl lg image source
1510  * 
1511  * @constructor
1512  * Create a new Input
1513  * @param {Object} config The config object
1514  */
1515
1516 Roo.bootstrap.Img = function(config){
1517     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1518     
1519     this.addEvents({
1520         // img events
1521         /**
1522          * @event click
1523          * The img click event for the img.
1524          * @param {Roo.EventObject} e
1525          */
1526         "click" : true
1527     });
1528 };
1529
1530 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1531     
1532     imgResponsive: true,
1533     border: '',
1534     src: 'about:blank',
1535     href: false,
1536     target: false,
1537     xsUrl: '',
1538     smUrl: '',
1539     mdUrl: '',
1540     lgUrl: '',
1541
1542     getAutoCreate : function()
1543     {   
1544         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1545             return this.createSingleImg();
1546         }
1547         
1548         var cfg = {
1549             tag: 'div',
1550             cls: 'roo-image-responsive-group',
1551             cn: []
1552         };
1553         var _this = this;
1554         
1555         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1556             
1557             if(!_this[size + 'Url']){
1558                 return;
1559             }
1560             
1561             var img = {
1562                 tag: 'img',
1563                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1564                 html: _this.html || cfg.html,
1565                 src: _this[size + 'Url']
1566             };
1567             
1568             img.cls += ' roo-image-responsive-' + size;
1569             
1570             var s = ['xs', 'sm', 'md', 'lg'];
1571             
1572             s.splice(s.indexOf(size), 1);
1573             
1574             Roo.each(s, function(ss){
1575                 img.cls += ' hidden-' + ss;
1576             });
1577             
1578             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1579                 cfg.cls += ' img-' + _this.border;
1580             }
1581             
1582             if(_this.alt){
1583                 cfg.alt = _this.alt;
1584             }
1585             
1586             if(_this.href){
1587                 var a = {
1588                     tag: 'a',
1589                     href: _this.href,
1590                     cn: [
1591                         img
1592                     ]
1593                 };
1594
1595                 if(this.target){
1596                     a.target = _this.target;
1597                 }
1598             }
1599             
1600             cfg.cn.push((_this.href) ? a : img);
1601             
1602         });
1603         
1604         return cfg;
1605     },
1606     
1607     createSingleImg : function()
1608     {
1609         var cfg = {
1610             tag: 'img',
1611             cls: (this.imgResponsive) ? 'img-responsive' : '',
1612             html : null,
1613             src : 'about:blank'  // just incase src get's set to undefined?!?
1614         };
1615         
1616         cfg.html = this.html || cfg.html;
1617         
1618         cfg.src = this.src || cfg.src;
1619         
1620         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1621             cfg.cls += ' img-' + this.border;
1622         }
1623         
1624         if(this.alt){
1625             cfg.alt = this.alt;
1626         }
1627         
1628         if(this.href){
1629             var a = {
1630                 tag: 'a',
1631                 href: this.href,
1632                 cn: [
1633                     cfg
1634                 ]
1635             };
1636             
1637             if(this.target){
1638                 a.target = this.target;
1639             }
1640             
1641         }
1642         
1643         return (this.href) ? a : cfg;
1644     },
1645     
1646     initEvents: function() 
1647     {
1648         if(!this.href){
1649             this.el.on('click', this.onClick, this);
1650         }
1651         
1652     },
1653     
1654     onClick : function(e)
1655     {
1656         Roo.log('img onclick');
1657         this.fireEvent('click', this, e);
1658     },
1659     /**
1660      * Sets the url of the image - used to update it
1661      * @param {String} url the url of the image
1662      */
1663     
1664     setSrc : function(url)
1665     {
1666         this.src =  url;
1667         
1668         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1669             this.el.dom.src =  url;
1670             return;
1671         }
1672         
1673         this.el.select('img', true).first().dom.src =  url;
1674     }
1675     
1676     
1677    
1678 });
1679
1680  /*
1681  * - LGPL
1682  *
1683  * image
1684  * 
1685  */
1686
1687
1688 /**
1689  * @class Roo.bootstrap.Link
1690  * @extends Roo.bootstrap.Component
1691  * Bootstrap Link Class
1692  * @cfg {String} alt image alternative text
1693  * @cfg {String} href a tag href
1694  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1695  * @cfg {String} html the content of the link.
1696  * @cfg {String} anchor name for the anchor link
1697  * @cfg {String} fa - favicon
1698
1699  * @cfg {Boolean} preventDefault (true | false) default false
1700
1701  * 
1702  * @constructor
1703  * Create a new Input
1704  * @param {Object} config The config object
1705  */
1706
1707 Roo.bootstrap.Link = function(config){
1708     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1709     
1710     this.addEvents({
1711         // img events
1712         /**
1713          * @event click
1714          * The img click event for the img.
1715          * @param {Roo.EventObject} e
1716          */
1717         "click" : true
1718     });
1719 };
1720
1721 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1722     
1723     href: false,
1724     target: false,
1725     preventDefault: false,
1726     anchor : false,
1727     alt : false,
1728     fa: false,
1729
1730
1731     getAutoCreate : function()
1732     {
1733         var html = this.html || '';
1734         
1735         if (this.fa !== false) {
1736             html = '<i class="fa fa-' + this.fa + '"></i>';
1737         }
1738         var cfg = {
1739             tag: 'a'
1740         };
1741         // anchor's do not require html/href...
1742         if (this.anchor === false) {
1743             cfg.html = html;
1744             cfg.href = this.href || '#';
1745         } else {
1746             cfg.name = this.anchor;
1747             if (this.html !== false || this.fa !== false) {
1748                 cfg.html = html;
1749             }
1750             if (this.href !== false) {
1751                 cfg.href = this.href;
1752             }
1753         }
1754         
1755         if(this.alt !== false){
1756             cfg.alt = this.alt;
1757         }
1758         
1759         
1760         if(this.target !== false) {
1761             cfg.target = this.target;
1762         }
1763         
1764         return cfg;
1765     },
1766     
1767     initEvents: function() {
1768         
1769         if(!this.href || this.preventDefault){
1770             this.el.on('click', this.onClick, this);
1771         }
1772     },
1773     
1774     onClick : function(e)
1775     {
1776         if(this.preventDefault){
1777             e.preventDefault();
1778         }
1779         //Roo.log('img onclick');
1780         this.fireEvent('click', this, e);
1781     }
1782    
1783 });
1784
1785  /*
1786  * - LGPL
1787  *
1788  * header
1789  * 
1790  */
1791
1792 /**
1793  * @class Roo.bootstrap.Header
1794  * @extends Roo.bootstrap.Component
1795  * Bootstrap Header class
1796  * @cfg {String} html content of header
1797  * @cfg {Number} level (1|2|3|4|5|6) default 1
1798  * 
1799  * @constructor
1800  * Create a new Header
1801  * @param {Object} config The config object
1802  */
1803
1804
1805 Roo.bootstrap.Header  = function(config){
1806     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1807 };
1808
1809 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1810     
1811     //href : false,
1812     html : false,
1813     level : 1,
1814     
1815     
1816     
1817     getAutoCreate : function(){
1818         
1819         
1820         
1821         var cfg = {
1822             tag: 'h' + (1 *this.level),
1823             html: this.html || ''
1824         } ;
1825         
1826         return cfg;
1827     }
1828    
1829 });
1830
1831  
1832
1833  /*
1834  * Based on:
1835  * Ext JS Library 1.1.1
1836  * Copyright(c) 2006-2007, Ext JS, LLC.
1837  *
1838  * Originally Released Under LGPL - original licence link has changed is not relivant.
1839  *
1840  * Fork - LGPL
1841  * <script type="text/javascript">
1842  */
1843  
1844 /**
1845  * @class Roo.bootstrap.MenuMgr
1846  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1847  * @singleton
1848  */
1849 Roo.bootstrap.MenuMgr = function(){
1850    var menus, active, groups = {}, attached = false, lastShow = new Date();
1851
1852    // private - called when first menu is created
1853    function init(){
1854        menus = {};
1855        active = new Roo.util.MixedCollection();
1856        Roo.get(document).addKeyListener(27, function(){
1857            if(active.length > 0){
1858                hideAll();
1859            }
1860        });
1861    }
1862
1863    // private
1864    function hideAll(){
1865        if(active && active.length > 0){
1866            var c = active.clone();
1867            c.each(function(m){
1868                m.hide();
1869            });
1870        }
1871    }
1872
1873    // private
1874    function onHide(m){
1875        active.remove(m);
1876        if(active.length < 1){
1877            Roo.get(document).un("mouseup", onMouseDown);
1878             
1879            attached = false;
1880        }
1881    }
1882
1883    // private
1884    function onShow(m){
1885        var last = active.last();
1886        lastShow = new Date();
1887        active.add(m);
1888        if(!attached){
1889           Roo.get(document).on("mouseup", onMouseDown);
1890            
1891            attached = true;
1892        }
1893        if(m.parentMenu){
1894           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1895           m.parentMenu.activeChild = m;
1896        }else if(last && last.isVisible()){
1897           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1898        }
1899    }
1900
1901    // private
1902    function onBeforeHide(m){
1903        if(m.activeChild){
1904            m.activeChild.hide();
1905        }
1906        if(m.autoHideTimer){
1907            clearTimeout(m.autoHideTimer);
1908            delete m.autoHideTimer;
1909        }
1910    }
1911
1912    // private
1913    function onBeforeShow(m){
1914        var pm = m.parentMenu;
1915        if(!pm && !m.allowOtherMenus){
1916            hideAll();
1917        }else if(pm && pm.activeChild && active != m){
1918            pm.activeChild.hide();
1919        }
1920    }
1921
1922    // private this should really trigger on mouseup..
1923    function onMouseDown(e){
1924         Roo.log("on Mouse Up");
1925         
1926         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1927             Roo.log("MenuManager hideAll");
1928             hideAll();
1929             e.stopEvent();
1930         }
1931         
1932         
1933    }
1934
1935    // private
1936    function onBeforeCheck(mi, state){
1937        if(state){
1938            var g = groups[mi.group];
1939            for(var i = 0, l = g.length; i < l; i++){
1940                if(g[i] != mi){
1941                    g[i].setChecked(false);
1942                }
1943            }
1944        }
1945    }
1946
1947    return {
1948
1949        /**
1950         * Hides all menus that are currently visible
1951         */
1952        hideAll : function(){
1953             hideAll();  
1954        },
1955
1956        // private
1957        register : function(menu){
1958            if(!menus){
1959                init();
1960            }
1961            menus[menu.id] = menu;
1962            menu.on("beforehide", onBeforeHide);
1963            menu.on("hide", onHide);
1964            menu.on("beforeshow", onBeforeShow);
1965            menu.on("show", onShow);
1966            var g = menu.group;
1967            if(g && menu.events["checkchange"]){
1968                if(!groups[g]){
1969                    groups[g] = [];
1970                }
1971                groups[g].push(menu);
1972                menu.on("checkchange", onCheck);
1973            }
1974        },
1975
1976         /**
1977          * Returns a {@link Roo.menu.Menu} object
1978          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1979          * be used to generate and return a new Menu instance.
1980          */
1981        get : function(menu){
1982            if(typeof menu == "string"){ // menu id
1983                return menus[menu];
1984            }else if(menu.events){  // menu instance
1985                return menu;
1986            }
1987            /*else if(typeof menu.length == 'number'){ // array of menu items?
1988                return new Roo.bootstrap.Menu({items:menu});
1989            }else{ // otherwise, must be a config
1990                return new Roo.bootstrap.Menu(menu);
1991            }
1992            */
1993            return false;
1994        },
1995
1996        // private
1997        unregister : function(menu){
1998            delete menus[menu.id];
1999            menu.un("beforehide", onBeforeHide);
2000            menu.un("hide", onHide);
2001            menu.un("beforeshow", onBeforeShow);
2002            menu.un("show", onShow);
2003            var g = menu.group;
2004            if(g && menu.events["checkchange"]){
2005                groups[g].remove(menu);
2006                menu.un("checkchange", onCheck);
2007            }
2008        },
2009
2010        // private
2011        registerCheckable : function(menuItem){
2012            var g = menuItem.group;
2013            if(g){
2014                if(!groups[g]){
2015                    groups[g] = [];
2016                }
2017                groups[g].push(menuItem);
2018                menuItem.on("beforecheckchange", onBeforeCheck);
2019            }
2020        },
2021
2022        // private
2023        unregisterCheckable : function(menuItem){
2024            var g = menuItem.group;
2025            if(g){
2026                groups[g].remove(menuItem);
2027                menuItem.un("beforecheckchange", onBeforeCheck);
2028            }
2029        }
2030    };
2031 }();/*
2032  * - LGPL
2033  *
2034  * menu
2035  * 
2036  */
2037
2038 /**
2039  * @class Roo.bootstrap.Menu
2040  * @extends Roo.bootstrap.Component
2041  * Bootstrap Menu class - container for MenuItems
2042  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2043  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2044  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2045  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2046  * 
2047  * @constructor
2048  * Create a new Menu
2049  * @param {Object} config The config object
2050  */
2051
2052
2053 Roo.bootstrap.Menu = function(config){
2054     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2055     if (this.registerMenu && this.type != 'treeview')  {
2056         Roo.bootstrap.MenuMgr.register(this);
2057     }
2058     
2059     
2060     this.addEvents({
2061         /**
2062          * @event beforeshow
2063          * Fires before this menu is displayed
2064          * @param {Roo.menu.Menu} this
2065          */
2066         beforeshow : true,
2067         /**
2068          * @event beforehide
2069          * Fires before this menu is hidden
2070          * @param {Roo.menu.Menu} this
2071          */
2072         beforehide : true,
2073         /**
2074          * @event show
2075          * Fires after this menu is displayed
2076          * @param {Roo.menu.Menu} this
2077          */
2078         show : true,
2079         /**
2080          * @event hide
2081          * Fires after this menu is hidden
2082          * @param {Roo.menu.Menu} this
2083          */
2084         hide : true,
2085         /**
2086          * @event click
2087          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2088          * @param {Roo.menu.Menu} this
2089          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2090          * @param {Roo.EventObject} e
2091          */
2092         click : true,
2093         /**
2094          * @event mouseover
2095          * Fires when the mouse is hovering over this menu
2096          * @param {Roo.menu.Menu} this
2097          * @param {Roo.EventObject} e
2098          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2099          */
2100         mouseover : true,
2101         /**
2102          * @event mouseout
2103          * Fires when the mouse exits this menu
2104          * @param {Roo.menu.Menu} this
2105          * @param {Roo.EventObject} e
2106          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2107          */
2108         mouseout : true,
2109         /**
2110          * @event itemclick
2111          * Fires when a menu item contained in this menu is clicked
2112          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2113          * @param {Roo.EventObject} e
2114          */
2115         itemclick: true
2116     });
2117     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2118 };
2119
2120 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2121     
2122    /// html : false,
2123     //align : '',
2124     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2125     type: false,
2126     /**
2127      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2128      */
2129     registerMenu : true,
2130     
2131     menuItems :false, // stores the menu items..
2132     
2133     hidden:true,
2134         
2135     parentMenu : false,
2136     
2137     stopEvent : true,
2138     
2139     isLink : false,
2140     
2141     getChildContainer : function() {
2142         return this.el;  
2143     },
2144     
2145     getAutoCreate : function(){
2146          
2147         //if (['right'].indexOf(this.align)!==-1) {
2148         //    cfg.cn[1].cls += ' pull-right'
2149         //}
2150         
2151         
2152         var cfg = {
2153             tag : 'ul',
2154             cls : 'dropdown-menu' ,
2155             style : 'z-index:1000'
2156             
2157         };
2158         
2159         if (this.type === 'submenu') {
2160             cfg.cls = 'submenu active';
2161         }
2162         if (this.type === 'treeview') {
2163             cfg.cls = 'treeview-menu';
2164         }
2165         
2166         return cfg;
2167     },
2168     initEvents : function() {
2169         
2170        // Roo.log("ADD event");
2171        // Roo.log(this.triggerEl.dom);
2172         
2173         this.triggerEl.on('click', this.onTriggerClick, this);
2174         
2175         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2176         
2177         
2178         if (this.triggerEl.hasClass('nav-item')) {
2179             // dropdown toggle on the 'a' in BS4?
2180             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2181         } else {
2182             this.triggerEl.addClass('dropdown-toggle');
2183         }
2184         if (Roo.isTouch) {
2185             this.el.on('touchstart'  , this.onTouch, this);
2186         }
2187         this.el.on('click' , this.onClick, this);
2188
2189         this.el.on("mouseover", this.onMouseOver, this);
2190         this.el.on("mouseout", this.onMouseOut, this);
2191         
2192     },
2193     
2194     findTargetItem : function(e)
2195     {
2196         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2197         if(!t){
2198             return false;
2199         }
2200         //Roo.log(t);         Roo.log(t.id);
2201         if(t && t.id){
2202             //Roo.log(this.menuitems);
2203             return this.menuitems.get(t.id);
2204             
2205             //return this.items.get(t.menuItemId);
2206         }
2207         
2208         return false;
2209     },
2210     
2211     onTouch : function(e) 
2212     {
2213         Roo.log("menu.onTouch");
2214         //e.stopEvent(); this make the user popdown broken
2215         this.onClick(e);
2216     },
2217     
2218     onClick : function(e)
2219     {
2220         Roo.log("menu.onClick");
2221         
2222         var t = this.findTargetItem(e);
2223         if(!t || t.isContainer){
2224             return;
2225         }
2226         Roo.log(e);
2227         /*
2228         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2229             if(t == this.activeItem && t.shouldDeactivate(e)){
2230                 this.activeItem.deactivate();
2231                 delete this.activeItem;
2232                 return;
2233             }
2234             if(t.canActivate){
2235                 this.setActiveItem(t, true);
2236             }
2237             return;
2238             
2239             
2240         }
2241         */
2242        
2243         Roo.log('pass click event');
2244         
2245         t.onClick(e);
2246         
2247         this.fireEvent("click", this, t, e);
2248         
2249         var _this = this;
2250         
2251         if(!t.href.length || t.href == '#'){
2252             (function() { _this.hide(); }).defer(100);
2253         }
2254         
2255     },
2256     
2257     onMouseOver : function(e){
2258         var t  = this.findTargetItem(e);
2259         //Roo.log(t);
2260         //if(t){
2261         //    if(t.canActivate && !t.disabled){
2262         //        this.setActiveItem(t, true);
2263         //    }
2264         //}
2265         
2266         this.fireEvent("mouseover", this, e, t);
2267     },
2268     isVisible : function(){
2269         return !this.hidden;
2270     },
2271      onMouseOut : function(e){
2272         var t  = this.findTargetItem(e);
2273         
2274         //if(t ){
2275         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2276         //        this.activeItem.deactivate();
2277         //        delete this.activeItem;
2278         //    }
2279         //}
2280         this.fireEvent("mouseout", this, e, t);
2281     },
2282     
2283     
2284     /**
2285      * Displays this menu relative to another element
2286      * @param {String/HTMLElement/Roo.Element} element The element to align to
2287      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2288      * the element (defaults to this.defaultAlign)
2289      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2290      */
2291     show : function(el, pos, parentMenu){
2292         this.parentMenu = parentMenu;
2293         if(!this.el){
2294             this.render();
2295         }
2296         this.fireEvent("beforeshow", this);
2297         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2298     },
2299      /**
2300      * Displays this menu at a specific xy position
2301      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2302      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2303      */
2304     showAt : function(xy, parentMenu, /* private: */_e){
2305         this.parentMenu = parentMenu;
2306         if(!this.el){
2307             this.render();
2308         }
2309         if(_e !== false){
2310             this.fireEvent("beforeshow", this);
2311             //xy = this.el.adjustForConstraints(xy);
2312         }
2313         
2314         //this.el.show();
2315         this.hideMenuItems();
2316         this.hidden = false;
2317         this.triggerEl.addClass('open');
2318         this.el.addClass('show');
2319         
2320         // reassign x when hitting right
2321         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2322             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2323         }
2324         
2325         // reassign y when hitting bottom
2326         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2327             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2328         }
2329         
2330         // but the list may align on trigger left or trigger top... should it be a properity?
2331         
2332         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2333             this.el.setXY(xy);
2334         }
2335         
2336         this.focus();
2337         this.fireEvent("show", this);
2338     },
2339     
2340     focus : function(){
2341         return;
2342         if(!this.hidden){
2343             this.doFocus.defer(50, this);
2344         }
2345     },
2346
2347     doFocus : function(){
2348         if(!this.hidden){
2349             this.focusEl.focus();
2350         }
2351     },
2352
2353     /**
2354      * Hides this menu and optionally all parent menus
2355      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2356      */
2357     hide : function(deep)
2358     {
2359         
2360         this.hideMenuItems();
2361         if(this.el && this.isVisible()){
2362             this.fireEvent("beforehide", this);
2363             if(this.activeItem){
2364                 this.activeItem.deactivate();
2365                 this.activeItem = null;
2366             }
2367             this.triggerEl.removeClass('open');;
2368             this.el.removeClass('show');
2369             this.hidden = true;
2370             this.fireEvent("hide", this);
2371         }
2372         if(deep === true && this.parentMenu){
2373             this.parentMenu.hide(true);
2374         }
2375     },
2376     
2377     onTriggerClick : function(e)
2378     {
2379         Roo.log('trigger click');
2380         
2381         var target = e.getTarget();
2382         
2383         Roo.log(target.nodeName.toLowerCase());
2384         
2385         if(target.nodeName.toLowerCase() === 'i'){
2386             e.preventDefault();
2387         }
2388         
2389     },
2390     
2391     onTriggerPress  : function(e)
2392     {
2393         Roo.log('trigger press');
2394         //Roo.log(e.getTarget());
2395        // Roo.log(this.triggerEl.dom);
2396        
2397         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2398         var pel = Roo.get(e.getTarget());
2399         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2400             Roo.log('is treeview or dropdown?');
2401             return;
2402         }
2403         
2404         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2405             return;
2406         }
2407         
2408         if (this.isVisible()) {
2409             Roo.log('hide');
2410             this.hide();
2411         } else {
2412             Roo.log('show');
2413             this.show(this.triggerEl, '?', false);
2414         }
2415         
2416         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2417             e.stopEvent();
2418         }
2419         
2420     },
2421        
2422     
2423     hideMenuItems : function()
2424     {
2425         Roo.log("hide Menu Items");
2426         if (!this.el) { 
2427             return;
2428         }
2429         //$(backdrop).remove()
2430         this.el.select('.open',true).each(function(aa) {
2431             
2432             aa.removeClass('open');
2433           //var parent = getParent($(this))
2434           //var relatedTarget = { relatedTarget: this }
2435           
2436            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2437           //if (e.isDefaultPrevented()) return
2438            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2439         });
2440     },
2441     addxtypeChild : function (tree, cntr) {
2442         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2443           
2444         this.menuitems.add(comp);
2445         return comp;
2446
2447     },
2448     getEl : function()
2449     {
2450         Roo.log(this.el);
2451         return this.el;
2452     },
2453     
2454     clear : function()
2455     {
2456         this.getEl().dom.innerHTML = '';
2457         this.menuitems.clear();
2458     }
2459 });
2460
2461  
2462  /*
2463  * - LGPL
2464  *
2465  * menu item
2466  * 
2467  */
2468
2469
2470 /**
2471  * @class Roo.bootstrap.MenuItem
2472  * @extends Roo.bootstrap.Component
2473  * Bootstrap MenuItem class
2474  * @cfg {String} html the menu label
2475  * @cfg {String} href the link
2476  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2477  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2478  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2479  * @cfg {String} fa favicon to show on left of menu item.
2480  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2481  * 
2482  * 
2483  * @constructor
2484  * Create a new MenuItem
2485  * @param {Object} config The config object
2486  */
2487
2488
2489 Roo.bootstrap.MenuItem = function(config){
2490     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2491     this.addEvents({
2492         // raw events
2493         /**
2494          * @event click
2495          * The raw click event for the entire grid.
2496          * @param {Roo.bootstrap.MenuItem} this
2497          * @param {Roo.EventObject} e
2498          */
2499         "click" : true
2500     });
2501 };
2502
2503 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2504     
2505     href : false,
2506     html : false,
2507     preventDefault: false,
2508     isContainer : false,
2509     active : false,
2510     fa: false,
2511     
2512     getAutoCreate : function(){
2513         
2514         if(this.isContainer){
2515             return {
2516                 tag: 'li',
2517                 cls: 'dropdown-menu-item '
2518             };
2519         }
2520         var ctag = {
2521             tag: 'span',
2522             html: 'Link'
2523         };
2524         
2525         var anc = {
2526             tag : 'a',
2527             cls : 'dropdown-item',
2528             href : '#',
2529             cn : [  ]
2530         };
2531         
2532         if (this.fa !== false) {
2533             anc.cn.push({
2534                 tag : 'i',
2535                 cls : 'fa fa-' + this.fa
2536             });
2537         }
2538         
2539         anc.cn.push(ctag);
2540         
2541         
2542         var cfg= {
2543             tag: 'li',
2544             cls: 'dropdown-menu-item',
2545             cn: [ anc ]
2546         };
2547         if (this.parent().type == 'treeview') {
2548             cfg.cls = 'treeview-menu';
2549         }
2550         if (this.active) {
2551             cfg.cls += ' active';
2552         }
2553         
2554         
2555         
2556         anc.href = this.href || cfg.cn[0].href ;
2557         ctag.html = this.html || cfg.cn[0].html ;
2558         return cfg;
2559     },
2560     
2561     initEvents: function()
2562     {
2563         if (this.parent().type == 'treeview') {
2564             this.el.select('a').on('click', this.onClick, this);
2565         }
2566         
2567         if (this.menu) {
2568             this.menu.parentType = this.xtype;
2569             this.menu.triggerEl = this.el;
2570             this.menu = this.addxtype(Roo.apply({}, this.menu));
2571         }
2572         
2573     },
2574     onClick : function(e)
2575     {
2576         Roo.log('item on click ');
2577         
2578         if(this.preventDefault){
2579             e.preventDefault();
2580         }
2581         //this.parent().hideMenuItems();
2582         
2583         this.fireEvent('click', this, e);
2584     },
2585     getEl : function()
2586     {
2587         return this.el;
2588     } 
2589 });
2590
2591  
2592
2593  /*
2594  * - LGPL
2595  *
2596  * menu separator
2597  * 
2598  */
2599
2600
2601 /**
2602  * @class Roo.bootstrap.MenuSeparator
2603  * @extends Roo.bootstrap.Component
2604  * Bootstrap MenuSeparator class
2605  * 
2606  * @constructor
2607  * Create a new MenuItem
2608  * @param {Object} config The config object
2609  */
2610
2611
2612 Roo.bootstrap.MenuSeparator = function(config){
2613     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2614 };
2615
2616 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2617     
2618     getAutoCreate : function(){
2619         var cfg = {
2620             cls: 'divider',
2621             tag : 'li'
2622         };
2623         
2624         return cfg;
2625     }
2626    
2627 });
2628
2629  
2630
2631  
2632 /*
2633 * Licence: LGPL
2634 */
2635
2636 /**
2637  * @class Roo.bootstrap.Modal
2638  * @extends Roo.bootstrap.Component
2639  * Bootstrap Modal class
2640  * @cfg {String} title Title of dialog
2641  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2642  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2643  * @cfg {Boolean} specificTitle default false
2644  * @cfg {Array} buttons Array of buttons or standard button set..
2645  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2646  * @cfg {Boolean} animate default true
2647  * @cfg {Boolean} allow_close default true
2648  * @cfg {Boolean} fitwindow default false
2649  * @cfg {String} size (sm|lg) default empty
2650  * @cfg {Number} max_width set the max width of modal
2651  *
2652  *
2653  * @constructor
2654  * Create a new Modal Dialog
2655  * @param {Object} config The config object
2656  */
2657
2658 Roo.bootstrap.Modal = function(config){
2659     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2660     this.addEvents({
2661         // raw events
2662         /**
2663          * @event btnclick
2664          * The raw btnclick event for the button
2665          * @param {Roo.EventObject} e
2666          */
2667         "btnclick" : true,
2668         /**
2669          * @event resize
2670          * Fire when dialog resize
2671          * @param {Roo.bootstrap.Modal} this
2672          * @param {Roo.EventObject} e
2673          */
2674         "resize" : true
2675     });
2676     this.buttons = this.buttons || [];
2677
2678     if (this.tmpl) {
2679         this.tmpl = Roo.factory(this.tmpl);
2680     }
2681
2682 };
2683
2684 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2685
2686     title : 'test dialog',
2687
2688     buttons : false,
2689
2690     // set on load...
2691
2692     html: false,
2693
2694     tmp: false,
2695
2696     specificTitle: false,
2697
2698     buttonPosition: 'right',
2699
2700     allow_close : true,
2701
2702     animate : true,
2703
2704     fitwindow: false,
2705     
2706      // private
2707     dialogEl: false,
2708     bodyEl:  false,
2709     footerEl:  false,
2710     titleEl:  false,
2711     closeEl:  false,
2712
2713     size: '',
2714     
2715     max_width: 0,
2716     
2717     max_height: 0,
2718     
2719     fit_content: false,
2720
2721     onRender : function(ct, position)
2722     {
2723         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2724
2725         if(!this.el){
2726             var cfg = Roo.apply({},  this.getAutoCreate());
2727             cfg.id = Roo.id();
2728             //if(!cfg.name){
2729             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2730             //}
2731             //if (!cfg.name.length) {
2732             //    delete cfg.name;
2733            // }
2734             if (this.cls) {
2735                 cfg.cls += ' ' + this.cls;
2736             }
2737             if (this.style) {
2738                 cfg.style = this.style;
2739             }
2740             this.el = Roo.get(document.body).createChild(cfg, position);
2741         }
2742         //var type = this.el.dom.type;
2743
2744
2745         if(this.tabIndex !== undefined){
2746             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2747         }
2748
2749         this.dialogEl = this.el.select('.modal-dialog',true).first();
2750         this.bodyEl = this.el.select('.modal-body',true).first();
2751         this.closeEl = this.el.select('.modal-header .close', true).first();
2752         this.headerEl = this.el.select('.modal-header',true).first();
2753         this.titleEl = this.el.select('.modal-title',true).first();
2754         this.footerEl = this.el.select('.modal-footer',true).first();
2755
2756         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2757         
2758         //this.el.addClass("x-dlg-modal");
2759
2760         if (this.buttons.length) {
2761             Roo.each(this.buttons, function(bb) {
2762                 var b = Roo.apply({}, bb);
2763                 b.xns = b.xns || Roo.bootstrap;
2764                 b.xtype = b.xtype || 'Button';
2765                 if (typeof(b.listeners) == 'undefined') {
2766                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2767                 }
2768
2769                 var btn = Roo.factory(b);
2770
2771                 btn.render(this.getButtonContainer());
2772
2773             },this);
2774         }
2775         // render the children.
2776         var nitems = [];
2777
2778         if(typeof(this.items) != 'undefined'){
2779             var items = this.items;
2780             delete this.items;
2781
2782             for(var i =0;i < items.length;i++) {
2783                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2784             }
2785         }
2786
2787         this.items = nitems;
2788
2789         // where are these used - they used to be body/close/footer
2790
2791
2792         this.initEvents();
2793         //this.el.addClass([this.fieldClass, this.cls]);
2794
2795     },
2796
2797     getAutoCreate : function()
2798     {
2799         var bdy = {
2800                 cls : 'modal-body',
2801                 html : this.html || ''
2802         };
2803
2804         var title = {
2805             tag: 'h4',
2806             cls : 'modal-title',
2807             html : this.title
2808         };
2809
2810         if(this.specificTitle){
2811             title = this.title;
2812
2813         };
2814
2815         var header = [];
2816         if (this.allow_close && Roo.bootstrap.version == 3) {
2817             header.push({
2818                 tag: 'button',
2819                 cls : 'close',
2820                 html : '&times'
2821             });
2822         }
2823
2824         header.push(title);
2825
2826         if (this.allow_close && Roo.bootstrap.version == 4) {
2827             header.push({
2828                 tag: 'button',
2829                 cls : 'close',
2830                 html : '&times'
2831             });
2832         }
2833         
2834         var size = '';
2835
2836         if(this.size.length){
2837             size = 'modal-' + this.size;
2838         }
2839         
2840         var footer = Roo.bootstrap.version == 3 ?
2841             {
2842                 cls : 'modal-footer',
2843                 cn : [
2844                     {
2845                         tag: 'div',
2846                         cls: 'btn-' + this.buttonPosition
2847                     }
2848                 ]
2849
2850             } :
2851             {  // BS4 uses mr-auto on left buttons....
2852                 cls : 'modal-footer'
2853             };
2854
2855             
2856
2857         
2858         
2859         var modal = {
2860             cls: "modal",
2861              cn : [
2862                 {
2863                     cls: "modal-dialog " + size,
2864                     cn : [
2865                         {
2866                             cls : "modal-content",
2867                             cn : [
2868                                 {
2869                                     cls : 'modal-header',
2870                                     cn : header
2871                                 },
2872                                 bdy,
2873                                 footer
2874                             ]
2875
2876                         }
2877                     ]
2878
2879                 }
2880             ]
2881         };
2882
2883         if(this.animate){
2884             modal.cls += ' fade';
2885         }
2886
2887         return modal;
2888
2889     },
2890     getChildContainer : function() {
2891
2892          return this.bodyEl;
2893
2894     },
2895     getButtonContainer : function() {
2896         
2897          return Roo.bootstrap.version == 4 ?
2898             this.el.select('.modal-footer',true).first()
2899             : this.el.select('.modal-footer div',true).first();
2900
2901     },
2902     initEvents : function()
2903     {
2904         if (this.allow_close) {
2905             this.closeEl.on('click', this.hide, this);
2906         }
2907         Roo.EventManager.onWindowResize(this.resize, this, true);
2908
2909
2910     },
2911
2912     resize : function()
2913     {
2914         this.maskEl.setSize(
2915             Roo.lib.Dom.getViewWidth(true),
2916             Roo.lib.Dom.getViewHeight(true)
2917         );
2918         
2919         if (this.fitwindow) {
2920             this.setSize(
2921                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2922                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2923             );
2924             return;
2925         }
2926         
2927         if(this.max_width !== 0) {
2928             
2929             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2930             
2931             if(this.height) {
2932                 this.setSize(w, this.height);
2933                 return;
2934             }
2935             
2936             if(this.max_height) {
2937                 this.setSize(w,Math.min(
2938                     this.max_height,
2939                     Roo.lib.Dom.getViewportHeight(true) - 60
2940                 ));
2941                 
2942                 return;
2943             }
2944             
2945             if(!this.fit_content) {
2946                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2947                 return;
2948             }
2949             
2950             this.setSize(w, Math.min(
2951                 60 +
2952                 this.headerEl.getHeight() + 
2953                 this.footerEl.getHeight() + 
2954                 this.getChildHeight(this.bodyEl.dom.childNodes),
2955                 Roo.lib.Dom.getViewportHeight(true) - 60)
2956             );
2957         }
2958         
2959     },
2960
2961     setSize : function(w,h)
2962     {
2963         if (!w && !h) {
2964             return;
2965         }
2966         
2967         this.resizeTo(w,h);
2968     },
2969
2970     show : function() {
2971
2972         if (!this.rendered) {
2973             this.render();
2974         }
2975
2976         //this.el.setStyle('display', 'block');
2977         this.el.removeClass('hideing');
2978         this.el.dom.style.display='block';
2979         
2980         Roo.get(document.body).addClass('modal-open');
2981  
2982         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2983             var _this = this;
2984             (function(){
2985                 this.el.addClass('show');
2986                 this.el.addClass('in');
2987             }).defer(50, this);
2988         }else{
2989             this.el.addClass('show');
2990             this.el.addClass('in');
2991         }
2992
2993         // not sure how we can show data in here..
2994         //if (this.tmpl) {
2995         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2996         //}
2997
2998         Roo.get(document.body).addClass("x-body-masked");
2999         
3000         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
3001         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3002         this.maskEl.dom.style.display = 'block';
3003         this.maskEl.addClass('show');
3004         
3005         
3006         this.resize();
3007         
3008         this.fireEvent('show', this);
3009
3010         // set zindex here - otherwise it appears to be ignored...
3011         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3012
3013         (function () {
3014             this.items.forEach( function(e) {
3015                 e.layout ? e.layout() : false;
3016
3017             });
3018         }).defer(100,this);
3019
3020     },
3021     hide : function()
3022     {
3023         if(this.fireEvent("beforehide", this) !== false){
3024             
3025             this.maskEl.removeClass('show');
3026             
3027             this.maskEl.dom.style.display = '';
3028             Roo.get(document.body).removeClass("x-body-masked");
3029             this.el.removeClass('in');
3030             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3031
3032             if(this.animate){ // why
3033                 this.el.addClass('hideing');
3034                 this.el.removeClass('show');
3035                 (function(){
3036                     if (!this.el.hasClass('hideing')) {
3037                         return; // it's been shown again...
3038                     }
3039                     
3040                     this.el.dom.style.display='';
3041
3042                     Roo.get(document.body).removeClass('modal-open');
3043                     this.el.removeClass('hideing');
3044                 }).defer(150,this);
3045                 
3046             }else{
3047                 this.el.removeClass('show');
3048                 this.el.dom.style.display='';
3049                 Roo.get(document.body).removeClass('modal-open');
3050
3051             }
3052             this.fireEvent('hide', this);
3053         }
3054     },
3055     isVisible : function()
3056     {
3057         
3058         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3059         
3060     },
3061
3062     addButton : function(str, cb)
3063     {
3064
3065
3066         var b = Roo.apply({}, { html : str } );
3067         b.xns = b.xns || Roo.bootstrap;
3068         b.xtype = b.xtype || 'Button';
3069         if (typeof(b.listeners) == 'undefined') {
3070             b.listeners = { click : cb.createDelegate(this)  };
3071         }
3072
3073         var btn = Roo.factory(b);
3074
3075         btn.render(this.getButtonContainer());
3076
3077         return btn;
3078
3079     },
3080
3081     setDefaultButton : function(btn)
3082     {
3083         //this.el.select('.modal-footer').()
3084     },
3085     diff : false,
3086
3087     resizeTo: function(w,h)
3088     {
3089         // skip.. ?? why??
3090
3091         this.dialogEl.setWidth(w);
3092         if (this.diff === false) {
3093             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
3094         }
3095
3096         this.bodyEl.setHeight(h - this.diff);
3097
3098         this.fireEvent('resize', this);
3099
3100     },
3101     setContentSize  : function(w, h)
3102     {
3103
3104     },
3105     onButtonClick: function(btn,e)
3106     {
3107         //Roo.log([a,b,c]);
3108         this.fireEvent('btnclick', btn.name, e);
3109     },
3110      /**
3111      * Set the title of the Dialog
3112      * @param {String} str new Title
3113      */
3114     setTitle: function(str) {
3115         this.titleEl.dom.innerHTML = str;
3116     },
3117     /**
3118      * Set the body of the Dialog
3119      * @param {String} str new Title
3120      */
3121     setBody: function(str) {
3122         this.bodyEl.dom.innerHTML = str;
3123     },
3124     /**
3125      * Set the body of the Dialog using the template
3126      * @param {Obj} data - apply this data to the template and replace the body contents.
3127      */
3128     applyBody: function(obj)
3129     {
3130         if (!this.tmpl) {
3131             Roo.log("Error - using apply Body without a template");
3132             //code
3133         }
3134         this.tmpl.overwrite(this.bodyEl, obj);
3135     },
3136     
3137     getChildHeight : function(child_nodes)
3138     {
3139         if(
3140             !child_nodes ||
3141             child_nodes.length == 0
3142         ) {
3143             return;
3144         }
3145         
3146         var child_height = 0;
3147         
3148         for(var i = 0; i < child_nodes.length; i++) {
3149             
3150             /*
3151             * for modal with tabs...
3152             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3153                 
3154                 var layout_childs = child_nodes[i].childNodes;
3155                 
3156                 for(var j = 0; j < layout_childs.length; j++) {
3157                     
3158                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3159                         
3160                         var layout_body_childs = layout_childs[j].childNodes;
3161                         
3162                         for(var k = 0; k < layout_body_childs.length; k++) {
3163                             
3164                             if(layout_body_childs[k].classList.contains('navbar')) {
3165                                 child_height += layout_body_childs[k].offsetHeight;
3166                                 continue;
3167                             }
3168                             
3169                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3170                                 
3171                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3172                                 
3173                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3174                                     
3175                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3176                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3177                                         continue;
3178                                     }
3179                                     
3180                                 }
3181                                 
3182                             }
3183                             
3184                         }
3185                     }
3186                 }
3187                 continue;
3188             }
3189             */
3190             
3191             child_height += child_nodes[i].offsetHeight;
3192             // Roo.log(child_nodes[i].offsetHeight);
3193         }
3194         
3195         return child_height;
3196     }
3197
3198 });
3199
3200
3201 Roo.apply(Roo.bootstrap.Modal,  {
3202     /**
3203          * Button config that displays a single OK button
3204          * @type Object
3205          */
3206         OK :  [{
3207             name : 'ok',
3208             weight : 'primary',
3209             html : 'OK'
3210         }],
3211         /**
3212          * Button config that displays Yes and No buttons
3213          * @type Object
3214          */
3215         YESNO : [
3216             {
3217                 name  : 'no',
3218                 html : 'No'
3219             },
3220             {
3221                 name  :'yes',
3222                 weight : 'primary',
3223                 html : 'Yes'
3224             }
3225         ],
3226
3227         /**
3228          * Button config that displays OK and Cancel buttons
3229          * @type Object
3230          */
3231         OKCANCEL : [
3232             {
3233                name : 'cancel',
3234                 html : 'Cancel'
3235             },
3236             {
3237                 name : 'ok',
3238                 weight : 'primary',
3239                 html : 'OK'
3240             }
3241         ],
3242         /**
3243          * Button config that displays Yes, No and Cancel buttons
3244          * @type Object
3245          */
3246         YESNOCANCEL : [
3247             {
3248                 name : 'yes',
3249                 weight : 'primary',
3250                 html : 'Yes'
3251             },
3252             {
3253                 name : 'no',
3254                 html : 'No'
3255             },
3256             {
3257                 name : 'cancel',
3258                 html : 'Cancel'
3259             }
3260         ],
3261         
3262         zIndex : 10001
3263 });
3264 /*
3265  * - LGPL
3266  *
3267  * messagebox - can be used as a replace
3268  * 
3269  */
3270 /**
3271  * @class Roo.MessageBox
3272  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3273  * Example usage:
3274  *<pre><code>
3275 // Basic alert:
3276 Roo.Msg.alert('Status', 'Changes saved successfully.');
3277
3278 // Prompt for user data:
3279 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3280     if (btn == 'ok'){
3281         // process text value...
3282     }
3283 });
3284
3285 // Show a dialog using config options:
3286 Roo.Msg.show({
3287    title:'Save Changes?',
3288    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3289    buttons: Roo.Msg.YESNOCANCEL,
3290    fn: processResult,
3291    animEl: 'elId'
3292 });
3293 </code></pre>
3294  * @singleton
3295  */
3296 Roo.bootstrap.MessageBox = function(){
3297     var dlg, opt, mask, waitTimer;
3298     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3299     var buttons, activeTextEl, bwidth;
3300
3301     
3302     // private
3303     var handleButton = function(button){
3304         dlg.hide();
3305         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3306     };
3307
3308     // private
3309     var handleHide = function(){
3310         if(opt && opt.cls){
3311             dlg.el.removeClass(opt.cls);
3312         }
3313         //if(waitTimer){
3314         //    Roo.TaskMgr.stop(waitTimer);
3315         //    waitTimer = null;
3316         //}
3317     };
3318
3319     // private
3320     var updateButtons = function(b){
3321         var width = 0;
3322         if(!b){
3323             buttons["ok"].hide();
3324             buttons["cancel"].hide();
3325             buttons["yes"].hide();
3326             buttons["no"].hide();
3327             dlg.footerEl.hide();
3328             
3329             return width;
3330         }
3331         dlg.footerEl.show();
3332         for(var k in buttons){
3333             if(typeof buttons[k] != "function"){
3334                 if(b[k]){
3335                     buttons[k].show();
3336                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3337                     width += buttons[k].el.getWidth()+15;
3338                 }else{
3339                     buttons[k].hide();
3340                 }
3341             }
3342         }
3343         return width;
3344     };
3345
3346     // private
3347     var handleEsc = function(d, k, e){
3348         if(opt && opt.closable !== false){
3349             dlg.hide();
3350         }
3351         if(e){
3352             e.stopEvent();
3353         }
3354     };
3355
3356     return {
3357         /**
3358          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3359          * @return {Roo.BasicDialog} The BasicDialog element
3360          */
3361         getDialog : function(){
3362            if(!dlg){
3363                 dlg = new Roo.bootstrap.Modal( {
3364                     //draggable: true,
3365                     //resizable:false,
3366                     //constraintoviewport:false,
3367                     //fixedcenter:true,
3368                     //collapsible : false,
3369                     //shim:true,
3370                     //modal: true,
3371                 //    width: 'auto',
3372                   //  height:100,
3373                     //buttonAlign:"center",
3374                     closeClick : function(){
3375                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3376                             handleButton("no");
3377                         }else{
3378                             handleButton("cancel");
3379                         }
3380                     }
3381                 });
3382                 dlg.render();
3383                 dlg.on("hide", handleHide);
3384                 mask = dlg.mask;
3385                 //dlg.addKeyListener(27, handleEsc);
3386                 buttons = {};
3387                 this.buttons = buttons;
3388                 var bt = this.buttonText;
3389                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3390                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3391                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3392                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3393                 //Roo.log(buttons);
3394                 bodyEl = dlg.bodyEl.createChild({
3395
3396                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3397                         '<textarea class="roo-mb-textarea"></textarea>' +
3398                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3399                 });
3400                 msgEl = bodyEl.dom.firstChild;
3401                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3402                 textboxEl.enableDisplayMode();
3403                 textboxEl.addKeyListener([10,13], function(){
3404                     if(dlg.isVisible() && opt && opt.buttons){
3405                         if(opt.buttons.ok){
3406                             handleButton("ok");
3407                         }else if(opt.buttons.yes){
3408                             handleButton("yes");
3409                         }
3410                     }
3411                 });
3412                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3413                 textareaEl.enableDisplayMode();
3414                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3415                 progressEl.enableDisplayMode();
3416                 
3417                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3418                 var pf = progressEl.dom.firstChild;
3419                 if (pf) {
3420                     pp = Roo.get(pf.firstChild);
3421                     pp.setHeight(pf.offsetHeight);
3422                 }
3423                 
3424             }
3425             return dlg;
3426         },
3427
3428         /**
3429          * Updates the message box body text
3430          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3431          * the XHTML-compliant non-breaking space character '&amp;#160;')
3432          * @return {Roo.MessageBox} This message box
3433          */
3434         updateText : function(text)
3435         {
3436             if(!dlg.isVisible() && !opt.width){
3437                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3438                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3439             }
3440             msgEl.innerHTML = text || '&#160;';
3441       
3442             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3443             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3444             var w = Math.max(
3445                     Math.min(opt.width || cw , this.maxWidth), 
3446                     Math.max(opt.minWidth || this.minWidth, bwidth)
3447             );
3448             if(opt.prompt){
3449                 activeTextEl.setWidth(w);
3450             }
3451             if(dlg.isVisible()){
3452                 dlg.fixedcenter = false;
3453             }
3454             // to big, make it scroll. = But as usual stupid IE does not support
3455             // !important..
3456             
3457             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3458                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3459                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3460             } else {
3461                 bodyEl.dom.style.height = '';
3462                 bodyEl.dom.style.overflowY = '';
3463             }
3464             if (cw > w) {
3465                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3466             } else {
3467                 bodyEl.dom.style.overflowX = '';
3468             }
3469             
3470             dlg.setContentSize(w, bodyEl.getHeight());
3471             if(dlg.isVisible()){
3472                 dlg.fixedcenter = true;
3473             }
3474             return this;
3475         },
3476
3477         /**
3478          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3479          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3480          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3481          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3482          * @return {Roo.MessageBox} This message box
3483          */
3484         updateProgress : function(value, text){
3485             if(text){
3486                 this.updateText(text);
3487             }
3488             
3489             if (pp) { // weird bug on my firefox - for some reason this is not defined
3490                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3491                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3492             }
3493             return this;
3494         },        
3495
3496         /**
3497          * Returns true if the message box is currently displayed
3498          * @return {Boolean} True if the message box is visible, else false
3499          */
3500         isVisible : function(){
3501             return dlg && dlg.isVisible();  
3502         },
3503
3504         /**
3505          * Hides the message box if it is displayed
3506          */
3507         hide : function(){
3508             if(this.isVisible()){
3509                 dlg.hide();
3510             }  
3511         },
3512
3513         /**
3514          * Displays a new message box, or reinitializes an existing message box, based on the config options
3515          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3516          * The following config object properties are supported:
3517          * <pre>
3518 Property    Type             Description
3519 ----------  ---------------  ------------------------------------------------------------------------------------
3520 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3521                                    closes (defaults to undefined)
3522 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3523                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3524 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3525                                    progress and wait dialogs will ignore this property and always hide the
3526                                    close button as they can only be closed programmatically.
3527 cls               String           A custom CSS class to apply to the message box element
3528 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3529                                    displayed (defaults to 75)
3530 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3531                                    function will be btn (the name of the button that was clicked, if applicable,
3532                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3533                                    Progress and wait dialogs will ignore this option since they do not respond to
3534                                    user actions and can only be closed programmatically, so any required function
3535                                    should be called by the same code after it closes the dialog.
3536 icon              String           A CSS class that provides a background image to be used as an icon for
3537                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3538 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3539 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3540 modal             Boolean          False to allow user interaction with the page while the message box is
3541                                    displayed (defaults to true)
3542 msg               String           A string that will replace the existing message box body text (defaults
3543                                    to the XHTML-compliant non-breaking space character '&#160;')
3544 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3545 progress          Boolean          True to display a progress bar (defaults to false)
3546 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3547 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3548 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3549 title             String           The title text
3550 value             String           The string value to set into the active textbox element if displayed
3551 wait              Boolean          True to display a progress bar (defaults to false)
3552 width             Number           The width of the dialog in pixels
3553 </pre>
3554          *
3555          * Example usage:
3556          * <pre><code>
3557 Roo.Msg.show({
3558    title: 'Address',
3559    msg: 'Please enter your address:',
3560    width: 300,
3561    buttons: Roo.MessageBox.OKCANCEL,
3562    multiline: true,
3563    fn: saveAddress,
3564    animEl: 'addAddressBtn'
3565 });
3566 </code></pre>
3567          * @param {Object} config Configuration options
3568          * @return {Roo.MessageBox} This message box
3569          */
3570         show : function(options)
3571         {
3572             
3573             // this causes nightmares if you show one dialog after another
3574             // especially on callbacks..
3575              
3576             if(this.isVisible()){
3577                 
3578                 this.hide();
3579                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3580                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3581                 Roo.log("New Dialog Message:" +  options.msg )
3582                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3583                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3584                 
3585             }
3586             var d = this.getDialog();
3587             opt = options;
3588             d.setTitle(opt.title || "&#160;");
3589             d.closeEl.setDisplayed(opt.closable !== false);
3590             activeTextEl = textboxEl;
3591             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3592             if(opt.prompt){
3593                 if(opt.multiline){
3594                     textboxEl.hide();
3595                     textareaEl.show();
3596                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3597                         opt.multiline : this.defaultTextHeight);
3598                     activeTextEl = textareaEl;
3599                 }else{
3600                     textboxEl.show();
3601                     textareaEl.hide();
3602                 }
3603             }else{
3604                 textboxEl.hide();
3605                 textareaEl.hide();
3606             }
3607             progressEl.setDisplayed(opt.progress === true);
3608             if (opt.progress) {
3609                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3610             }
3611             this.updateProgress(0);
3612             activeTextEl.dom.value = opt.value || "";
3613             if(opt.prompt){
3614                 dlg.setDefaultButton(activeTextEl);
3615             }else{
3616                 var bs = opt.buttons;
3617                 var db = null;
3618                 if(bs && bs.ok){
3619                     db = buttons["ok"];
3620                 }else if(bs && bs.yes){
3621                     db = buttons["yes"];
3622                 }
3623                 dlg.setDefaultButton(db);
3624             }
3625             bwidth = updateButtons(opt.buttons);
3626             this.updateText(opt.msg);
3627             if(opt.cls){
3628                 d.el.addClass(opt.cls);
3629             }
3630             d.proxyDrag = opt.proxyDrag === true;
3631             d.modal = opt.modal !== false;
3632             d.mask = opt.modal !== false ? mask : false;
3633             if(!d.isVisible()){
3634                 // force it to the end of the z-index stack so it gets a cursor in FF
3635                 document.body.appendChild(dlg.el.dom);
3636                 d.animateTarget = null;
3637                 d.show(options.animEl);
3638             }
3639             return this;
3640         },
3641
3642         /**
3643          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3644          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3645          * and closing the message box when the process is complete.
3646          * @param {String} title The title bar text
3647          * @param {String} msg The message box body text
3648          * @return {Roo.MessageBox} This message box
3649          */
3650         progress : function(title, msg){
3651             this.show({
3652                 title : title,
3653                 msg : msg,
3654                 buttons: false,
3655                 progress:true,
3656                 closable:false,
3657                 minWidth: this.minProgressWidth,
3658                 modal : true
3659             });
3660             return this;
3661         },
3662
3663         /**
3664          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3665          * If a callback function is passed it will be called after the user clicks the button, and the
3666          * id of the button that was clicked will be passed as the only parameter to the callback
3667          * (could also be the top-right close button).
3668          * @param {String} title The title bar text
3669          * @param {String} msg The message box body text
3670          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3671          * @param {Object} scope (optional) The scope of the callback function
3672          * @return {Roo.MessageBox} This message box
3673          */
3674         alert : function(title, msg, fn, scope)
3675         {
3676             this.show({
3677                 title : title,
3678                 msg : msg,
3679                 buttons: this.OK,
3680                 fn: fn,
3681                 closable : false,
3682                 scope : scope,
3683                 modal : true
3684             });
3685             return this;
3686         },
3687
3688         /**
3689          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3690          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3691          * You are responsible for closing the message box when the process is complete.
3692          * @param {String} msg The message box body text
3693          * @param {String} title (optional) The title bar text
3694          * @return {Roo.MessageBox} This message box
3695          */
3696         wait : function(msg, title){
3697             this.show({
3698                 title : title,
3699                 msg : msg,
3700                 buttons: false,
3701                 closable:false,
3702                 progress:true,
3703                 modal:true,
3704                 width:300,
3705                 wait:true
3706             });
3707             waitTimer = Roo.TaskMgr.start({
3708                 run: function(i){
3709                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3710                 },
3711                 interval: 1000
3712             });
3713             return this;
3714         },
3715
3716         /**
3717          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3718          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3719          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3720          * @param {String} title The title bar text
3721          * @param {String} msg The message box body text
3722          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3723          * @param {Object} scope (optional) The scope of the callback function
3724          * @return {Roo.MessageBox} This message box
3725          */
3726         confirm : function(title, msg, fn, scope){
3727             this.show({
3728                 title : title,
3729                 msg : msg,
3730                 buttons: this.YESNO,
3731                 fn: fn,
3732                 scope : scope,
3733                 modal : true
3734             });
3735             return this;
3736         },
3737
3738         /**
3739          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3740          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3741          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3742          * (could also be the top-right close button) and the text that was entered will be passed as the two
3743          * parameters to the callback.
3744          * @param {String} title The title bar text
3745          * @param {String} msg The message box body text
3746          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3747          * @param {Object} scope (optional) The scope of the callback function
3748          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3749          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3750          * @return {Roo.MessageBox} This message box
3751          */
3752         prompt : function(title, msg, fn, scope, multiline){
3753             this.show({
3754                 title : title,
3755                 msg : msg,
3756                 buttons: this.OKCANCEL,
3757                 fn: fn,
3758                 minWidth:250,
3759                 scope : scope,
3760                 prompt:true,
3761                 multiline: multiline,
3762                 modal : true
3763             });
3764             return this;
3765         },
3766
3767         /**
3768          * Button config that displays a single OK button
3769          * @type Object
3770          */
3771         OK : {ok:true},
3772         /**
3773          * Button config that displays Yes and No buttons
3774          * @type Object
3775          */
3776         YESNO : {yes:true, no:true},
3777         /**
3778          * Button config that displays OK and Cancel buttons
3779          * @type Object
3780          */
3781         OKCANCEL : {ok:true, cancel:true},
3782         /**
3783          * Button config that displays Yes, No and Cancel buttons
3784          * @type Object
3785          */
3786         YESNOCANCEL : {yes:true, no:true, cancel:true},
3787
3788         /**
3789          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3790          * @type Number
3791          */
3792         defaultTextHeight : 75,
3793         /**
3794          * The maximum width in pixels of the message box (defaults to 600)
3795          * @type Number
3796          */
3797         maxWidth : 600,
3798         /**
3799          * The minimum width in pixels of the message box (defaults to 100)
3800          * @type Number
3801          */
3802         minWidth : 100,
3803         /**
3804          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3805          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3806          * @type Number
3807          */
3808         minProgressWidth : 250,
3809         /**
3810          * An object containing the default button text strings that can be overriden for localized language support.
3811          * Supported properties are: ok, cancel, yes and no.
3812          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3813          * @type Object
3814          */
3815         buttonText : {
3816             ok : "OK",
3817             cancel : "Cancel",
3818             yes : "Yes",
3819             no : "No"
3820         }
3821     };
3822 }();
3823
3824 /**
3825  * Shorthand for {@link Roo.MessageBox}
3826  */
3827 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3828 Roo.Msg = Roo.Msg || Roo.MessageBox;
3829 /*
3830  * - LGPL
3831  *
3832  * navbar
3833  * 
3834  */
3835
3836 /**
3837  * @class Roo.bootstrap.Navbar
3838  * @extends Roo.bootstrap.Component
3839  * Bootstrap Navbar class
3840
3841  * @constructor
3842  * Create a new Navbar
3843  * @param {Object} config The config object
3844  */
3845
3846
3847 Roo.bootstrap.Navbar = function(config){
3848     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3849     this.addEvents({
3850         // raw events
3851         /**
3852          * @event beforetoggle
3853          * Fire before toggle the menu
3854          * @param {Roo.EventObject} e
3855          */
3856         "beforetoggle" : true
3857     });
3858 };
3859
3860 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3861     
3862     
3863    
3864     // private
3865     navItems : false,
3866     loadMask : false,
3867     
3868     
3869     getAutoCreate : function(){
3870         
3871         
3872         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3873         
3874     },
3875     
3876     initEvents :function ()
3877     {
3878         //Roo.log(this.el.select('.navbar-toggle',true));
3879         this.el.select('.navbar-toggle',true).on('click', function() {
3880             if(this.fireEvent('beforetoggle', this) !== false){
3881                 var ce = this.el.select('.navbar-collapse',true).first();
3882                 ce.toggleClass('in'); // old...
3883                 if (ce.hasClass('collapse')) {
3884                     // show it...
3885                     ce.removeClass('collapse');
3886                     ce.addClass('show');
3887                     var h = ce.getHeight();
3888                     Roo.log(h);
3889                     ce.removeClass('show');
3890                     // at this point we should be able to see it..
3891                     ce.addClass('collapsing');
3892                     
3893                     ce.setHeight(0); // resize it ...
3894                     ce.on('transitionend', function() {
3895                         Roo.log('done transition');
3896                         ce.removeClass('collapsing');
3897                         ce.addClass('show');
3898                         ce.removeClass('collapse');
3899
3900                         ce.dom.style.height = '';
3901                     }, this, { single: true} );
3902                     ce.setHeight(h);
3903                     
3904                 } else {
3905                     ce.setHeight(ce.getHeight());
3906                     ce.removeClass('show');
3907                     ce.addClass('collapsing');
3908                     
3909                     ce.on('transitionend', function() {
3910                         ce.dom.style.height = '';
3911                         ce.removeClass('collapsing');
3912                         ce.addClass('collapse');
3913                     }, this, { single: true} );
3914                     ce.setHeight(0);
3915                 }
3916             }
3917             
3918         }, this);
3919         
3920         var mark = {
3921             tag: "div",
3922             cls:"x-dlg-mask"
3923         };
3924         
3925         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3926         
3927         var size = this.el.getSize();
3928         this.maskEl.setSize(size.width, size.height);
3929         this.maskEl.enableDisplayMode("block");
3930         this.maskEl.hide();
3931         
3932         if(this.loadMask){
3933             this.maskEl.show();
3934         }
3935     },
3936     
3937     
3938     getChildContainer : function()
3939     {
3940         if (this.el.select('.collapse').getCount()) {
3941             return this.el.select('.collapse',true).first();
3942         }
3943         
3944         return this.el;
3945     },
3946     
3947     mask : function()
3948     {
3949         this.maskEl.show();
3950     },
3951     
3952     unmask : function()
3953     {
3954         this.maskEl.hide();
3955     } 
3956     
3957     
3958     
3959     
3960 });
3961
3962
3963
3964  
3965
3966  /*
3967  * - LGPL
3968  *
3969  * navbar
3970  * 
3971  */
3972
3973 /**
3974  * @class Roo.bootstrap.NavSimplebar
3975  * @extends Roo.bootstrap.Navbar
3976  * Bootstrap Sidebar class
3977  *
3978  * @cfg {Boolean} inverse is inverted color
3979  * 
3980  * @cfg {String} type (nav | pills | tabs)
3981  * @cfg {Boolean} arrangement stacked | justified
3982  * @cfg {String} align (left | right) alignment
3983  * 
3984  * @cfg {Boolean} main (true|false) main nav bar? default false
3985  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3986  * 
3987  * @cfg {String} tag (header|footer|nav|div) default is nav 
3988
3989  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3990  * 
3991  * 
3992  * @constructor
3993  * Create a new Sidebar
3994  * @param {Object} config The config object
3995  */
3996
3997
3998 Roo.bootstrap.NavSimplebar = function(config){
3999     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4000 };
4001
4002 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
4003     
4004     inverse: false,
4005     
4006     type: false,
4007     arrangement: '',
4008     align : false,
4009     
4010     weight : 'light',
4011     
4012     main : false,
4013     
4014     
4015     tag : false,
4016     
4017     
4018     getAutoCreate : function(){
4019         
4020         
4021         var cfg = {
4022             tag : this.tag || 'div',
4023             cls : 'navbar navbar-expand-lg'
4024         };
4025         if (['light','white'].indexOf(this.weight) > -1) {
4026             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4027         }
4028         cfg.cls += ' bg-' + this.weight;
4029         
4030         if (this.inverse) {
4031             cfg.cls += ' navbar-inverse';
4032             
4033         }
4034         
4035         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4036         
4037         if (Roo.bootstrap.version == 4) {
4038             return cfg;
4039         }
4040         
4041         cfg.cn = [
4042             {
4043                 cls: 'nav',
4044                 tag : 'ul'
4045             }
4046         ];
4047         
4048          
4049         this.type = this.type || 'nav';
4050         if (['tabs','pills'].indexOf(this.type)!==-1) {
4051             cfg.cn[0].cls += ' nav-' + this.type
4052         
4053         
4054         } else {
4055             if (this.type!=='nav') {
4056                 Roo.log('nav type must be nav/tabs/pills')
4057             }
4058             cfg.cn[0].cls += ' navbar-nav'
4059         }
4060         
4061         
4062         
4063         
4064         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
4065             cfg.cn[0].cls += ' nav-' + this.arrangement;
4066         }
4067         
4068         
4069         if (this.align === 'right') {
4070             cfg.cn[0].cls += ' navbar-right';
4071         }
4072         
4073         
4074         
4075         
4076         return cfg;
4077     
4078         
4079     }
4080     
4081     
4082     
4083 });
4084
4085
4086
4087  
4088
4089  
4090        /*
4091  * - LGPL
4092  *
4093  * navbar
4094  * navbar-fixed-top
4095  * navbar-expand-md  fixed-top 
4096  */
4097
4098 /**
4099  * @class Roo.bootstrap.NavHeaderbar
4100  * @extends Roo.bootstrap.NavSimplebar
4101  * Bootstrap Sidebar class
4102  *
4103  * @cfg {String} brand what is brand
4104  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4105  * @cfg {String} brand_href href of the brand
4106  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4107  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4108  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4109  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4110  * 
4111  * @constructor
4112  * Create a new Sidebar
4113  * @param {Object} config The config object
4114  */
4115
4116
4117 Roo.bootstrap.NavHeaderbar = function(config){
4118     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4119       
4120 };
4121
4122 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4123     
4124     position: '',
4125     brand: '',
4126     brand_href: false,
4127     srButton : true,
4128     autohide : false,
4129     desktopCenter : false,
4130    
4131     
4132     getAutoCreate : function(){
4133         
4134         var   cfg = {
4135             tag: this.nav || 'nav',
4136             cls: 'navbar navbar-expand-md',
4137             role: 'navigation',
4138             cn: []
4139         };
4140         
4141         var cn = cfg.cn;
4142         if (this.desktopCenter) {
4143             cn.push({cls : 'container', cn : []});
4144             cn = cn[0].cn;
4145         }
4146         
4147         if(this.srButton){
4148             var btn = {
4149                 tag: 'button',
4150                 type: 'button',
4151                 cls: 'navbar-toggle navbar-toggler',
4152                 'data-toggle': 'collapse',
4153                 cn: [
4154                     {
4155                         tag: 'span',
4156                         cls: 'sr-only',
4157                         html: 'Toggle navigation'
4158                     },
4159                     {
4160                         tag: 'span',
4161                         cls: 'icon-bar navbar-toggler-icon'
4162                     },
4163                     {
4164                         tag: 'span',
4165                         cls: 'icon-bar'
4166                     },
4167                     {
4168                         tag: 'span',
4169                         cls: 'icon-bar'
4170                     }
4171                 ]
4172             };
4173             
4174             cn.push( Roo.bootstrap.version == 4 ? btn : {
4175                 tag: 'div',
4176                 cls: 'navbar-header',
4177                 cn: [
4178                     btn
4179                 ]
4180             });
4181         }
4182         
4183         cn.push({
4184             tag: 'div',
4185             cls: 'collapse navbar-collapse',
4186             cn : []
4187         });
4188         
4189         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4190         
4191         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4192             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4193             
4194             // tag can override this..
4195             
4196             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4197         }
4198         
4199         if (this.brand !== '') {
4200             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4201             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4202                 tag: 'a',
4203                 href: this.brand_href ? this.brand_href : '#',
4204                 cls: 'navbar-brand',
4205                 cn: [
4206                 this.brand
4207                 ]
4208             });
4209         }
4210         
4211         if(this.main){
4212             cfg.cls += ' main-nav';
4213         }
4214         
4215         
4216         return cfg;
4217
4218         
4219     },
4220     getHeaderChildContainer : function()
4221     {
4222         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4223             return this.el.select('.navbar-header',true).first();
4224         }
4225         
4226         return this.getChildContainer();
4227     },
4228     
4229     
4230     initEvents : function()
4231     {
4232         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4233         
4234         if (this.autohide) {
4235             
4236             var prevScroll = 0;
4237             var ft = this.el;
4238             
4239             Roo.get(document).on('scroll',function(e) {
4240                 var ns = Roo.get(document).getScroll().top;
4241                 var os = prevScroll;
4242                 prevScroll = ns;
4243                 
4244                 if(ns > os){
4245                     ft.removeClass('slideDown');
4246                     ft.addClass('slideUp');
4247                     return;
4248                 }
4249                 ft.removeClass('slideUp');
4250                 ft.addClass('slideDown');
4251                  
4252               
4253           },this);
4254         }
4255     }    
4256     
4257 });
4258
4259
4260
4261  
4262
4263  /*
4264  * - LGPL
4265  *
4266  * navbar
4267  * 
4268  */
4269
4270 /**
4271  * @class Roo.bootstrap.NavSidebar
4272  * @extends Roo.bootstrap.Navbar
4273  * Bootstrap Sidebar class
4274  * 
4275  * @constructor
4276  * Create a new Sidebar
4277  * @param {Object} config The config object
4278  */
4279
4280
4281 Roo.bootstrap.NavSidebar = function(config){
4282     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4283 };
4284
4285 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4286     
4287     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4288     
4289     getAutoCreate : function(){
4290         
4291         
4292         return  {
4293             tag: 'div',
4294             cls: 'sidebar sidebar-nav'
4295         };
4296     
4297         
4298     }
4299     
4300     
4301     
4302 });
4303
4304
4305
4306  
4307
4308  /*
4309  * - LGPL
4310  *
4311  * nav group
4312  * 
4313  */
4314
4315 /**
4316  * @class Roo.bootstrap.NavGroup
4317  * @extends Roo.bootstrap.Component
4318  * Bootstrap NavGroup class
4319  * @cfg {String} align (left|right)
4320  * @cfg {Boolean} inverse
4321  * @cfg {String} type (nav|pills|tab) default nav
4322  * @cfg {String} navId - reference Id for navbar.
4323
4324  * 
4325  * @constructor
4326  * Create a new nav group
4327  * @param {Object} config The config object
4328  */
4329
4330 Roo.bootstrap.NavGroup = function(config){
4331     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4332     this.navItems = [];
4333    
4334     Roo.bootstrap.NavGroup.register(this);
4335      this.addEvents({
4336         /**
4337              * @event changed
4338              * Fires when the active item changes
4339              * @param {Roo.bootstrap.NavGroup} this
4340              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4341              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4342          */
4343         'changed': true
4344      });
4345     
4346 };
4347
4348 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4349     
4350     align: '',
4351     inverse: false,
4352     form: false,
4353     type: 'nav',
4354     navId : '',
4355     // private
4356     
4357     navItems : false, 
4358     
4359     getAutoCreate : function()
4360     {
4361         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4362         
4363         cfg = {
4364             tag : 'ul',
4365             cls: 'nav' 
4366         };
4367         if (Roo.bootstrap.version == 4) {
4368             if (this.type == 'pills') {
4369                 cfg.cls = ' nav-pills';
4370             }
4371         } else {
4372             if (['tabs','pills'].indexOf(this.type)!==-1) {
4373                 cfg.cls += ' nav-' + this.type
4374             } else {
4375                 if (this.type !== 'nav') {
4376                     Roo.log('nav type must be nav/tabs/pills')
4377                 }
4378                 cfg.cls += ' navbar-nav'
4379             }
4380         }
4381         
4382         if (this.parent() && this.parent().sidebar) {
4383             cfg = {
4384                 tag: 'ul',
4385                 cls: 'dashboard-menu sidebar-menu'
4386             };
4387             
4388             return cfg;
4389         }
4390         
4391         if (this.form === true) {
4392             cfg = {
4393                 tag: 'form',
4394                 cls: 'navbar-form form-inline'
4395             };
4396             
4397             if (this.align === 'right') {
4398                 cfg.cls += ' navbar-right ml-md-auto';
4399             } else {
4400                 cfg.cls += ' navbar-left';
4401             }
4402         }
4403         
4404         if (this.align === 'right') {
4405             cfg.cls += ' navbar-right ml-md-auto';
4406         } else {
4407             cfg.cls += ' mr-auto';
4408         }
4409         
4410         if (this.inverse) {
4411             cfg.cls += ' navbar-inverse';
4412             
4413         }
4414         
4415         
4416         return cfg;
4417     },
4418     /**
4419     * sets the active Navigation item
4420     * @param {Roo.bootstrap.NavItem} the new current navitem
4421     */
4422     setActiveItem : function(item)
4423     {
4424         var prev = false;
4425         Roo.each(this.navItems, function(v){
4426             if (v == item) {
4427                 return ;
4428             }
4429             if (v.isActive()) {
4430                 v.setActive(false, true);
4431                 prev = v;
4432                 
4433             }
4434             
4435         });
4436
4437         item.setActive(true, true);
4438         this.fireEvent('changed', this, item, prev);
4439         
4440         
4441     },
4442     /**
4443     * gets the active Navigation item
4444     * @return {Roo.bootstrap.NavItem} the current navitem
4445     */
4446     getActive : function()
4447     {
4448         
4449         var prev = false;
4450         Roo.each(this.navItems, function(v){
4451             
4452             if (v.isActive()) {
4453                 prev = v;
4454                 
4455             }
4456             
4457         });
4458         return prev;
4459     },
4460     
4461     indexOfNav : function()
4462     {
4463         
4464         var prev = false;
4465         Roo.each(this.navItems, function(v,i){
4466             
4467             if (v.isActive()) {
4468                 prev = i;
4469                 
4470             }
4471             
4472         });
4473         return prev;
4474     },
4475     /**
4476     * adds a Navigation item
4477     * @param {Roo.bootstrap.NavItem} the navitem to add
4478     */
4479     addItem : function(cfg)
4480     {
4481         if (this.form && Roo.bootstrap.version == 4) {
4482             cfg.tag = 'div';
4483         }
4484         var cn = new Roo.bootstrap.NavItem(cfg);
4485         this.register(cn);
4486         cn.parentId = this.id;
4487         cn.onRender(this.el, null);
4488         return cn;
4489     },
4490     /**
4491     * register a Navigation item
4492     * @param {Roo.bootstrap.NavItem} the navitem to add
4493     */
4494     register : function(item)
4495     {
4496         this.navItems.push( item);
4497         item.navId = this.navId;
4498     
4499     },
4500     
4501     /**
4502     * clear all the Navigation item
4503     */
4504    
4505     clearAll : function()
4506     {
4507         this.navItems = [];
4508         this.el.dom.innerHTML = '';
4509     },
4510     
4511     getNavItem: function(tabId)
4512     {
4513         var ret = false;
4514         Roo.each(this.navItems, function(e) {
4515             if (e.tabId == tabId) {
4516                ret =  e;
4517                return false;
4518             }
4519             return true;
4520             
4521         });
4522         return ret;
4523     },
4524     
4525     setActiveNext : function()
4526     {
4527         var i = this.indexOfNav(this.getActive());
4528         if (i > this.navItems.length) {
4529             return;
4530         }
4531         this.setActiveItem(this.navItems[i+1]);
4532     },
4533     setActivePrev : function()
4534     {
4535         var i = this.indexOfNav(this.getActive());
4536         if (i  < 1) {
4537             return;
4538         }
4539         this.setActiveItem(this.navItems[i-1]);
4540     },
4541     clearWasActive : function(except) {
4542         Roo.each(this.navItems, function(e) {
4543             if (e.tabId != except.tabId && e.was_active) {
4544                e.was_active = false;
4545                return false;
4546             }
4547             return true;
4548             
4549         });
4550     },
4551     getWasActive : function ()
4552     {
4553         var r = false;
4554         Roo.each(this.navItems, function(e) {
4555             if (e.was_active) {
4556                r = e;
4557                return false;
4558             }
4559             return true;
4560             
4561         });
4562         return r;
4563     }
4564     
4565     
4566 });
4567
4568  
4569 Roo.apply(Roo.bootstrap.NavGroup, {
4570     
4571     groups: {},
4572      /**
4573     * register a Navigation Group
4574     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4575     */
4576     register : function(navgrp)
4577     {
4578         this.groups[navgrp.navId] = navgrp;
4579         
4580     },
4581     /**
4582     * fetch a Navigation Group based on the navigation ID
4583     * @param {string} the navgroup to add
4584     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4585     */
4586     get: function(navId) {
4587         if (typeof(this.groups[navId]) == 'undefined') {
4588             return false;
4589             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4590         }
4591         return this.groups[navId] ;
4592     }
4593     
4594     
4595     
4596 });
4597
4598  /*
4599  * - LGPL
4600  *
4601  * row
4602  * 
4603  */
4604
4605 /**
4606  * @class Roo.bootstrap.NavItem
4607  * @extends Roo.bootstrap.Component
4608  * Bootstrap Navbar.NavItem class
4609  * @cfg {String} href  link to
4610  * @cfg {String} html content of button
4611  * @cfg {String} badge text inside badge
4612  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4613  * @cfg {String} glyphicon DEPRICATED - use fa
4614  * @cfg {String} icon DEPRICATED - use fa
4615  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4616  * @cfg {Boolean} active Is item active
4617  * @cfg {Boolean} disabled Is item disabled
4618  
4619  * @cfg {Boolean} preventDefault (true | false) default false
4620  * @cfg {String} tabId the tab that this item activates.
4621  * @cfg {String} tagtype (a|span) render as a href or span?
4622  * @cfg {Boolean} animateRef (true|false) link to element default false  
4623   
4624  * @constructor
4625  * Create a new Navbar Item
4626  * @param {Object} config The config object
4627  */
4628 Roo.bootstrap.NavItem = function(config){
4629     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4630     this.addEvents({
4631         // raw events
4632         /**
4633          * @event click
4634          * The raw click event for the entire grid.
4635          * @param {Roo.EventObject} e
4636          */
4637         "click" : true,
4638          /**
4639             * @event changed
4640             * Fires when the active item active state changes
4641             * @param {Roo.bootstrap.NavItem} this
4642             * @param {boolean} state the new state
4643              
4644          */
4645         'changed': true,
4646         /**
4647             * @event scrollto
4648             * Fires when scroll to element
4649             * @param {Roo.bootstrap.NavItem} this
4650             * @param {Object} options
4651             * @param {Roo.EventObject} e
4652              
4653          */
4654         'scrollto': true
4655     });
4656    
4657 };
4658
4659 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4660     
4661     href: false,
4662     html: '',
4663     badge: '',
4664     icon: false,
4665     fa : false,
4666     glyphicon: false,
4667     active: false,
4668     preventDefault : false,
4669     tabId : false,
4670     tagtype : 'a',
4671     tag: 'li',
4672     disabled : false,
4673     animateRef : false,
4674     was_active : false,
4675     
4676     getAutoCreate : function(){
4677          
4678         var cfg = {
4679             tag: this.tag,
4680             cls: 'nav-item'
4681             
4682         };
4683         
4684         if (this.active) {
4685             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4686         }
4687         if (this.disabled) {
4688             cfg.cls += ' disabled';
4689         }
4690         
4691         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4692             cfg.cn = [
4693                 {
4694                     tag: this.tagtype,
4695                     href : this.href || "#",
4696                     html: this.html || ''
4697                 }
4698             ];
4699             if (this.tagtype == 'a') {
4700                 cfg.cn[0].cls = 'nav-link';
4701             }
4702             if (this.icon) {
4703                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4704             }
4705             if (this.fa) {
4706                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>'
4707             }
4708             if(this.glyphicon) {
4709                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4710             }
4711             
4712             if (this.menu) {
4713                 
4714                 cfg.cn[0].html += " <span class='caret'></span>";
4715              
4716             }
4717             
4718             if (this.badge !== '') {
4719                  
4720                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4721             }
4722         }
4723         
4724         
4725         
4726         return cfg;
4727     },
4728     onRender : function(ct, position)
4729     {
4730        // Roo.log("Call onRender: " + this.xtype);
4731         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
4732             this.tag = 'div';
4733         }
4734         
4735         return Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
4736     },
4737       
4738     
4739     initEvents: function() 
4740     {
4741         if (typeof (this.menu) != 'undefined') {
4742             this.menu.parentType = this.xtype;
4743             this.menu.triggerEl = this.el;
4744             this.menu = this.addxtype(Roo.apply({}, this.menu));
4745         }
4746         
4747         this.el.select('a',true).on('click', this.onClick, this);
4748         
4749         if(this.tagtype == 'span'){
4750             this.el.select('span',true).on('click', this.onClick, this);
4751         }
4752        
4753         // at this point parent should be available..
4754         this.parent().register(this);
4755     },
4756     
4757     onClick : function(e)
4758     {
4759         if (e.getTarget('.dropdown-menu-item')) {
4760             // did you click on a menu itemm.... - then don't trigger onclick..
4761             return;
4762         }
4763         
4764         if(
4765                 this.preventDefault || 
4766                 this.href == '#' 
4767         ){
4768             Roo.log("NavItem - prevent Default?");
4769             e.preventDefault();
4770         }
4771         
4772         if (this.disabled) {
4773             return;
4774         }
4775         
4776         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4777         if (tg && tg.transition) {
4778             Roo.log("waiting for the transitionend");
4779             return;
4780         }
4781         
4782         
4783         
4784         //Roo.log("fire event clicked");
4785         if(this.fireEvent('click', this, e) === false){
4786             return;
4787         };
4788         
4789         if(this.tagtype == 'span'){
4790             return;
4791         }
4792         
4793         //Roo.log(this.href);
4794         var ael = this.el.select('a',true).first();
4795         //Roo.log(ael);
4796         
4797         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4798             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4799             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4800                 return; // ignore... - it's a 'hash' to another page.
4801             }
4802             Roo.log("NavItem - prevent Default?");
4803             e.preventDefault();
4804             this.scrollToElement(e);
4805         }
4806         
4807         
4808         var p =  this.parent();
4809    
4810         if (['tabs','pills'].indexOf(p.type)!==-1) {
4811             if (typeof(p.setActiveItem) !== 'undefined') {
4812                 p.setActiveItem(this);
4813             }
4814         }
4815         
4816         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4817         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4818             // remove the collapsed menu expand...
4819             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4820         }
4821     },
4822     
4823     isActive: function () {
4824         return this.active
4825     },
4826     setActive : function(state, fire, is_was_active)
4827     {
4828         if (this.active && !state && this.navId) {
4829             this.was_active = true;
4830             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4831             if (nv) {
4832                 nv.clearWasActive(this);
4833             }
4834             
4835         }
4836         this.active = state;
4837         
4838         if (!state ) {
4839             this.el.removeClass('active');
4840         } else if (!this.el.hasClass('active')) {
4841             this.el.addClass('active');
4842         }
4843         if (fire) {
4844             this.fireEvent('changed', this, state);
4845         }
4846         
4847         // show a panel if it's registered and related..
4848         
4849         if (!this.navId || !this.tabId || !state || is_was_active) {
4850             return;
4851         }
4852         
4853         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4854         if (!tg) {
4855             return;
4856         }
4857         var pan = tg.getPanelByName(this.tabId);
4858         if (!pan) {
4859             return;
4860         }
4861         // if we can not flip to new panel - go back to old nav highlight..
4862         if (false == tg.showPanel(pan)) {
4863             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4864             if (nv) {
4865                 var onav = nv.getWasActive();
4866                 if (onav) {
4867                     onav.setActive(true, false, true);
4868                 }
4869             }
4870             
4871         }
4872         
4873         
4874         
4875     },
4876      // this should not be here...
4877     setDisabled : function(state)
4878     {
4879         this.disabled = state;
4880         if (!state ) {
4881             this.el.removeClass('disabled');
4882         } else if (!this.el.hasClass('disabled')) {
4883             this.el.addClass('disabled');
4884         }
4885         
4886     },
4887     
4888     /**
4889      * Fetch the element to display the tooltip on.
4890      * @return {Roo.Element} defaults to this.el
4891      */
4892     tooltipEl : function()
4893     {
4894         return this.el.select('' + this.tagtype + '', true).first();
4895     },
4896     
4897     scrollToElement : function(e)
4898     {
4899         var c = document.body;
4900         
4901         /*
4902          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4903          */
4904         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4905             c = document.documentElement;
4906         }
4907         
4908         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4909         
4910         if(!target){
4911             return;
4912         }
4913
4914         var o = target.calcOffsetsTo(c);
4915         
4916         var options = {
4917             target : target,
4918             value : o[1]
4919         };
4920         
4921         this.fireEvent('scrollto', this, options, e);
4922         
4923         Roo.get(c).scrollTo('top', options.value, true);
4924         
4925         return;
4926     }
4927 });
4928  
4929
4930  /*
4931  * - LGPL
4932  *
4933  * sidebar item
4934  *
4935  *  li
4936  *    <span> icon </span>
4937  *    <span> text </span>
4938  *    <span>badge </span>
4939  */
4940
4941 /**
4942  * @class Roo.bootstrap.NavSidebarItem
4943  * @extends Roo.bootstrap.NavItem
4944  * Bootstrap Navbar.NavSidebarItem class
4945  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4946  * {Boolean} open is the menu open
4947  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4948  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4949  * {String} buttonSize (sm|md|lg)the extra classes for the button
4950  * {Boolean} showArrow show arrow next to the text (default true)
4951  * @constructor
4952  * Create a new Navbar Button
4953  * @param {Object} config The config object
4954  */
4955 Roo.bootstrap.NavSidebarItem = function(config){
4956     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4957     this.addEvents({
4958         // raw events
4959         /**
4960          * @event click
4961          * The raw click event for the entire grid.
4962          * @param {Roo.EventObject} e
4963          */
4964         "click" : true,
4965          /**
4966             * @event changed
4967             * Fires when the active item active state changes
4968             * @param {Roo.bootstrap.NavSidebarItem} this
4969             * @param {boolean} state the new state
4970              
4971          */
4972         'changed': true
4973     });
4974    
4975 };
4976
4977 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4978     
4979     badgeWeight : 'default',
4980     
4981     open: false,
4982     
4983     buttonView : false,
4984     
4985     buttonWeight : 'default',
4986     
4987     buttonSize : 'md',
4988     
4989     showArrow : true,
4990     
4991     getAutoCreate : function(){
4992         
4993         
4994         var a = {
4995                 tag: 'a',
4996                 href : this.href || '#',
4997                 cls: '',
4998                 html : '',
4999                 cn : []
5000         };
5001         
5002         if(this.buttonView){
5003             a = {
5004                 tag: 'button',
5005                 href : this.href || '#',
5006                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5007                 html : this.html,
5008                 cn : []
5009             };
5010         }
5011         
5012         var cfg = {
5013             tag: 'li',
5014             cls: '',
5015             cn: [ a ]
5016         };
5017         
5018         if (this.active) {
5019             cfg.cls += ' active';
5020         }
5021         
5022         if (this.disabled) {
5023             cfg.cls += ' disabled';
5024         }
5025         if (this.open) {
5026             cfg.cls += ' open x-open';
5027         }
5028         // left icon..
5029         if (this.glyphicon || this.icon) {
5030             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5031             a.cn.push({ tag : 'i', cls : c }) ;
5032         }
5033         
5034         if(!this.buttonView){
5035             var span = {
5036                 tag: 'span',
5037                 html : this.html || ''
5038             };
5039
5040             a.cn.push(span);
5041             
5042         }
5043         
5044         if (this.badge !== '') {
5045             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5046         }
5047         
5048         if (this.menu) {
5049             
5050             if(this.showArrow){
5051                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5052             }
5053             
5054             a.cls += ' dropdown-toggle treeview' ;
5055         }
5056         
5057         return cfg;
5058     },
5059     
5060     initEvents : function()
5061     { 
5062         if (typeof (this.menu) != 'undefined') {
5063             this.menu.parentType = this.xtype;
5064             this.menu.triggerEl = this.el;
5065             this.menu = this.addxtype(Roo.apply({}, this.menu));
5066         }
5067         
5068         this.el.on('click', this.onClick, this);
5069         
5070         if(this.badge !== ''){
5071             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5072         }
5073         
5074     },
5075     
5076     onClick : function(e)
5077     {
5078         if(this.disabled){
5079             e.preventDefault();
5080             return;
5081         }
5082         
5083         if(this.preventDefault){
5084             e.preventDefault();
5085         }
5086         
5087         this.fireEvent('click', this);
5088     },
5089     
5090     disable : function()
5091     {
5092         this.setDisabled(true);
5093     },
5094     
5095     enable : function()
5096     {
5097         this.setDisabled(false);
5098     },
5099     
5100     setDisabled : function(state)
5101     {
5102         if(this.disabled == state){
5103             return;
5104         }
5105         
5106         this.disabled = state;
5107         
5108         if (state) {
5109             this.el.addClass('disabled');
5110             return;
5111         }
5112         
5113         this.el.removeClass('disabled');
5114         
5115         return;
5116     },
5117     
5118     setActive : function(state)
5119     {
5120         if(this.active == state){
5121             return;
5122         }
5123         
5124         this.active = state;
5125         
5126         if (state) {
5127             this.el.addClass('active');
5128             return;
5129         }
5130         
5131         this.el.removeClass('active');
5132         
5133         return;
5134     },
5135     
5136     isActive: function () 
5137     {
5138         return this.active;
5139     },
5140     
5141     setBadge : function(str)
5142     {
5143         if(!this.badgeEl){
5144             return;
5145         }
5146         
5147         this.badgeEl.dom.innerHTML = str;
5148     }
5149     
5150    
5151      
5152  
5153 });
5154  
5155
5156  /*
5157  * - LGPL
5158  *
5159  * row
5160  * 
5161  */
5162
5163 /**
5164  * @class Roo.bootstrap.Row
5165  * @extends Roo.bootstrap.Component
5166  * Bootstrap Row class (contains columns...)
5167  * 
5168  * @constructor
5169  * Create a new Row
5170  * @param {Object} config The config object
5171  */
5172
5173 Roo.bootstrap.Row = function(config){
5174     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5175 };
5176
5177 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5178     
5179     getAutoCreate : function(){
5180        return {
5181             cls: 'row clearfix'
5182        };
5183     }
5184     
5185     
5186 });
5187
5188  
5189
5190  /*
5191  * - LGPL
5192  *
5193  * element
5194  * 
5195  */
5196
5197 /**
5198  * @class Roo.bootstrap.Element
5199  * @extends Roo.bootstrap.Component
5200  * Bootstrap Element class
5201  * @cfg {String} html contents of the element
5202  * @cfg {String} tag tag of the element
5203  * @cfg {String} cls class of the element
5204  * @cfg {Boolean} preventDefault (true|false) default false
5205  * @cfg {Boolean} clickable (true|false) default false
5206  * 
5207  * @constructor
5208  * Create a new Element
5209  * @param {Object} config The config object
5210  */
5211
5212 Roo.bootstrap.Element = function(config){
5213     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5214     
5215     this.addEvents({
5216         // raw events
5217         /**
5218          * @event click
5219          * When a element is chick
5220          * @param {Roo.bootstrap.Element} this
5221          * @param {Roo.EventObject} e
5222          */
5223         "click" : true
5224     });
5225 };
5226
5227 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5228     
5229     tag: 'div',
5230     cls: '',
5231     html: '',
5232     preventDefault: false, 
5233     clickable: false,
5234     
5235     getAutoCreate : function(){
5236         
5237         var cfg = {
5238             tag: this.tag,
5239             // cls: this.cls, double assign in parent class Component.js :: onRender
5240             html: this.html
5241         };
5242         
5243         return cfg;
5244     },
5245     
5246     initEvents: function() 
5247     {
5248         Roo.bootstrap.Element.superclass.initEvents.call(this);
5249         
5250         if(this.clickable){
5251             this.el.on('click', this.onClick, this);
5252         }
5253         
5254     },
5255     
5256     onClick : function(e)
5257     {
5258         if(this.preventDefault){
5259             e.preventDefault();
5260         }
5261         
5262         this.fireEvent('click', this, e);
5263     },
5264     
5265     getValue : function()
5266     {
5267         return this.el.dom.innerHTML;
5268     },
5269     
5270     setValue : function(value)
5271     {
5272         this.el.dom.innerHTML = value;
5273     }
5274    
5275 });
5276
5277  
5278
5279  /*
5280  * - LGPL
5281  *
5282  * pagination
5283  * 
5284  */
5285
5286 /**
5287  * @class Roo.bootstrap.Pagination
5288  * @extends Roo.bootstrap.Component
5289  * Bootstrap Pagination class
5290  * @cfg {String} size xs | sm | md | lg
5291  * @cfg {Boolean} inverse false | true
5292  * 
5293  * @constructor
5294  * Create a new Pagination
5295  * @param {Object} config The config object
5296  */
5297
5298 Roo.bootstrap.Pagination = function(config){
5299     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5300 };
5301
5302 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5303     
5304     cls: false,
5305     size: false,
5306     inverse: false,
5307     
5308     getAutoCreate : function(){
5309         var cfg = {
5310             tag: 'ul',
5311                 cls: 'pagination'
5312         };
5313         if (this.inverse) {
5314             cfg.cls += ' inverse';
5315         }
5316         if (this.html) {
5317             cfg.html=this.html;
5318         }
5319         if (this.cls) {
5320             cfg.cls += " " + this.cls;
5321         }
5322         return cfg;
5323     }
5324    
5325 });
5326
5327  
5328
5329  /*
5330  * - LGPL
5331  *
5332  * Pagination item
5333  * 
5334  */
5335
5336
5337 /**
5338  * @class Roo.bootstrap.PaginationItem
5339  * @extends Roo.bootstrap.Component
5340  * Bootstrap PaginationItem class
5341  * @cfg {String} html text
5342  * @cfg {String} href the link
5343  * @cfg {Boolean} preventDefault (true | false) default true
5344  * @cfg {Boolean} active (true | false) default false
5345  * @cfg {Boolean} disabled default false
5346  * 
5347  * 
5348  * @constructor
5349  * Create a new PaginationItem
5350  * @param {Object} config The config object
5351  */
5352
5353
5354 Roo.bootstrap.PaginationItem = function(config){
5355     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5356     this.addEvents({
5357         // raw events
5358         /**
5359          * @event click
5360          * The raw click event for the entire grid.
5361          * @param {Roo.EventObject} e
5362          */
5363         "click" : true
5364     });
5365 };
5366
5367 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5368     
5369     href : false,
5370     html : false,
5371     preventDefault: true,
5372     active : false,
5373     cls : false,
5374     disabled: false,
5375     
5376     getAutoCreate : function(){
5377         var cfg= {
5378             tag: 'li',
5379             cn: [
5380                 {
5381                     tag : 'a',
5382                     href : this.href ? this.href : '#',
5383                     html : this.html ? this.html : ''
5384                 }
5385             ]
5386         };
5387         
5388         if(this.cls){
5389             cfg.cls = this.cls;
5390         }
5391         
5392         if(this.disabled){
5393             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5394         }
5395         
5396         if(this.active){
5397             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5398         }
5399         
5400         return cfg;
5401     },
5402     
5403     initEvents: function() {
5404         
5405         this.el.on('click', this.onClick, this);
5406         
5407     },
5408     onClick : function(e)
5409     {
5410         Roo.log('PaginationItem on click ');
5411         if(this.preventDefault){
5412             e.preventDefault();
5413         }
5414         
5415         if(this.disabled){
5416             return;
5417         }
5418         
5419         this.fireEvent('click', this, e);
5420     }
5421    
5422 });
5423
5424  
5425
5426  /*
5427  * - LGPL
5428  *
5429  * slider
5430  * 
5431  */
5432
5433
5434 /**
5435  * @class Roo.bootstrap.Slider
5436  * @extends Roo.bootstrap.Component
5437  * Bootstrap Slider class
5438  *    
5439  * @constructor
5440  * Create a new Slider
5441  * @param {Object} config The config object
5442  */
5443
5444 Roo.bootstrap.Slider = function(config){
5445     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5446 };
5447
5448 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5449     
5450     getAutoCreate : function(){
5451         
5452         var cfg = {
5453             tag: 'div',
5454             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5455             cn: [
5456                 {
5457                     tag: 'a',
5458                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5459                 }
5460             ]
5461         };
5462         
5463         return cfg;
5464     }
5465    
5466 });
5467
5468  /*
5469  * Based on:
5470  * Ext JS Library 1.1.1
5471  * Copyright(c) 2006-2007, Ext JS, LLC.
5472  *
5473  * Originally Released Under LGPL - original licence link has changed is not relivant.
5474  *
5475  * Fork - LGPL
5476  * <script type="text/javascript">
5477  */
5478  
5479
5480 /**
5481  * @class Roo.grid.ColumnModel
5482  * @extends Roo.util.Observable
5483  * This is the default implementation of a ColumnModel used by the Grid. It defines
5484  * the columns in the grid.
5485  * <br>Usage:<br>
5486  <pre><code>
5487  var colModel = new Roo.grid.ColumnModel([
5488         {header: "Ticker", width: 60, sortable: true, locked: true},
5489         {header: "Company Name", width: 150, sortable: true},
5490         {header: "Market Cap.", width: 100, sortable: true},
5491         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5492         {header: "Employees", width: 100, sortable: true, resizable: false}
5493  ]);
5494  </code></pre>
5495  * <p>
5496  
5497  * The config options listed for this class are options which may appear in each
5498  * individual column definition.
5499  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5500  * @constructor
5501  * @param {Object} config An Array of column config objects. See this class's
5502  * config objects for details.
5503 */
5504 Roo.grid.ColumnModel = function(config){
5505         /**
5506      * The config passed into the constructor
5507      */
5508     this.config = config;
5509     this.lookup = {};
5510
5511     // if no id, create one
5512     // if the column does not have a dataIndex mapping,
5513     // map it to the order it is in the config
5514     for(var i = 0, len = config.length; i < len; i++){
5515         var c = config[i];
5516         if(typeof c.dataIndex == "undefined"){
5517             c.dataIndex = i;
5518         }
5519         if(typeof c.renderer == "string"){
5520             c.renderer = Roo.util.Format[c.renderer];
5521         }
5522         if(typeof c.id == "undefined"){
5523             c.id = Roo.id();
5524         }
5525         if(c.editor && c.editor.xtype){
5526             c.editor  = Roo.factory(c.editor, Roo.grid);
5527         }
5528         if(c.editor && c.editor.isFormField){
5529             c.editor = new Roo.grid.GridEditor(c.editor);
5530         }
5531         this.lookup[c.id] = c;
5532     }
5533
5534     /**
5535      * The width of columns which have no width specified (defaults to 100)
5536      * @type Number
5537      */
5538     this.defaultWidth = 100;
5539
5540     /**
5541      * Default sortable of columns which have no sortable specified (defaults to false)
5542      * @type Boolean
5543      */
5544     this.defaultSortable = false;
5545
5546     this.addEvents({
5547         /**
5548              * @event widthchange
5549              * Fires when the width of a column changes.
5550              * @param {ColumnModel} this
5551              * @param {Number} columnIndex The column index
5552              * @param {Number} newWidth The new width
5553              */
5554             "widthchange": true,
5555         /**
5556              * @event headerchange
5557              * Fires when the text of a header changes.
5558              * @param {ColumnModel} this
5559              * @param {Number} columnIndex The column index
5560              * @param {Number} newText The new header text
5561              */
5562             "headerchange": true,
5563         /**
5564              * @event hiddenchange
5565              * Fires when a column is hidden or "unhidden".
5566              * @param {ColumnModel} this
5567              * @param {Number} columnIndex The column index
5568              * @param {Boolean} hidden true if hidden, false otherwise
5569              */
5570             "hiddenchange": true,
5571             /**
5572          * @event columnmoved
5573          * Fires when a column is moved.
5574          * @param {ColumnModel} this
5575          * @param {Number} oldIndex
5576          * @param {Number} newIndex
5577          */
5578         "columnmoved" : true,
5579         /**
5580          * @event columlockchange
5581          * Fires when a column's locked state is changed
5582          * @param {ColumnModel} this
5583          * @param {Number} colIndex
5584          * @param {Boolean} locked true if locked
5585          */
5586         "columnlockchange" : true
5587     });
5588     Roo.grid.ColumnModel.superclass.constructor.call(this);
5589 };
5590 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5591     /**
5592      * @cfg {String} header The header text to display in the Grid view.
5593      */
5594     /**
5595      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5596      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5597      * specified, the column's index is used as an index into the Record's data Array.
5598      */
5599     /**
5600      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5601      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5602      */
5603     /**
5604      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5605      * Defaults to the value of the {@link #defaultSortable} property.
5606      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5607      */
5608     /**
5609      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5610      */
5611     /**
5612      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5613      */
5614     /**
5615      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5616      */
5617     /**
5618      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5619      */
5620     /**
5621      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5622      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5623      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5624      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5625      */
5626        /**
5627      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5628      */
5629     /**
5630      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5631      */
5632     /**
5633      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5634      */
5635     /**
5636      * @cfg {String} cursor (Optional)
5637      */
5638     /**
5639      * @cfg {String} tooltip (Optional)
5640      */
5641     /**
5642      * @cfg {Number} xs (Optional)
5643      */
5644     /**
5645      * @cfg {Number} sm (Optional)
5646      */
5647     /**
5648      * @cfg {Number} md (Optional)
5649      */
5650     /**
5651      * @cfg {Number} lg (Optional)
5652      */
5653     /**
5654      * Returns the id of the column at the specified index.
5655      * @param {Number} index The column index
5656      * @return {String} the id
5657      */
5658     getColumnId : function(index){
5659         return this.config[index].id;
5660     },
5661
5662     /**
5663      * Returns the column for a specified id.
5664      * @param {String} id The column id
5665      * @return {Object} the column
5666      */
5667     getColumnById : function(id){
5668         return this.lookup[id];
5669     },
5670
5671     
5672     /**
5673      * Returns the column for a specified dataIndex.
5674      * @param {String} dataIndex The column dataIndex
5675      * @return {Object|Boolean} the column or false if not found
5676      */
5677     getColumnByDataIndex: function(dataIndex){
5678         var index = this.findColumnIndex(dataIndex);
5679         return index > -1 ? this.config[index] : false;
5680     },
5681     
5682     /**
5683      * Returns the index for a specified column id.
5684      * @param {String} id The column id
5685      * @return {Number} the index, or -1 if not found
5686      */
5687     getIndexById : function(id){
5688         for(var i = 0, len = this.config.length; i < len; i++){
5689             if(this.config[i].id == id){
5690                 return i;
5691             }
5692         }
5693         return -1;
5694     },
5695     
5696     /**
5697      * Returns the index for a specified column dataIndex.
5698      * @param {String} dataIndex The column dataIndex
5699      * @return {Number} the index, or -1 if not found
5700      */
5701     
5702     findColumnIndex : function(dataIndex){
5703         for(var i = 0, len = this.config.length; i < len; i++){
5704             if(this.config[i].dataIndex == dataIndex){
5705                 return i;
5706             }
5707         }
5708         return -1;
5709     },
5710     
5711     
5712     moveColumn : function(oldIndex, newIndex){
5713         var c = this.config[oldIndex];
5714         this.config.splice(oldIndex, 1);
5715         this.config.splice(newIndex, 0, c);
5716         this.dataMap = null;
5717         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5718     },
5719
5720     isLocked : function(colIndex){
5721         return this.config[colIndex].locked === true;
5722     },
5723
5724     setLocked : function(colIndex, value, suppressEvent){
5725         if(this.isLocked(colIndex) == value){
5726             return;
5727         }
5728         this.config[colIndex].locked = value;
5729         if(!suppressEvent){
5730             this.fireEvent("columnlockchange", this, colIndex, value);
5731         }
5732     },
5733
5734     getTotalLockedWidth : function(){
5735         var totalWidth = 0;
5736         for(var i = 0; i < this.config.length; i++){
5737             if(this.isLocked(i) && !this.isHidden(i)){
5738                 this.totalWidth += this.getColumnWidth(i);
5739             }
5740         }
5741         return totalWidth;
5742     },
5743
5744     getLockedCount : function(){
5745         for(var i = 0, len = this.config.length; i < len; i++){
5746             if(!this.isLocked(i)){
5747                 return i;
5748             }
5749         }
5750         
5751         return this.config.length;
5752     },
5753
5754     /**
5755      * Returns the number of columns.
5756      * @return {Number}
5757      */
5758     getColumnCount : function(visibleOnly){
5759         if(visibleOnly === true){
5760             var c = 0;
5761             for(var i = 0, len = this.config.length; i < len; i++){
5762                 if(!this.isHidden(i)){
5763                     c++;
5764                 }
5765             }
5766             return c;
5767         }
5768         return this.config.length;
5769     },
5770
5771     /**
5772      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5773      * @param {Function} fn
5774      * @param {Object} scope (optional)
5775      * @return {Array} result
5776      */
5777     getColumnsBy : function(fn, scope){
5778         var r = [];
5779         for(var i = 0, len = this.config.length; i < len; i++){
5780             var c = this.config[i];
5781             if(fn.call(scope||this, c, i) === true){
5782                 r[r.length] = c;
5783             }
5784         }
5785         return r;
5786     },
5787
5788     /**
5789      * Returns true if the specified column is sortable.
5790      * @param {Number} col The column index
5791      * @return {Boolean}
5792      */
5793     isSortable : function(col){
5794         if(typeof this.config[col].sortable == "undefined"){
5795             return this.defaultSortable;
5796         }
5797         return this.config[col].sortable;
5798     },
5799
5800     /**
5801      * Returns the rendering (formatting) function defined for the column.
5802      * @param {Number} col The column index.
5803      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5804      */
5805     getRenderer : function(col){
5806         if(!this.config[col].renderer){
5807             return Roo.grid.ColumnModel.defaultRenderer;
5808         }
5809         return this.config[col].renderer;
5810     },
5811
5812     /**
5813      * Sets the rendering (formatting) function for a column.
5814      * @param {Number} col The column index
5815      * @param {Function} fn The function to use to process the cell's raw data
5816      * to return HTML markup for the grid view. The render function is called with
5817      * the following parameters:<ul>
5818      * <li>Data value.</li>
5819      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5820      * <li>css A CSS style string to apply to the table cell.</li>
5821      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5822      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5823      * <li>Row index</li>
5824      * <li>Column index</li>
5825      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5826      */
5827     setRenderer : function(col, fn){
5828         this.config[col].renderer = fn;
5829     },
5830
5831     /**
5832      * Returns the width for the specified column.
5833      * @param {Number} col The column index
5834      * @return {Number}
5835      */
5836     getColumnWidth : function(col){
5837         return this.config[col].width * 1 || this.defaultWidth;
5838     },
5839
5840     /**
5841      * Sets the width for a column.
5842      * @param {Number} col The column index
5843      * @param {Number} width The new width
5844      */
5845     setColumnWidth : function(col, width, suppressEvent){
5846         this.config[col].width = width;
5847         this.totalWidth = null;
5848         if(!suppressEvent){
5849              this.fireEvent("widthchange", this, col, width);
5850         }
5851     },
5852
5853     /**
5854      * Returns the total width of all columns.
5855      * @param {Boolean} includeHidden True to include hidden column widths
5856      * @return {Number}
5857      */
5858     getTotalWidth : function(includeHidden){
5859         if(!this.totalWidth){
5860             this.totalWidth = 0;
5861             for(var i = 0, len = this.config.length; i < len; i++){
5862                 if(includeHidden || !this.isHidden(i)){
5863                     this.totalWidth += this.getColumnWidth(i);
5864                 }
5865             }
5866         }
5867         return this.totalWidth;
5868     },
5869
5870     /**
5871      * Returns the header for the specified column.
5872      * @param {Number} col The column index
5873      * @return {String}
5874      */
5875     getColumnHeader : function(col){
5876         return this.config[col].header;
5877     },
5878
5879     /**
5880      * Sets the header for a column.
5881      * @param {Number} col The column index
5882      * @param {String} header The new header
5883      */
5884     setColumnHeader : function(col, header){
5885         this.config[col].header = header;
5886         this.fireEvent("headerchange", this, col, header);
5887     },
5888
5889     /**
5890      * Returns the tooltip for the specified column.
5891      * @param {Number} col The column index
5892      * @return {String}
5893      */
5894     getColumnTooltip : function(col){
5895             return this.config[col].tooltip;
5896     },
5897     /**
5898      * Sets the tooltip for a column.
5899      * @param {Number} col The column index
5900      * @param {String} tooltip The new tooltip
5901      */
5902     setColumnTooltip : function(col, tooltip){
5903             this.config[col].tooltip = tooltip;
5904     },
5905
5906     /**
5907      * Returns the dataIndex for the specified column.
5908      * @param {Number} col The column index
5909      * @return {Number}
5910      */
5911     getDataIndex : function(col){
5912         return this.config[col].dataIndex;
5913     },
5914
5915     /**
5916      * Sets the dataIndex for a column.
5917      * @param {Number} col The column index
5918      * @param {Number} dataIndex The new dataIndex
5919      */
5920     setDataIndex : function(col, dataIndex){
5921         this.config[col].dataIndex = dataIndex;
5922     },
5923
5924     
5925     
5926     /**
5927      * Returns true if the cell is editable.
5928      * @param {Number} colIndex The column index
5929      * @param {Number} rowIndex The row index - this is nto actually used..?
5930      * @return {Boolean}
5931      */
5932     isCellEditable : function(colIndex, rowIndex){
5933         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5934     },
5935
5936     /**
5937      * Returns the editor defined for the cell/column.
5938      * return false or null to disable editing.
5939      * @param {Number} colIndex The column index
5940      * @param {Number} rowIndex The row index
5941      * @return {Object}
5942      */
5943     getCellEditor : function(colIndex, rowIndex){
5944         return this.config[colIndex].editor;
5945     },
5946
5947     /**
5948      * Sets if a column is editable.
5949      * @param {Number} col The column index
5950      * @param {Boolean} editable True if the column is editable
5951      */
5952     setEditable : function(col, editable){
5953         this.config[col].editable = editable;
5954     },
5955
5956
5957     /**
5958      * Returns true if the column is hidden.
5959      * @param {Number} colIndex The column index
5960      * @return {Boolean}
5961      */
5962     isHidden : function(colIndex){
5963         return this.config[colIndex].hidden;
5964     },
5965
5966
5967     /**
5968      * Returns true if the column width cannot be changed
5969      */
5970     isFixed : function(colIndex){
5971         return this.config[colIndex].fixed;
5972     },
5973
5974     /**
5975      * Returns true if the column can be resized
5976      * @return {Boolean}
5977      */
5978     isResizable : function(colIndex){
5979         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5980     },
5981     /**
5982      * Sets if a column is hidden.
5983      * @param {Number} colIndex The column index
5984      * @param {Boolean} hidden True if the column is hidden
5985      */
5986     setHidden : function(colIndex, hidden){
5987         this.config[colIndex].hidden = hidden;
5988         this.totalWidth = null;
5989         this.fireEvent("hiddenchange", this, colIndex, hidden);
5990     },
5991
5992     /**
5993      * Sets the editor for a column.
5994      * @param {Number} col The column index
5995      * @param {Object} editor The editor object
5996      */
5997     setEditor : function(col, editor){
5998         this.config[col].editor = editor;
5999     }
6000 });
6001
6002 Roo.grid.ColumnModel.defaultRenderer = function(value)
6003 {
6004     if(typeof value == "object") {
6005         return value;
6006     }
6007         if(typeof value == "string" && value.length < 1){
6008             return "&#160;";
6009         }
6010     
6011         return String.format("{0}", value);
6012 };
6013
6014 // Alias for backwards compatibility
6015 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6016 /*
6017  * Based on:
6018  * Ext JS Library 1.1.1
6019  * Copyright(c) 2006-2007, Ext JS, LLC.
6020  *
6021  * Originally Released Under LGPL - original licence link has changed is not relivant.
6022  *
6023  * Fork - LGPL
6024  * <script type="text/javascript">
6025  */
6026  
6027 /**
6028  * @class Roo.LoadMask
6029  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6030  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6031  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6032  * element's UpdateManager load indicator and will be destroyed after the initial load.
6033  * @constructor
6034  * Create a new LoadMask
6035  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6036  * @param {Object} config The config object
6037  */
6038 Roo.LoadMask = function(el, config){
6039     this.el = Roo.get(el);
6040     Roo.apply(this, config);
6041     if(this.store){
6042         this.store.on('beforeload', this.onBeforeLoad, this);
6043         this.store.on('load', this.onLoad, this);
6044         this.store.on('loadexception', this.onLoadException, this);
6045         this.removeMask = false;
6046     }else{
6047         var um = this.el.getUpdateManager();
6048         um.showLoadIndicator = false; // disable the default indicator
6049         um.on('beforeupdate', this.onBeforeLoad, this);
6050         um.on('update', this.onLoad, this);
6051         um.on('failure', this.onLoad, this);
6052         this.removeMask = true;
6053     }
6054 };
6055
6056 Roo.LoadMask.prototype = {
6057     /**
6058      * @cfg {Boolean} removeMask
6059      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6060      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6061      */
6062     /**
6063      * @cfg {String} msg
6064      * The text to display in a centered loading message box (defaults to 'Loading...')
6065      */
6066     msg : 'Loading...',
6067     /**
6068      * @cfg {String} msgCls
6069      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6070      */
6071     msgCls : 'x-mask-loading',
6072
6073     /**
6074      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6075      * @type Boolean
6076      */
6077     disabled: false,
6078
6079     /**
6080      * Disables the mask to prevent it from being displayed
6081      */
6082     disable : function(){
6083        this.disabled = true;
6084     },
6085
6086     /**
6087      * Enables the mask so that it can be displayed
6088      */
6089     enable : function(){
6090         this.disabled = false;
6091     },
6092     
6093     onLoadException : function()
6094     {
6095         Roo.log(arguments);
6096         
6097         if (typeof(arguments[3]) != 'undefined') {
6098             Roo.MessageBox.alert("Error loading",arguments[3]);
6099         } 
6100         /*
6101         try {
6102             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6103                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6104             }   
6105         } catch(e) {
6106             
6107         }
6108         */
6109     
6110         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6111     },
6112     // private
6113     onLoad : function()
6114     {
6115         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6116     },
6117
6118     // private
6119     onBeforeLoad : function(){
6120         if(!this.disabled){
6121             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6122         }
6123     },
6124
6125     // private
6126     destroy : function(){
6127         if(this.store){
6128             this.store.un('beforeload', this.onBeforeLoad, this);
6129             this.store.un('load', this.onLoad, this);
6130             this.store.un('loadexception', this.onLoadException, this);
6131         }else{
6132             var um = this.el.getUpdateManager();
6133             um.un('beforeupdate', this.onBeforeLoad, this);
6134             um.un('update', this.onLoad, this);
6135             um.un('failure', this.onLoad, this);
6136         }
6137     }
6138 };/*
6139  * - LGPL
6140  *
6141  * table
6142  * 
6143  */
6144
6145 /**
6146  * @class Roo.bootstrap.Table
6147  * @extends Roo.bootstrap.Component
6148  * Bootstrap Table class
6149  * @cfg {String} cls table class
6150  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6151  * @cfg {String} bgcolor Specifies the background color for a table
6152  * @cfg {Number} border Specifies whether the table cells should have borders or not
6153  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6154  * @cfg {Number} cellspacing Specifies the space between cells
6155  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6156  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6157  * @cfg {String} sortable Specifies that the table should be sortable
6158  * @cfg {String} summary Specifies a summary of the content of a table
6159  * @cfg {Number} width Specifies the width of a table
6160  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6161  * 
6162  * @cfg {boolean} striped Should the rows be alternative striped
6163  * @cfg {boolean} bordered Add borders to the table
6164  * @cfg {boolean} hover Add hover highlighting
6165  * @cfg {boolean} condensed Format condensed
6166  * @cfg {boolean} responsive Format condensed
6167  * @cfg {Boolean} loadMask (true|false) default false
6168  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6169  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6170  * @cfg {Boolean} rowSelection (true|false) default false
6171  * @cfg {Boolean} cellSelection (true|false) default false
6172  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6173  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6174  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6175  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6176  
6177  * 
6178  * @constructor
6179  * Create a new Table
6180  * @param {Object} config The config object
6181  */
6182
6183 Roo.bootstrap.Table = function(config){
6184     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6185     
6186   
6187     
6188     // BC...
6189     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6190     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6191     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6192     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6193     
6194     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6195     if (this.sm) {
6196         this.sm.grid = this;
6197         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6198         this.sm = this.selModel;
6199         this.sm.xmodule = this.xmodule || false;
6200     }
6201     
6202     if (this.cm && typeof(this.cm.config) == 'undefined') {
6203         this.colModel = new Roo.grid.ColumnModel(this.cm);
6204         this.cm = this.colModel;
6205         this.cm.xmodule = this.xmodule || false;
6206     }
6207     if (this.store) {
6208         this.store= Roo.factory(this.store, Roo.data);
6209         this.ds = this.store;
6210         this.ds.xmodule = this.xmodule || false;
6211          
6212     }
6213     if (this.footer && this.store) {
6214         this.footer.dataSource = this.ds;
6215         this.footer = Roo.factory(this.footer);
6216     }
6217     
6218     /** @private */
6219     this.addEvents({
6220         /**
6221          * @event cellclick
6222          * Fires when a cell is clicked
6223          * @param {Roo.bootstrap.Table} this
6224          * @param {Roo.Element} el
6225          * @param {Number} rowIndex
6226          * @param {Number} columnIndex
6227          * @param {Roo.EventObject} e
6228          */
6229         "cellclick" : true,
6230         /**
6231          * @event celldblclick
6232          * Fires when a cell is double clicked
6233          * @param {Roo.bootstrap.Table} this
6234          * @param {Roo.Element} el
6235          * @param {Number} rowIndex
6236          * @param {Number} columnIndex
6237          * @param {Roo.EventObject} e
6238          */
6239         "celldblclick" : true,
6240         /**
6241          * @event rowclick
6242          * Fires when a row is clicked
6243          * @param {Roo.bootstrap.Table} this
6244          * @param {Roo.Element} el
6245          * @param {Number} rowIndex
6246          * @param {Roo.EventObject} e
6247          */
6248         "rowclick" : true,
6249         /**
6250          * @event rowdblclick
6251          * Fires when a row is double clicked
6252          * @param {Roo.bootstrap.Table} this
6253          * @param {Roo.Element} el
6254          * @param {Number} rowIndex
6255          * @param {Roo.EventObject} e
6256          */
6257         "rowdblclick" : true,
6258         /**
6259          * @event mouseover
6260          * Fires when a mouseover occur
6261          * @param {Roo.bootstrap.Table} this
6262          * @param {Roo.Element} el
6263          * @param {Number} rowIndex
6264          * @param {Number} columnIndex
6265          * @param {Roo.EventObject} e
6266          */
6267         "mouseover" : true,
6268         /**
6269          * @event mouseout
6270          * Fires when a mouseout occur
6271          * @param {Roo.bootstrap.Table} this
6272          * @param {Roo.Element} el
6273          * @param {Number} rowIndex
6274          * @param {Number} columnIndex
6275          * @param {Roo.EventObject} e
6276          */
6277         "mouseout" : true,
6278         /**
6279          * @event rowclass
6280          * Fires when a row is rendered, so you can change add a style to it.
6281          * @param {Roo.bootstrap.Table} this
6282          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6283          */
6284         'rowclass' : true,
6285           /**
6286          * @event rowsrendered
6287          * Fires when all the  rows have been rendered
6288          * @param {Roo.bootstrap.Table} this
6289          */
6290         'rowsrendered' : true,
6291         /**
6292          * @event contextmenu
6293          * The raw contextmenu event for the entire grid.
6294          * @param {Roo.EventObject} e
6295          */
6296         "contextmenu" : true,
6297         /**
6298          * @event rowcontextmenu
6299          * Fires when a row is right clicked
6300          * @param {Roo.bootstrap.Table} this
6301          * @param {Number} rowIndex
6302          * @param {Roo.EventObject} e
6303          */
6304         "rowcontextmenu" : true,
6305         /**
6306          * @event cellcontextmenu
6307          * Fires when a cell is right clicked
6308          * @param {Roo.bootstrap.Table} this
6309          * @param {Number} rowIndex
6310          * @param {Number} cellIndex
6311          * @param {Roo.EventObject} e
6312          */
6313          "cellcontextmenu" : true,
6314          /**
6315          * @event headercontextmenu
6316          * Fires when a header is right clicked
6317          * @param {Roo.bootstrap.Table} this
6318          * @param {Number} columnIndex
6319          * @param {Roo.EventObject} e
6320          */
6321         "headercontextmenu" : true
6322     });
6323 };
6324
6325 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6326     
6327     cls: false,
6328     align: false,
6329     bgcolor: false,
6330     border: false,
6331     cellpadding: false,
6332     cellspacing: false,
6333     frame: false,
6334     rules: false,
6335     sortable: false,
6336     summary: false,
6337     width: false,
6338     striped : false,
6339     scrollBody : false,
6340     bordered: false,
6341     hover:  false,
6342     condensed : false,
6343     responsive : false,
6344     sm : false,
6345     cm : false,
6346     store : false,
6347     loadMask : false,
6348     footerShow : true,
6349     headerShow : true,
6350   
6351     rowSelection : false,
6352     cellSelection : false,
6353     layout : false,
6354     
6355     // Roo.Element - the tbody
6356     mainBody: false,
6357     // Roo.Element - thead element
6358     mainHead: false,
6359     
6360     container: false, // used by gridpanel...
6361     
6362     lazyLoad : false,
6363     
6364     CSS : Roo.util.CSS,
6365     
6366     auto_hide_footer : false,
6367     
6368     getAutoCreate : function()
6369     {
6370         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6371         
6372         cfg = {
6373             tag: 'table',
6374             cls : 'table',
6375             cn : []
6376         };
6377         if (this.scrollBody) {
6378             cfg.cls += ' table-body-fixed';
6379         }    
6380         if (this.striped) {
6381             cfg.cls += ' table-striped';
6382         }
6383         
6384         if (this.hover) {
6385             cfg.cls += ' table-hover';
6386         }
6387         if (this.bordered) {
6388             cfg.cls += ' table-bordered';
6389         }
6390         if (this.condensed) {
6391             cfg.cls += ' table-condensed';
6392         }
6393         if (this.responsive) {
6394             cfg.cls += ' table-responsive';
6395         }
6396         
6397         if (this.cls) {
6398             cfg.cls+=  ' ' +this.cls;
6399         }
6400         
6401         // this lot should be simplifed...
6402         var _t = this;
6403         var cp = [
6404             'align',
6405             'bgcolor',
6406             'border',
6407             'cellpadding',
6408             'cellspacing',
6409             'frame',
6410             'rules',
6411             'sortable',
6412             'summary',
6413             'width'
6414         ].forEach(function(k) {
6415             if (_t[k]) {
6416                 cfg[k] = _t[k];
6417             }
6418         });
6419         
6420         
6421         if (this.layout) {
6422             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6423         }
6424         
6425         if(this.store || this.cm){
6426             if(this.headerShow){
6427                 cfg.cn.push(this.renderHeader());
6428             }
6429             
6430             cfg.cn.push(this.renderBody());
6431             
6432             if(this.footerShow){
6433                 cfg.cn.push(this.renderFooter());
6434             }
6435             // where does this come from?
6436             //cfg.cls+=  ' TableGrid';
6437         }
6438         
6439         return { cn : [ cfg ] };
6440     },
6441     
6442     initEvents : function()
6443     {   
6444         if(!this.store || !this.cm){
6445             return;
6446         }
6447         if (this.selModel) {
6448             this.selModel.initEvents();
6449         }
6450         
6451         
6452         //Roo.log('initEvents with ds!!!!');
6453         
6454         this.mainBody = this.el.select('tbody', true).first();
6455         this.mainHead = this.el.select('thead', true).first();
6456         this.mainFoot = this.el.select('tfoot', true).first();
6457         
6458         
6459         
6460         var _this = this;
6461         
6462         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6463             e.on('click', _this.sort, _this);
6464         });
6465         
6466         this.mainBody.on("click", this.onClick, this);
6467         this.mainBody.on("dblclick", this.onDblClick, this);
6468         
6469         // why is this done????? = it breaks dialogs??
6470         //this.parent().el.setStyle('position', 'relative');
6471         
6472         
6473         if (this.footer) {
6474             this.footer.parentId = this.id;
6475             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6476             
6477             if(this.lazyLoad){
6478                 this.el.select('tfoot tr td').first().addClass('hide');
6479             }
6480         } 
6481         
6482         if(this.loadMask) {
6483             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6484         }
6485         
6486         this.store.on('load', this.onLoad, this);
6487         this.store.on('beforeload', this.onBeforeLoad, this);
6488         this.store.on('update', this.onUpdate, this);
6489         this.store.on('add', this.onAdd, this);
6490         this.store.on("clear", this.clear, this);
6491         
6492         this.el.on("contextmenu", this.onContextMenu, this);
6493         
6494         this.mainBody.on('scroll', this.onBodyScroll, this);
6495         
6496         this.cm.on("headerchange", this.onHeaderChange, this);
6497         
6498         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6499         
6500     },
6501     
6502     onContextMenu : function(e, t)
6503     {
6504         this.processEvent("contextmenu", e);
6505     },
6506     
6507     processEvent : function(name, e)
6508     {
6509         if (name != 'touchstart' ) {
6510             this.fireEvent(name, e);    
6511         }
6512         
6513         var t = e.getTarget();
6514         
6515         var cell = Roo.get(t);
6516         
6517         if(!cell){
6518             return;
6519         }
6520         
6521         if(cell.findParent('tfoot', false, true)){
6522             return;
6523         }
6524         
6525         if(cell.findParent('thead', false, true)){
6526             
6527             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6528                 cell = Roo.get(t).findParent('th', false, true);
6529                 if (!cell) {
6530                     Roo.log("failed to find th in thead?");
6531                     Roo.log(e.getTarget());
6532                     return;
6533                 }
6534             }
6535             
6536             var cellIndex = cell.dom.cellIndex;
6537             
6538             var ename = name == 'touchstart' ? 'click' : name;
6539             this.fireEvent("header" + ename, this, cellIndex, e);
6540             
6541             return;
6542         }
6543         
6544         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6545             cell = Roo.get(t).findParent('td', false, true);
6546             if (!cell) {
6547                 Roo.log("failed to find th in tbody?");
6548                 Roo.log(e.getTarget());
6549                 return;
6550             }
6551         }
6552         
6553         var row = cell.findParent('tr', false, true);
6554         var cellIndex = cell.dom.cellIndex;
6555         var rowIndex = row.dom.rowIndex - 1;
6556         
6557         if(row !== false){
6558             
6559             this.fireEvent("row" + name, this, rowIndex, e);
6560             
6561             if(cell !== false){
6562             
6563                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6564             }
6565         }
6566         
6567     },
6568     
6569     onMouseover : function(e, el)
6570     {
6571         var cell = Roo.get(el);
6572         
6573         if(!cell){
6574             return;
6575         }
6576         
6577         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6578             cell = cell.findParent('td', false, true);
6579         }
6580         
6581         var row = cell.findParent('tr', false, true);
6582         var cellIndex = cell.dom.cellIndex;
6583         var rowIndex = row.dom.rowIndex - 1; // start from 0
6584         
6585         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6586         
6587     },
6588     
6589     onMouseout : function(e, el)
6590     {
6591         var cell = Roo.get(el);
6592         
6593         if(!cell){
6594             return;
6595         }
6596         
6597         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6598             cell = cell.findParent('td', false, true);
6599         }
6600         
6601         var row = cell.findParent('tr', false, true);
6602         var cellIndex = cell.dom.cellIndex;
6603         var rowIndex = row.dom.rowIndex - 1; // start from 0
6604         
6605         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6606         
6607     },
6608     
6609     onClick : function(e, el)
6610     {
6611         var cell = Roo.get(el);
6612         
6613         if(!cell || (!this.cellSelection && !this.rowSelection)){
6614             return;
6615         }
6616         
6617         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6618             cell = cell.findParent('td', false, true);
6619         }
6620         
6621         if(!cell || typeof(cell) == 'undefined'){
6622             return;
6623         }
6624         
6625         var row = cell.findParent('tr', false, true);
6626         
6627         if(!row || typeof(row) == 'undefined'){
6628             return;
6629         }
6630         
6631         var cellIndex = cell.dom.cellIndex;
6632         var rowIndex = this.getRowIndex(row);
6633         
6634         // why??? - should these not be based on SelectionModel?
6635         if(this.cellSelection){
6636             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6637         }
6638         
6639         if(this.rowSelection){
6640             this.fireEvent('rowclick', this, row, rowIndex, e);
6641         }
6642         
6643         
6644     },
6645         
6646     onDblClick : function(e,el)
6647     {
6648         var cell = Roo.get(el);
6649         
6650         if(!cell || (!this.cellSelection && !this.rowSelection)){
6651             return;
6652         }
6653         
6654         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6655             cell = cell.findParent('td', false, true);
6656         }
6657         
6658         if(!cell || typeof(cell) == 'undefined'){
6659             return;
6660         }
6661         
6662         var row = cell.findParent('tr', false, true);
6663         
6664         if(!row || typeof(row) == 'undefined'){
6665             return;
6666         }
6667         
6668         var cellIndex = cell.dom.cellIndex;
6669         var rowIndex = this.getRowIndex(row);
6670         
6671         if(this.cellSelection){
6672             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6673         }
6674         
6675         if(this.rowSelection){
6676             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6677         }
6678     },
6679     
6680     sort : function(e,el)
6681     {
6682         var col = Roo.get(el);
6683         
6684         if(!col.hasClass('sortable')){
6685             return;
6686         }
6687         
6688         var sort = col.attr('sort');
6689         var dir = 'ASC';
6690         
6691         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6692             dir = 'DESC';
6693         }
6694         
6695         this.store.sortInfo = {field : sort, direction : dir};
6696         
6697         if (this.footer) {
6698             Roo.log("calling footer first");
6699             this.footer.onClick('first');
6700         } else {
6701         
6702             this.store.load({ params : { start : 0 } });
6703         }
6704     },
6705     
6706     renderHeader : function()
6707     {
6708         var header = {
6709             tag: 'thead',
6710             cn : []
6711         };
6712         
6713         var cm = this.cm;
6714         this.totalWidth = 0;
6715         
6716         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6717             
6718             var config = cm.config[i];
6719             
6720             var c = {
6721                 tag: 'th',
6722                 cls : 'x-hcol-' + i,
6723                 style : '',
6724                 html: cm.getColumnHeader(i)
6725             };
6726             
6727             var hh = '';
6728             
6729             if(typeof(config.sortable) != 'undefined' && config.sortable){
6730                 c.cls = 'sortable';
6731                 c.html = '<i class="glyphicon"></i>' + c.html;
6732             }
6733             
6734             if(typeof(config.lgHeader) != 'undefined'){
6735                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6736             }
6737             
6738             if(typeof(config.mdHeader) != 'undefined'){
6739                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6740             }
6741             
6742             if(typeof(config.smHeader) != 'undefined'){
6743                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6744             }
6745             
6746             if(typeof(config.xsHeader) != 'undefined'){
6747                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6748             }
6749             
6750             if(hh.length){
6751                 c.html = hh;
6752             }
6753             
6754             if(typeof(config.tooltip) != 'undefined'){
6755                 c.tooltip = config.tooltip;
6756             }
6757             
6758             if(typeof(config.colspan) != 'undefined'){
6759                 c.colspan = config.colspan;
6760             }
6761             
6762             if(typeof(config.hidden) != 'undefined' && config.hidden){
6763                 c.style += ' display:none;';
6764             }
6765             
6766             if(typeof(config.dataIndex) != 'undefined'){
6767                 c.sort = config.dataIndex;
6768             }
6769             
6770            
6771             
6772             if(typeof(config.align) != 'undefined' && config.align.length){
6773                 c.style += ' text-align:' + config.align + ';';
6774             }
6775             
6776             if(typeof(config.width) != 'undefined'){
6777                 c.style += ' width:' + config.width + 'px;';
6778                 this.totalWidth += config.width;
6779             } else {
6780                 this.totalWidth += 100; // assume minimum of 100 per column?
6781             }
6782             
6783             if(typeof(config.cls) != 'undefined'){
6784                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6785             }
6786             
6787             ['xs','sm','md','lg'].map(function(size){
6788                 
6789                 if(typeof(config[size]) == 'undefined'){
6790                     return;
6791                 }
6792                 
6793                 if (!config[size]) { // 0 = hidden
6794                     c.cls += ' hidden-' + size;
6795                     return;
6796                 }
6797                 
6798                 c.cls += ' col-' + size + '-' + config[size];
6799
6800             });
6801             
6802             header.cn.push(c)
6803         }
6804         
6805         return header;
6806     },
6807     
6808     renderBody : function()
6809     {
6810         var body = {
6811             tag: 'tbody',
6812             cn : [
6813                 {
6814                     tag: 'tr',
6815                     cn : [
6816                         {
6817                             tag : 'td',
6818                             colspan :  this.cm.getColumnCount()
6819                         }
6820                     ]
6821                 }
6822             ]
6823         };
6824         
6825         return body;
6826     },
6827     
6828     renderFooter : function()
6829     {
6830         var footer = {
6831             tag: 'tfoot',
6832             cn : [
6833                 {
6834                     tag: 'tr',
6835                     cn : [
6836                         {
6837                             tag : 'td',
6838                             colspan :  this.cm.getColumnCount()
6839                         }
6840                     ]
6841                 }
6842             ]
6843         };
6844         
6845         return footer;
6846     },
6847     
6848     
6849     
6850     onLoad : function()
6851     {
6852 //        Roo.log('ds onload');
6853         this.clear();
6854         
6855         var _this = this;
6856         var cm = this.cm;
6857         var ds = this.store;
6858         
6859         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6860             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6861             if (_this.store.sortInfo) {
6862                     
6863                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6864                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6865                 }
6866                 
6867                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6868                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6869                 }
6870             }
6871         });
6872         
6873         var tbody =  this.mainBody;
6874               
6875         if(ds.getCount() > 0){
6876             ds.data.each(function(d,rowIndex){
6877                 var row =  this.renderRow(cm, ds, rowIndex);
6878                 
6879                 tbody.createChild(row);
6880                 
6881                 var _this = this;
6882                 
6883                 if(row.cellObjects.length){
6884                     Roo.each(row.cellObjects, function(r){
6885                         _this.renderCellObject(r);
6886                     })
6887                 }
6888                 
6889             }, this);
6890         }
6891         
6892         var tfoot = this.el.select('tfoot', true).first();
6893         
6894         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6895             
6896             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6897             
6898             var total = this.ds.getTotalCount();
6899             
6900             if(this.footer.pageSize < total){
6901                 this.mainFoot.show();
6902             }
6903         }
6904         
6905         Roo.each(this.el.select('tbody td', true).elements, function(e){
6906             e.on('mouseover', _this.onMouseover, _this);
6907         });
6908         
6909         Roo.each(this.el.select('tbody td', true).elements, function(e){
6910             e.on('mouseout', _this.onMouseout, _this);
6911         });
6912         this.fireEvent('rowsrendered', this);
6913         
6914         this.autoSize();
6915     },
6916     
6917     
6918     onUpdate : function(ds,record)
6919     {
6920         this.refreshRow(record);
6921         this.autoSize();
6922     },
6923     
6924     onRemove : function(ds, record, index, isUpdate){
6925         if(isUpdate !== true){
6926             this.fireEvent("beforerowremoved", this, index, record);
6927         }
6928         var bt = this.mainBody.dom;
6929         
6930         var rows = this.el.select('tbody > tr', true).elements;
6931         
6932         if(typeof(rows[index]) != 'undefined'){
6933             bt.removeChild(rows[index].dom);
6934         }
6935         
6936 //        if(bt.rows[index]){
6937 //            bt.removeChild(bt.rows[index]);
6938 //        }
6939         
6940         if(isUpdate !== true){
6941             //this.stripeRows(index);
6942             //this.syncRowHeights(index, index);
6943             //this.layout();
6944             this.fireEvent("rowremoved", this, index, record);
6945         }
6946     },
6947     
6948     onAdd : function(ds, records, rowIndex)
6949     {
6950         //Roo.log('on Add called');
6951         // - note this does not handle multiple adding very well..
6952         var bt = this.mainBody.dom;
6953         for (var i =0 ; i < records.length;i++) {
6954             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6955             //Roo.log(records[i]);
6956             //Roo.log(this.store.getAt(rowIndex+i));
6957             this.insertRow(this.store, rowIndex + i, false);
6958             return;
6959         }
6960         
6961     },
6962     
6963     
6964     refreshRow : function(record){
6965         var ds = this.store, index;
6966         if(typeof record == 'number'){
6967             index = record;
6968             record = ds.getAt(index);
6969         }else{
6970             index = ds.indexOf(record);
6971         }
6972         this.insertRow(ds, index, true);
6973         this.autoSize();
6974         this.onRemove(ds, record, index+1, true);
6975         this.autoSize();
6976         //this.syncRowHeights(index, index);
6977         //this.layout();
6978         this.fireEvent("rowupdated", this, index, record);
6979     },
6980     
6981     insertRow : function(dm, rowIndex, isUpdate){
6982         
6983         if(!isUpdate){
6984             this.fireEvent("beforerowsinserted", this, rowIndex);
6985         }
6986             //var s = this.getScrollState();
6987         var row = this.renderRow(this.cm, this.store, rowIndex);
6988         // insert before rowIndex..
6989         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6990         
6991         var _this = this;
6992                 
6993         if(row.cellObjects.length){
6994             Roo.each(row.cellObjects, function(r){
6995                 _this.renderCellObject(r);
6996             })
6997         }
6998             
6999         if(!isUpdate){
7000             this.fireEvent("rowsinserted", this, rowIndex);
7001             //this.syncRowHeights(firstRow, lastRow);
7002             //this.stripeRows(firstRow);
7003             //this.layout();
7004         }
7005         
7006     },
7007     
7008     
7009     getRowDom : function(rowIndex)
7010     {
7011         var rows = this.el.select('tbody > tr', true).elements;
7012         
7013         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7014         
7015     },
7016     // returns the object tree for a tr..
7017   
7018     
7019     renderRow : function(cm, ds, rowIndex) 
7020     {
7021         var d = ds.getAt(rowIndex);
7022         
7023         var row = {
7024             tag : 'tr',
7025             cls : 'x-row-' + rowIndex,
7026             cn : []
7027         };
7028             
7029         var cellObjects = [];
7030         
7031         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7032             var config = cm.config[i];
7033             
7034             var renderer = cm.getRenderer(i);
7035             var value = '';
7036             var id = false;
7037             
7038             if(typeof(renderer) !== 'undefined'){
7039                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7040             }
7041             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7042             // and are rendered into the cells after the row is rendered - using the id for the element.
7043             
7044             if(typeof(value) === 'object'){
7045                 id = Roo.id();
7046                 cellObjects.push({
7047                     container : id,
7048                     cfg : value 
7049                 })
7050             }
7051             
7052             var rowcfg = {
7053                 record: d,
7054                 rowIndex : rowIndex,
7055                 colIndex : i,
7056                 rowClass : ''
7057             };
7058
7059             this.fireEvent('rowclass', this, rowcfg);
7060             
7061             var td = {
7062                 tag: 'td',
7063                 cls : rowcfg.rowClass + ' x-col-' + i,
7064                 style: '',
7065                 html: (typeof(value) === 'object') ? '' : value
7066             };
7067             
7068             if (id) {
7069                 td.id = id;
7070             }
7071             
7072             if(typeof(config.colspan) != 'undefined'){
7073                 td.colspan = config.colspan;
7074             }
7075             
7076             if(typeof(config.hidden) != 'undefined' && config.hidden){
7077                 td.style += ' display:none;';
7078             }
7079             
7080             if(typeof(config.align) != 'undefined' && config.align.length){
7081                 td.style += ' text-align:' + config.align + ';';
7082             }
7083             if(typeof(config.valign) != 'undefined' && config.valign.length){
7084                 td.style += ' vertical-align:' + config.valign + ';';
7085             }
7086             
7087             if(typeof(config.width) != 'undefined'){
7088                 td.style += ' width:' +  config.width + 'px;';
7089             }
7090             
7091             if(typeof(config.cursor) != 'undefined'){
7092                 td.style += ' cursor:' +  config.cursor + ';';
7093             }
7094             
7095             if(typeof(config.cls) != 'undefined'){
7096                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7097             }
7098             
7099             ['xs','sm','md','lg'].map(function(size){
7100                 
7101                 if(typeof(config[size]) == 'undefined'){
7102                     return;
7103                 }
7104                 
7105                 if (!config[size]) { // 0 = hidden
7106                     td.cls += ' hidden-' + size;
7107                     return;
7108                 }
7109                 
7110                 td.cls += ' col-' + size + '-' + config[size];
7111
7112             });
7113             
7114             row.cn.push(td);
7115            
7116         }
7117         
7118         row.cellObjects = cellObjects;
7119         
7120         return row;
7121           
7122     },
7123     
7124     
7125     
7126     onBeforeLoad : function()
7127     {
7128         
7129     },
7130      /**
7131      * Remove all rows
7132      */
7133     clear : function()
7134     {
7135         this.el.select('tbody', true).first().dom.innerHTML = '';
7136     },
7137     /**
7138      * Show or hide a row.
7139      * @param {Number} rowIndex to show or hide
7140      * @param {Boolean} state hide
7141      */
7142     setRowVisibility : function(rowIndex, state)
7143     {
7144         var bt = this.mainBody.dom;
7145         
7146         var rows = this.el.select('tbody > tr', true).elements;
7147         
7148         if(typeof(rows[rowIndex]) == 'undefined'){
7149             return;
7150         }
7151         rows[rowIndex].dom.style.display = state ? '' : 'none';
7152     },
7153     
7154     
7155     getSelectionModel : function(){
7156         if(!this.selModel){
7157             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7158         }
7159         return this.selModel;
7160     },
7161     /*
7162      * Render the Roo.bootstrap object from renderder
7163      */
7164     renderCellObject : function(r)
7165     {
7166         var _this = this;
7167         
7168         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7169         
7170         var t = r.cfg.render(r.container);
7171         
7172         if(r.cfg.cn){
7173             Roo.each(r.cfg.cn, function(c){
7174                 var child = {
7175                     container: t.getChildContainer(),
7176                     cfg: c
7177                 };
7178                 _this.renderCellObject(child);
7179             })
7180         }
7181     },
7182     
7183     getRowIndex : function(row)
7184     {
7185         var rowIndex = -1;
7186         
7187         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7188             if(el != row){
7189                 return;
7190             }
7191             
7192             rowIndex = index;
7193         });
7194         
7195         return rowIndex;
7196     },
7197      /**
7198      * Returns the grid's underlying element = used by panel.Grid
7199      * @return {Element} The element
7200      */
7201     getGridEl : function(){
7202         return this.el;
7203     },
7204      /**
7205      * Forces a resize - used by panel.Grid
7206      * @return {Element} The element
7207      */
7208     autoSize : function()
7209     {
7210         //var ctr = Roo.get(this.container.dom.parentElement);
7211         var ctr = Roo.get(this.el.dom);
7212         
7213         var thd = this.getGridEl().select('thead',true).first();
7214         var tbd = this.getGridEl().select('tbody', true).first();
7215         var tfd = this.getGridEl().select('tfoot', true).first();
7216         
7217         var cw = ctr.getWidth();
7218         
7219         if (tbd) {
7220             
7221             tbd.setSize(ctr.getWidth(),
7222                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7223             );
7224             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7225             cw -= barsize;
7226         }
7227         cw = Math.max(cw, this.totalWidth);
7228         this.getGridEl().select('tr',true).setWidth(cw);
7229         // resize 'expandable coloumn?
7230         
7231         return; // we doe not have a view in this design..
7232         
7233     },
7234     onBodyScroll: function()
7235     {
7236         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7237         if(this.mainHead){
7238             this.mainHead.setStyle({
7239                 'position' : 'relative',
7240                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7241             });
7242         }
7243         
7244         if(this.lazyLoad){
7245             
7246             var scrollHeight = this.mainBody.dom.scrollHeight;
7247             
7248             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7249             
7250             var height = this.mainBody.getHeight();
7251             
7252             if(scrollHeight - height == scrollTop) {
7253                 
7254                 var total = this.ds.getTotalCount();
7255                 
7256                 if(this.footer.cursor + this.footer.pageSize < total){
7257                     
7258                     this.footer.ds.load({
7259                         params : {
7260                             start : this.footer.cursor + this.footer.pageSize,
7261                             limit : this.footer.pageSize
7262                         },
7263                         add : true
7264                     });
7265                 }
7266             }
7267             
7268         }
7269     },
7270     
7271     onHeaderChange : function()
7272     {
7273         var header = this.renderHeader();
7274         var table = this.el.select('table', true).first();
7275         
7276         this.mainHead.remove();
7277         this.mainHead = table.createChild(header, this.mainBody, false);
7278     },
7279     
7280     onHiddenChange : function(colModel, colIndex, hidden)
7281     {
7282         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7283         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7284         
7285         this.CSS.updateRule(thSelector, "display", "");
7286         this.CSS.updateRule(tdSelector, "display", "");
7287         
7288         if(hidden){
7289             this.CSS.updateRule(thSelector, "display", "none");
7290             this.CSS.updateRule(tdSelector, "display", "none");
7291         }
7292         
7293         this.onHeaderChange();
7294         this.onLoad();
7295     },
7296     
7297     setColumnWidth: function(col_index, width)
7298     {
7299         // width = "md-2 xs-2..."
7300         if(!this.colModel.config[col_index]) {
7301             return;
7302         }
7303         
7304         var w = width.split(" ");
7305         
7306         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7307         
7308         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7309         
7310         
7311         for(var j = 0; j < w.length; j++) {
7312             
7313             if(!w[j]) {
7314                 continue;
7315             }
7316             
7317             var size_cls = w[j].split("-");
7318             
7319             if(!Number.isInteger(size_cls[1] * 1)) {
7320                 continue;
7321             }
7322             
7323             if(!this.colModel.config[col_index][size_cls[0]]) {
7324                 continue;
7325             }
7326             
7327             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7328                 continue;
7329             }
7330             
7331             h_row[0].classList.replace(
7332                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7333                 "col-"+size_cls[0]+"-"+size_cls[1]
7334             );
7335             
7336             for(var i = 0; i < rows.length; i++) {
7337                 
7338                 var size_cls = w[j].split("-");
7339                 
7340                 if(!Number.isInteger(size_cls[1] * 1)) {
7341                     continue;
7342                 }
7343                 
7344                 if(!this.colModel.config[col_index][size_cls[0]]) {
7345                     continue;
7346                 }
7347                 
7348                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7349                     continue;
7350                 }
7351                 
7352                 rows[i].classList.replace(
7353                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7354                     "col-"+size_cls[0]+"-"+size_cls[1]
7355                 );
7356             }
7357             
7358             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7359         }
7360     }
7361 });
7362
7363  
7364
7365  /*
7366  * - LGPL
7367  *
7368  * table cell
7369  * 
7370  */
7371
7372 /**
7373  * @class Roo.bootstrap.TableCell
7374  * @extends Roo.bootstrap.Component
7375  * Bootstrap TableCell class
7376  * @cfg {String} html cell contain text
7377  * @cfg {String} cls cell class
7378  * @cfg {String} tag cell tag (td|th) default td
7379  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7380  * @cfg {String} align Aligns the content in a cell
7381  * @cfg {String} axis Categorizes cells
7382  * @cfg {String} bgcolor Specifies the background color of a cell
7383  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7384  * @cfg {Number} colspan Specifies the number of columns a cell should span
7385  * @cfg {String} headers Specifies one or more header cells a cell is related to
7386  * @cfg {Number} height Sets the height of a cell
7387  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7388  * @cfg {Number} rowspan Sets the number of rows a cell should span
7389  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7390  * @cfg {String} valign Vertical aligns the content in a cell
7391  * @cfg {Number} width Specifies the width of a cell
7392  * 
7393  * @constructor
7394  * Create a new TableCell
7395  * @param {Object} config The config object
7396  */
7397
7398 Roo.bootstrap.TableCell = function(config){
7399     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7400 };
7401
7402 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7403     
7404     html: false,
7405     cls: false,
7406     tag: false,
7407     abbr: false,
7408     align: false,
7409     axis: false,
7410     bgcolor: false,
7411     charoff: false,
7412     colspan: false,
7413     headers: false,
7414     height: false,
7415     nowrap: false,
7416     rowspan: false,
7417     scope: false,
7418     valign: false,
7419     width: false,
7420     
7421     
7422     getAutoCreate : function(){
7423         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7424         
7425         cfg = {
7426             tag: 'td'
7427         };
7428         
7429         if(this.tag){
7430             cfg.tag = this.tag;
7431         }
7432         
7433         if (this.html) {
7434             cfg.html=this.html
7435         }
7436         if (this.cls) {
7437             cfg.cls=this.cls
7438         }
7439         if (this.abbr) {
7440             cfg.abbr=this.abbr
7441         }
7442         if (this.align) {
7443             cfg.align=this.align
7444         }
7445         if (this.axis) {
7446             cfg.axis=this.axis
7447         }
7448         if (this.bgcolor) {
7449             cfg.bgcolor=this.bgcolor
7450         }
7451         if (this.charoff) {
7452             cfg.charoff=this.charoff
7453         }
7454         if (this.colspan) {
7455             cfg.colspan=this.colspan
7456         }
7457         if (this.headers) {
7458             cfg.headers=this.headers
7459         }
7460         if (this.height) {
7461             cfg.height=this.height
7462         }
7463         if (this.nowrap) {
7464             cfg.nowrap=this.nowrap
7465         }
7466         if (this.rowspan) {
7467             cfg.rowspan=this.rowspan
7468         }
7469         if (this.scope) {
7470             cfg.scope=this.scope
7471         }
7472         if (this.valign) {
7473             cfg.valign=this.valign
7474         }
7475         if (this.width) {
7476             cfg.width=this.width
7477         }
7478         
7479         
7480         return cfg;
7481     }
7482    
7483 });
7484
7485  
7486
7487  /*
7488  * - LGPL
7489  *
7490  * table row
7491  * 
7492  */
7493
7494 /**
7495  * @class Roo.bootstrap.TableRow
7496  * @extends Roo.bootstrap.Component
7497  * Bootstrap TableRow class
7498  * @cfg {String} cls row class
7499  * @cfg {String} align Aligns the content in a table row
7500  * @cfg {String} bgcolor Specifies a background color for a table row
7501  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7502  * @cfg {String} valign Vertical aligns the content in a table row
7503  * 
7504  * @constructor
7505  * Create a new TableRow
7506  * @param {Object} config The config object
7507  */
7508
7509 Roo.bootstrap.TableRow = function(config){
7510     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7511 };
7512
7513 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7514     
7515     cls: false,
7516     align: false,
7517     bgcolor: false,
7518     charoff: false,
7519     valign: false,
7520     
7521     getAutoCreate : function(){
7522         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7523         
7524         cfg = {
7525             tag: 'tr'
7526         };
7527             
7528         if(this.cls){
7529             cfg.cls = this.cls;
7530         }
7531         if(this.align){
7532             cfg.align = this.align;
7533         }
7534         if(this.bgcolor){
7535             cfg.bgcolor = this.bgcolor;
7536         }
7537         if(this.charoff){
7538             cfg.charoff = this.charoff;
7539         }
7540         if(this.valign){
7541             cfg.valign = this.valign;
7542         }
7543         
7544         return cfg;
7545     }
7546    
7547 });
7548
7549  
7550
7551  /*
7552  * - LGPL
7553  *
7554  * table body
7555  * 
7556  */
7557
7558 /**
7559  * @class Roo.bootstrap.TableBody
7560  * @extends Roo.bootstrap.Component
7561  * Bootstrap TableBody class
7562  * @cfg {String} cls element class
7563  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7564  * @cfg {String} align Aligns the content inside the element
7565  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7566  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7567  * 
7568  * @constructor
7569  * Create a new TableBody
7570  * @param {Object} config The config object
7571  */
7572
7573 Roo.bootstrap.TableBody = function(config){
7574     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7575 };
7576
7577 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7578     
7579     cls: false,
7580     tag: false,
7581     align: false,
7582     charoff: false,
7583     valign: false,
7584     
7585     getAutoCreate : function(){
7586         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7587         
7588         cfg = {
7589             tag: 'tbody'
7590         };
7591             
7592         if (this.cls) {
7593             cfg.cls=this.cls
7594         }
7595         if(this.tag){
7596             cfg.tag = this.tag;
7597         }
7598         
7599         if(this.align){
7600             cfg.align = this.align;
7601         }
7602         if(this.charoff){
7603             cfg.charoff = this.charoff;
7604         }
7605         if(this.valign){
7606             cfg.valign = this.valign;
7607         }
7608         
7609         return cfg;
7610     }
7611     
7612     
7613 //    initEvents : function()
7614 //    {
7615 //        
7616 //        if(!this.store){
7617 //            return;
7618 //        }
7619 //        
7620 //        this.store = Roo.factory(this.store, Roo.data);
7621 //        this.store.on('load', this.onLoad, this);
7622 //        
7623 //        this.store.load();
7624 //        
7625 //    },
7626 //    
7627 //    onLoad: function () 
7628 //    {   
7629 //        this.fireEvent('load', this);
7630 //    }
7631 //    
7632 //   
7633 });
7634
7635  
7636
7637  /*
7638  * Based on:
7639  * Ext JS Library 1.1.1
7640  * Copyright(c) 2006-2007, Ext JS, LLC.
7641  *
7642  * Originally Released Under LGPL - original licence link has changed is not relivant.
7643  *
7644  * Fork - LGPL
7645  * <script type="text/javascript">
7646  */
7647
7648 // as we use this in bootstrap.
7649 Roo.namespace('Roo.form');
7650  /**
7651  * @class Roo.form.Action
7652  * Internal Class used to handle form actions
7653  * @constructor
7654  * @param {Roo.form.BasicForm} el The form element or its id
7655  * @param {Object} config Configuration options
7656  */
7657
7658  
7659  
7660 // define the action interface
7661 Roo.form.Action = function(form, options){
7662     this.form = form;
7663     this.options = options || {};
7664 };
7665 /**
7666  * Client Validation Failed
7667  * @const 
7668  */
7669 Roo.form.Action.CLIENT_INVALID = 'client';
7670 /**
7671  * Server Validation Failed
7672  * @const 
7673  */
7674 Roo.form.Action.SERVER_INVALID = 'server';
7675  /**
7676  * Connect to Server Failed
7677  * @const 
7678  */
7679 Roo.form.Action.CONNECT_FAILURE = 'connect';
7680 /**
7681  * Reading Data from Server Failed
7682  * @const 
7683  */
7684 Roo.form.Action.LOAD_FAILURE = 'load';
7685
7686 Roo.form.Action.prototype = {
7687     type : 'default',
7688     failureType : undefined,
7689     response : undefined,
7690     result : undefined,
7691
7692     // interface method
7693     run : function(options){
7694
7695     },
7696
7697     // interface method
7698     success : function(response){
7699
7700     },
7701
7702     // interface method
7703     handleResponse : function(response){
7704
7705     },
7706
7707     // default connection failure
7708     failure : function(response){
7709         
7710         this.response = response;
7711         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7712         this.form.afterAction(this, false);
7713     },
7714
7715     processResponse : function(response){
7716         this.response = response;
7717         if(!response.responseText){
7718             return true;
7719         }
7720         this.result = this.handleResponse(response);
7721         return this.result;
7722     },
7723
7724     // utility functions used internally
7725     getUrl : function(appendParams){
7726         var url = this.options.url || this.form.url || this.form.el.dom.action;
7727         if(appendParams){
7728             var p = this.getParams();
7729             if(p){
7730                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7731             }
7732         }
7733         return url;
7734     },
7735
7736     getMethod : function(){
7737         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7738     },
7739
7740     getParams : function(){
7741         var bp = this.form.baseParams;
7742         var p = this.options.params;
7743         if(p){
7744             if(typeof p == "object"){
7745                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7746             }else if(typeof p == 'string' && bp){
7747                 p += '&' + Roo.urlEncode(bp);
7748             }
7749         }else if(bp){
7750             p = Roo.urlEncode(bp);
7751         }
7752         return p;
7753     },
7754
7755     createCallback : function(){
7756         return {
7757             success: this.success,
7758             failure: this.failure,
7759             scope: this,
7760             timeout: (this.form.timeout*1000),
7761             upload: this.form.fileUpload ? this.success : undefined
7762         };
7763     }
7764 };
7765
7766 Roo.form.Action.Submit = function(form, options){
7767     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7768 };
7769
7770 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7771     type : 'submit',
7772
7773     haveProgress : false,
7774     uploadComplete : false,
7775     
7776     // uploadProgress indicator.
7777     uploadProgress : function()
7778     {
7779         if (!this.form.progressUrl) {
7780             return;
7781         }
7782         
7783         if (!this.haveProgress) {
7784             Roo.MessageBox.progress("Uploading", "Uploading");
7785         }
7786         if (this.uploadComplete) {
7787            Roo.MessageBox.hide();
7788            return;
7789         }
7790         
7791         this.haveProgress = true;
7792    
7793         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7794         
7795         var c = new Roo.data.Connection();
7796         c.request({
7797             url : this.form.progressUrl,
7798             params: {
7799                 id : uid
7800             },
7801             method: 'GET',
7802             success : function(req){
7803                //console.log(data);
7804                 var rdata = false;
7805                 var edata;
7806                 try  {
7807                    rdata = Roo.decode(req.responseText)
7808                 } catch (e) {
7809                     Roo.log("Invalid data from server..");
7810                     Roo.log(edata);
7811                     return;
7812                 }
7813                 if (!rdata || !rdata.success) {
7814                     Roo.log(rdata);
7815                     Roo.MessageBox.alert(Roo.encode(rdata));
7816                     return;
7817                 }
7818                 var data = rdata.data;
7819                 
7820                 if (this.uploadComplete) {
7821                    Roo.MessageBox.hide();
7822                    return;
7823                 }
7824                    
7825                 if (data){
7826                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7827                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7828                     );
7829                 }
7830                 this.uploadProgress.defer(2000,this);
7831             },
7832        
7833             failure: function(data) {
7834                 Roo.log('progress url failed ');
7835                 Roo.log(data);
7836             },
7837             scope : this
7838         });
7839            
7840     },
7841     
7842     
7843     run : function()
7844     {
7845         // run get Values on the form, so it syncs any secondary forms.
7846         this.form.getValues();
7847         
7848         var o = this.options;
7849         var method = this.getMethod();
7850         var isPost = method == 'POST';
7851         if(o.clientValidation === false || this.form.isValid()){
7852             
7853             if (this.form.progressUrl) {
7854                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7855                     (new Date() * 1) + '' + Math.random());
7856                     
7857             } 
7858             
7859             
7860             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7861                 form:this.form.el.dom,
7862                 url:this.getUrl(!isPost),
7863                 method: method,
7864                 params:isPost ? this.getParams() : null,
7865                 isUpload: this.form.fileUpload
7866             }));
7867             
7868             this.uploadProgress();
7869
7870         }else if (o.clientValidation !== false){ // client validation failed
7871             this.failureType = Roo.form.Action.CLIENT_INVALID;
7872             this.form.afterAction(this, false);
7873         }
7874     },
7875
7876     success : function(response)
7877     {
7878         this.uploadComplete= true;
7879         if (this.haveProgress) {
7880             Roo.MessageBox.hide();
7881         }
7882         
7883         
7884         var result = this.processResponse(response);
7885         if(result === true || result.success){
7886             this.form.afterAction(this, true);
7887             return;
7888         }
7889         if(result.errors){
7890             this.form.markInvalid(result.errors);
7891             this.failureType = Roo.form.Action.SERVER_INVALID;
7892         }
7893         this.form.afterAction(this, false);
7894     },
7895     failure : function(response)
7896     {
7897         this.uploadComplete= true;
7898         if (this.haveProgress) {
7899             Roo.MessageBox.hide();
7900         }
7901         
7902         this.response = response;
7903         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7904         this.form.afterAction(this, false);
7905     },
7906     
7907     handleResponse : function(response){
7908         if(this.form.errorReader){
7909             var rs = this.form.errorReader.read(response);
7910             var errors = [];
7911             if(rs.records){
7912                 for(var i = 0, len = rs.records.length; i < len; i++) {
7913                     var r = rs.records[i];
7914                     errors[i] = r.data;
7915                 }
7916             }
7917             if(errors.length < 1){
7918                 errors = null;
7919             }
7920             return {
7921                 success : rs.success,
7922                 errors : errors
7923             };
7924         }
7925         var ret = false;
7926         try {
7927             ret = Roo.decode(response.responseText);
7928         } catch (e) {
7929             ret = {
7930                 success: false,
7931                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7932                 errors : []
7933             };
7934         }
7935         return ret;
7936         
7937     }
7938 });
7939
7940
7941 Roo.form.Action.Load = function(form, options){
7942     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7943     this.reader = this.form.reader;
7944 };
7945
7946 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7947     type : 'load',
7948
7949     run : function(){
7950         
7951         Roo.Ajax.request(Roo.apply(
7952                 this.createCallback(), {
7953                     method:this.getMethod(),
7954                     url:this.getUrl(false),
7955                     params:this.getParams()
7956         }));
7957     },
7958
7959     success : function(response){
7960         
7961         var result = this.processResponse(response);
7962         if(result === true || !result.success || !result.data){
7963             this.failureType = Roo.form.Action.LOAD_FAILURE;
7964             this.form.afterAction(this, false);
7965             return;
7966         }
7967         this.form.clearInvalid();
7968         this.form.setValues(result.data);
7969         this.form.afterAction(this, true);
7970     },
7971
7972     handleResponse : function(response){
7973         if(this.form.reader){
7974             var rs = this.form.reader.read(response);
7975             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7976             return {
7977                 success : rs.success,
7978                 data : data
7979             };
7980         }
7981         return Roo.decode(response.responseText);
7982     }
7983 });
7984
7985 Roo.form.Action.ACTION_TYPES = {
7986     'load' : Roo.form.Action.Load,
7987     'submit' : Roo.form.Action.Submit
7988 };/*
7989  * - LGPL
7990  *
7991  * form
7992  *
7993  */
7994
7995 /**
7996  * @class Roo.bootstrap.Form
7997  * @extends Roo.bootstrap.Component
7998  * Bootstrap Form class
7999  * @cfg {String} method  GET | POST (default POST)
8000  * @cfg {String} labelAlign top | left (default top)
8001  * @cfg {String} align left  | right - for navbars
8002  * @cfg {Boolean} loadMask load mask when submit (default true)
8003
8004  *
8005  * @constructor
8006  * Create a new Form
8007  * @param {Object} config The config object
8008  */
8009
8010
8011 Roo.bootstrap.Form = function(config){
8012     
8013     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8014     
8015     Roo.bootstrap.Form.popover.apply();
8016     
8017     this.addEvents({
8018         /**
8019          * @event clientvalidation
8020          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8021          * @param {Form} this
8022          * @param {Boolean} valid true if the form has passed client-side validation
8023          */
8024         clientvalidation: true,
8025         /**
8026          * @event beforeaction
8027          * Fires before any action is performed. Return false to cancel the action.
8028          * @param {Form} this
8029          * @param {Action} action The action to be performed
8030          */
8031         beforeaction: true,
8032         /**
8033          * @event actionfailed
8034          * Fires when an action fails.
8035          * @param {Form} this
8036          * @param {Action} action The action that failed
8037          */
8038         actionfailed : true,
8039         /**
8040          * @event actioncomplete
8041          * Fires when an action is completed.
8042          * @param {Form} this
8043          * @param {Action} action The action that completed
8044          */
8045         actioncomplete : true
8046     });
8047 };
8048
8049 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8050
8051      /**
8052      * @cfg {String} method
8053      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8054      */
8055     method : 'POST',
8056     /**
8057      * @cfg {String} url
8058      * The URL to use for form actions if one isn't supplied in the action options.
8059      */
8060     /**
8061      * @cfg {Boolean} fileUpload
8062      * Set to true if this form is a file upload.
8063      */
8064
8065     /**
8066      * @cfg {Object} baseParams
8067      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8068      */
8069
8070     /**
8071      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8072      */
8073     timeout: 30,
8074     /**
8075      * @cfg {Sting} align (left|right) for navbar forms
8076      */
8077     align : 'left',
8078
8079     // private
8080     activeAction : null,
8081
8082     /**
8083      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8084      * element by passing it or its id or mask the form itself by passing in true.
8085      * @type Mixed
8086      */
8087     waitMsgTarget : false,
8088
8089     loadMask : true,
8090     
8091     /**
8092      * @cfg {Boolean} errorMask (true|false) default false
8093      */
8094     errorMask : false,
8095     
8096     /**
8097      * @cfg {Number} maskOffset Default 100
8098      */
8099     maskOffset : 100,
8100     
8101     /**
8102      * @cfg {Boolean} maskBody
8103      */
8104     maskBody : false,
8105
8106     getAutoCreate : function(){
8107
8108         var cfg = {
8109             tag: 'form',
8110             method : this.method || 'POST',
8111             id : this.id || Roo.id(),
8112             cls : ''
8113         };
8114         if (this.parent().xtype.match(/^Nav/)) {
8115             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8116
8117         }
8118
8119         if (this.labelAlign == 'left' ) {
8120             cfg.cls += ' form-horizontal';
8121         }
8122
8123
8124         return cfg;
8125     },
8126     initEvents : function()
8127     {
8128         this.el.on('submit', this.onSubmit, this);
8129         // this was added as random key presses on the form where triggering form submit.
8130         this.el.on('keypress', function(e) {
8131             if (e.getCharCode() != 13) {
8132                 return true;
8133             }
8134             // we might need to allow it for textareas.. and some other items.
8135             // check e.getTarget().
8136
8137             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8138                 return true;
8139             }
8140
8141             Roo.log("keypress blocked");
8142
8143             e.preventDefault();
8144             return false;
8145         });
8146         
8147     },
8148     // private
8149     onSubmit : function(e){
8150         e.stopEvent();
8151     },
8152
8153      /**
8154      * Returns true if client-side validation on the form is successful.
8155      * @return Boolean
8156      */
8157     isValid : function(){
8158         var items = this.getItems();
8159         var valid = true;
8160         var target = false;
8161         
8162         items.each(function(f){
8163             
8164             if(f.validate()){
8165                 return;
8166             }
8167             
8168             Roo.log('invalid field: ' + f.name);
8169             
8170             valid = false;
8171
8172             if(!target && f.el.isVisible(true)){
8173                 target = f;
8174             }
8175            
8176         });
8177         
8178         if(this.errorMask && !valid){
8179             Roo.bootstrap.Form.popover.mask(this, target);
8180         }
8181         
8182         return valid;
8183     },
8184     
8185     /**
8186      * Returns true if any fields in this form have changed since their original load.
8187      * @return Boolean
8188      */
8189     isDirty : function(){
8190         var dirty = false;
8191         var items = this.getItems();
8192         items.each(function(f){
8193            if(f.isDirty()){
8194                dirty = true;
8195                return false;
8196            }
8197            return true;
8198         });
8199         return dirty;
8200     },
8201      /**
8202      * Performs a predefined action (submit or load) or custom actions you define on this form.
8203      * @param {String} actionName The name of the action type
8204      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8205      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8206      * accept other config options):
8207      * <pre>
8208 Property          Type             Description
8209 ----------------  ---------------  ----------------------------------------------------------------------------------
8210 url               String           The url for the action (defaults to the form's url)
8211 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8212 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8213 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8214                                    validate the form on the client (defaults to false)
8215      * </pre>
8216      * @return {BasicForm} this
8217      */
8218     doAction : function(action, options){
8219         if(typeof action == 'string'){
8220             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8221         }
8222         if(this.fireEvent('beforeaction', this, action) !== false){
8223             this.beforeAction(action);
8224             action.run.defer(100, action);
8225         }
8226         return this;
8227     },
8228
8229     // private
8230     beforeAction : function(action){
8231         var o = action.options;
8232         
8233         if(this.loadMask){
8234             
8235             if(this.maskBody){
8236                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8237             } else {
8238                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8239             }
8240         }
8241         // not really supported yet.. ??
8242
8243         //if(this.waitMsgTarget === true){
8244         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8245         //}else if(this.waitMsgTarget){
8246         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8247         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8248         //}else {
8249         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8250        // }
8251
8252     },
8253
8254     // private
8255     afterAction : function(action, success){
8256         this.activeAction = null;
8257         var o = action.options;
8258
8259         if(this.loadMask){
8260             
8261             if(this.maskBody){
8262                 Roo.get(document.body).unmask();
8263             } else {
8264                 this.el.unmask();
8265             }
8266         }
8267         
8268         //if(this.waitMsgTarget === true){
8269 //            this.el.unmask();
8270         //}else if(this.waitMsgTarget){
8271         //    this.waitMsgTarget.unmask();
8272         //}else{
8273         //    Roo.MessageBox.updateProgress(1);
8274         //    Roo.MessageBox.hide();
8275        // }
8276         //
8277         if(success){
8278             if(o.reset){
8279                 this.reset();
8280             }
8281             Roo.callback(o.success, o.scope, [this, action]);
8282             this.fireEvent('actioncomplete', this, action);
8283
8284         }else{
8285
8286             // failure condition..
8287             // we have a scenario where updates need confirming.
8288             // eg. if a locking scenario exists..
8289             // we look for { errors : { needs_confirm : true }} in the response.
8290             if (
8291                 (typeof(action.result) != 'undefined')  &&
8292                 (typeof(action.result.errors) != 'undefined')  &&
8293                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8294            ){
8295                 var _t = this;
8296                 Roo.log("not supported yet");
8297                  /*
8298
8299                 Roo.MessageBox.confirm(
8300                     "Change requires confirmation",
8301                     action.result.errorMsg,
8302                     function(r) {
8303                         if (r != 'yes') {
8304                             return;
8305                         }
8306                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8307                     }
8308
8309                 );
8310                 */
8311
8312
8313                 return;
8314             }
8315
8316             Roo.callback(o.failure, o.scope, [this, action]);
8317             // show an error message if no failed handler is set..
8318             if (!this.hasListener('actionfailed')) {
8319                 Roo.log("need to add dialog support");
8320                 /*
8321                 Roo.MessageBox.alert("Error",
8322                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8323                         action.result.errorMsg :
8324                         "Saving Failed, please check your entries or try again"
8325                 );
8326                 */
8327             }
8328
8329             this.fireEvent('actionfailed', this, action);
8330         }
8331
8332     },
8333     /**
8334      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8335      * @param {String} id The value to search for
8336      * @return Field
8337      */
8338     findField : function(id){
8339         var items = this.getItems();
8340         var field = items.get(id);
8341         if(!field){
8342              items.each(function(f){
8343                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8344                     field = f;
8345                     return false;
8346                 }
8347                 return true;
8348             });
8349         }
8350         return field || null;
8351     },
8352      /**
8353      * Mark fields in this form invalid in bulk.
8354      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8355      * @return {BasicForm} this
8356      */
8357     markInvalid : function(errors){
8358         if(errors instanceof Array){
8359             for(var i = 0, len = errors.length; i < len; i++){
8360                 var fieldError = errors[i];
8361                 var f = this.findField(fieldError.id);
8362                 if(f){
8363                     f.markInvalid(fieldError.msg);
8364                 }
8365             }
8366         }else{
8367             var field, id;
8368             for(id in errors){
8369                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8370                     field.markInvalid(errors[id]);
8371                 }
8372             }
8373         }
8374         //Roo.each(this.childForms || [], function (f) {
8375         //    f.markInvalid(errors);
8376         //});
8377
8378         return this;
8379     },
8380
8381     /**
8382      * Set values for fields in this form in bulk.
8383      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8384      * @return {BasicForm} this
8385      */
8386     setValues : function(values){
8387         if(values instanceof Array){ // array of objects
8388             for(var i = 0, len = values.length; i < len; i++){
8389                 var v = values[i];
8390                 var f = this.findField(v.id);
8391                 if(f){
8392                     f.setValue(v.value);
8393                     if(this.trackResetOnLoad){
8394                         f.originalValue = f.getValue();
8395                     }
8396                 }
8397             }
8398         }else{ // object hash
8399             var field, id;
8400             for(id in values){
8401                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8402
8403                     if (field.setFromData &&
8404                         field.valueField &&
8405                         field.displayField &&
8406                         // combos' with local stores can
8407                         // be queried via setValue()
8408                         // to set their value..
8409                         (field.store && !field.store.isLocal)
8410                         ) {
8411                         // it's a combo
8412                         var sd = { };
8413                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8414                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8415                         field.setFromData(sd);
8416
8417                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8418                         
8419                         field.setFromData(values);
8420                         
8421                     } else {
8422                         field.setValue(values[id]);
8423                     }
8424
8425
8426                     if(this.trackResetOnLoad){
8427                         field.originalValue = field.getValue();
8428                     }
8429                 }
8430             }
8431         }
8432
8433         //Roo.each(this.childForms || [], function (f) {
8434         //    f.setValues(values);
8435         //});
8436
8437         return this;
8438     },
8439
8440     /**
8441      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8442      * they are returned as an array.
8443      * @param {Boolean} asString
8444      * @return {Object}
8445      */
8446     getValues : function(asString){
8447         //if (this.childForms) {
8448             // copy values from the child forms
8449         //    Roo.each(this.childForms, function (f) {
8450         //        this.setValues(f.getValues());
8451         //    }, this);
8452         //}
8453
8454
8455
8456         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8457         if(asString === true){
8458             return fs;
8459         }
8460         return Roo.urlDecode(fs);
8461     },
8462
8463     /**
8464      * Returns the fields in this form as an object with key/value pairs.
8465      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8466      * @return {Object}
8467      */
8468     getFieldValues : function(with_hidden)
8469     {
8470         var items = this.getItems();
8471         var ret = {};
8472         items.each(function(f){
8473             
8474             if (!f.getName()) {
8475                 return;
8476             }
8477             
8478             var v = f.getValue();
8479             
8480             if (f.inputType =='radio') {
8481                 if (typeof(ret[f.getName()]) == 'undefined') {
8482                     ret[f.getName()] = ''; // empty..
8483                 }
8484
8485                 if (!f.el.dom.checked) {
8486                     return;
8487
8488                 }
8489                 v = f.el.dom.value;
8490
8491             }
8492             
8493             if(f.xtype == 'MoneyField'){
8494                 ret[f.currencyName] = f.getCurrency();
8495             }
8496
8497             // not sure if this supported any more..
8498             if ((typeof(v) == 'object') && f.getRawValue) {
8499                 v = f.getRawValue() ; // dates..
8500             }
8501             // combo boxes where name != hiddenName...
8502             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8503                 ret[f.name] = f.getRawValue();
8504             }
8505             ret[f.getName()] = v;
8506         });
8507
8508         return ret;
8509     },
8510
8511     /**
8512      * Clears all invalid messages in this form.
8513      * @return {BasicForm} this
8514      */
8515     clearInvalid : function(){
8516         var items = this.getItems();
8517
8518         items.each(function(f){
8519            f.clearInvalid();
8520         });
8521
8522         return this;
8523     },
8524
8525     /**
8526      * Resets this form.
8527      * @return {BasicForm} this
8528      */
8529     reset : function(){
8530         var items = this.getItems();
8531         items.each(function(f){
8532             f.reset();
8533         });
8534
8535         Roo.each(this.childForms || [], function (f) {
8536             f.reset();
8537         });
8538
8539
8540         return this;
8541     },
8542     
8543     getItems : function()
8544     {
8545         var r=new Roo.util.MixedCollection(false, function(o){
8546             return o.id || (o.id = Roo.id());
8547         });
8548         var iter = function(el) {
8549             if (el.inputEl) {
8550                 r.add(el);
8551             }
8552             if (!el.items) {
8553                 return;
8554             }
8555             Roo.each(el.items,function(e) {
8556                 iter(e);
8557             });
8558         };
8559
8560         iter(this);
8561         return r;
8562     },
8563     
8564     hideFields : function(items)
8565     {
8566         Roo.each(items, function(i){
8567             
8568             var f = this.findField(i);
8569             
8570             if(!f){
8571                 return;
8572             }
8573             
8574             f.hide();
8575             
8576         }, this);
8577     },
8578     
8579     showFields : function(items)
8580     {
8581         Roo.each(items, function(i){
8582             
8583             var f = this.findField(i);
8584             
8585             if(!f){
8586                 return;
8587             }
8588             
8589             f.show();
8590             
8591         }, this);
8592     }
8593
8594 });
8595
8596 Roo.apply(Roo.bootstrap.Form, {
8597     
8598     popover : {
8599         
8600         padding : 5,
8601         
8602         isApplied : false,
8603         
8604         isMasked : false,
8605         
8606         form : false,
8607         
8608         target : false,
8609         
8610         toolTip : false,
8611         
8612         intervalID : false,
8613         
8614         maskEl : false,
8615         
8616         apply : function()
8617         {
8618             if(this.isApplied){
8619                 return;
8620             }
8621             
8622             this.maskEl = {
8623                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8624                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8625                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8626                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8627             };
8628             
8629             this.maskEl.top.enableDisplayMode("block");
8630             this.maskEl.left.enableDisplayMode("block");
8631             this.maskEl.bottom.enableDisplayMode("block");
8632             this.maskEl.right.enableDisplayMode("block");
8633             
8634             this.toolTip = new Roo.bootstrap.Tooltip({
8635                 cls : 'roo-form-error-popover',
8636                 alignment : {
8637                     'left' : ['r-l', [-2,0], 'right'],
8638                     'right' : ['l-r', [2,0], 'left'],
8639                     'bottom' : ['tl-bl', [0,2], 'top'],
8640                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8641                 }
8642             });
8643             
8644             this.toolTip.render(Roo.get(document.body));
8645
8646             this.toolTip.el.enableDisplayMode("block");
8647             
8648             Roo.get(document.body).on('click', function(){
8649                 this.unmask();
8650             }, this);
8651             
8652             Roo.get(document.body).on('touchstart', function(){
8653                 this.unmask();
8654             }, this);
8655             
8656             this.isApplied = true
8657         },
8658         
8659         mask : function(form, target)
8660         {
8661             this.form = form;
8662             
8663             this.target = target;
8664             
8665             if(!this.form.errorMask || !target.el){
8666                 return;
8667             }
8668             
8669             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8670             
8671             Roo.log(scrollable);
8672             
8673             var ot = this.target.el.calcOffsetsTo(scrollable);
8674             
8675             var scrollTo = ot[1] - this.form.maskOffset;
8676             
8677             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8678             
8679             scrollable.scrollTo('top', scrollTo);
8680             
8681             var box = this.target.el.getBox();
8682             Roo.log(box);
8683             var zIndex = Roo.bootstrap.Modal.zIndex++;
8684
8685             
8686             this.maskEl.top.setStyle('position', 'absolute');
8687             this.maskEl.top.setStyle('z-index', zIndex);
8688             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8689             this.maskEl.top.setLeft(0);
8690             this.maskEl.top.setTop(0);
8691             this.maskEl.top.show();
8692             
8693             this.maskEl.left.setStyle('position', 'absolute');
8694             this.maskEl.left.setStyle('z-index', zIndex);
8695             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8696             this.maskEl.left.setLeft(0);
8697             this.maskEl.left.setTop(box.y - this.padding);
8698             this.maskEl.left.show();
8699
8700             this.maskEl.bottom.setStyle('position', 'absolute');
8701             this.maskEl.bottom.setStyle('z-index', zIndex);
8702             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8703             this.maskEl.bottom.setLeft(0);
8704             this.maskEl.bottom.setTop(box.bottom + this.padding);
8705             this.maskEl.bottom.show();
8706
8707             this.maskEl.right.setStyle('position', 'absolute');
8708             this.maskEl.right.setStyle('z-index', zIndex);
8709             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8710             this.maskEl.right.setLeft(box.right + this.padding);
8711             this.maskEl.right.setTop(box.y - this.padding);
8712             this.maskEl.right.show();
8713
8714             this.toolTip.bindEl = this.target.el;
8715
8716             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8717
8718             var tip = this.target.blankText;
8719
8720             if(this.target.getValue() !== '' ) {
8721                 
8722                 if (this.target.invalidText.length) {
8723                     tip = this.target.invalidText;
8724                 } else if (this.target.regexText.length){
8725                     tip = this.target.regexText;
8726                 }
8727             }
8728
8729             this.toolTip.show(tip);
8730
8731             this.intervalID = window.setInterval(function() {
8732                 Roo.bootstrap.Form.popover.unmask();
8733             }, 10000);
8734
8735             window.onwheel = function(){ return false;};
8736             
8737             (function(){ this.isMasked = true; }).defer(500, this);
8738             
8739         },
8740         
8741         unmask : function()
8742         {
8743             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8744                 return;
8745             }
8746             
8747             this.maskEl.top.setStyle('position', 'absolute');
8748             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8749             this.maskEl.top.hide();
8750
8751             this.maskEl.left.setStyle('position', 'absolute');
8752             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8753             this.maskEl.left.hide();
8754
8755             this.maskEl.bottom.setStyle('position', 'absolute');
8756             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8757             this.maskEl.bottom.hide();
8758
8759             this.maskEl.right.setStyle('position', 'absolute');
8760             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8761             this.maskEl.right.hide();
8762             
8763             this.toolTip.hide();
8764             
8765             this.toolTip.el.hide();
8766             
8767             window.onwheel = function(){ return true;};
8768             
8769             if(this.intervalID){
8770                 window.clearInterval(this.intervalID);
8771                 this.intervalID = false;
8772             }
8773             
8774             this.isMasked = false;
8775             
8776         }
8777         
8778     }
8779     
8780 });
8781
8782 /*
8783  * Based on:
8784  * Ext JS Library 1.1.1
8785  * Copyright(c) 2006-2007, Ext JS, LLC.
8786  *
8787  * Originally Released Under LGPL - original licence link has changed is not relivant.
8788  *
8789  * Fork - LGPL
8790  * <script type="text/javascript">
8791  */
8792 /**
8793  * @class Roo.form.VTypes
8794  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8795  * @singleton
8796  */
8797 Roo.form.VTypes = function(){
8798     // closure these in so they are only created once.
8799     var alpha = /^[a-zA-Z_]+$/;
8800     var alphanum = /^[a-zA-Z0-9_]+$/;
8801     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8802     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8803
8804     // All these messages and functions are configurable
8805     return {
8806         /**
8807          * The function used to validate email addresses
8808          * @param {String} value The email address
8809          */
8810         'email' : function(v){
8811             return email.test(v);
8812         },
8813         /**
8814          * The error text to display when the email validation function returns false
8815          * @type String
8816          */
8817         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8818         /**
8819          * The keystroke filter mask to be applied on email input
8820          * @type RegExp
8821          */
8822         'emailMask' : /[a-z0-9_\.\-@]/i,
8823
8824         /**
8825          * The function used to validate URLs
8826          * @param {String} value The URL
8827          */
8828         'url' : function(v){
8829             return url.test(v);
8830         },
8831         /**
8832          * The error text to display when the url validation function returns false
8833          * @type String
8834          */
8835         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8836         
8837         /**
8838          * The function used to validate alpha values
8839          * @param {String} value The value
8840          */
8841         'alpha' : function(v){
8842             return alpha.test(v);
8843         },
8844         /**
8845          * The error text to display when the alpha validation function returns false
8846          * @type String
8847          */
8848         'alphaText' : 'This field should only contain letters and _',
8849         /**
8850          * The keystroke filter mask to be applied on alpha input
8851          * @type RegExp
8852          */
8853         'alphaMask' : /[a-z_]/i,
8854
8855         /**
8856          * The function used to validate alphanumeric values
8857          * @param {String} value The value
8858          */
8859         'alphanum' : function(v){
8860             return alphanum.test(v);
8861         },
8862         /**
8863          * The error text to display when the alphanumeric validation function returns false
8864          * @type String
8865          */
8866         'alphanumText' : 'This field should only contain letters, numbers and _',
8867         /**
8868          * The keystroke filter mask to be applied on alphanumeric input
8869          * @type RegExp
8870          */
8871         'alphanumMask' : /[a-z0-9_]/i
8872     };
8873 }();/*
8874  * - LGPL
8875  *
8876  * Input
8877  * 
8878  */
8879
8880 /**
8881  * @class Roo.bootstrap.Input
8882  * @extends Roo.bootstrap.Component
8883  * Bootstrap Input class
8884  * @cfg {Boolean} disabled is it disabled
8885  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8886  * @cfg {String} name name of the input
8887  * @cfg {string} fieldLabel - the label associated
8888  * @cfg {string} placeholder - placeholder to put in text.
8889  * @cfg {string}  before - input group add on before
8890  * @cfg {string} after - input group add on after
8891  * @cfg {string} size - (lg|sm) or leave empty..
8892  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8893  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8894  * @cfg {Number} md colspan out of 12 for computer-sized screens
8895  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8896  * @cfg {string} value default value of the input
8897  * @cfg {Number} labelWidth set the width of label 
8898  * @cfg {Number} labellg set the width of label (1-12)
8899  * @cfg {Number} labelmd set the width of label (1-12)
8900  * @cfg {Number} labelsm set the width of label (1-12)
8901  * @cfg {Number} labelxs set the width of label (1-12)
8902  * @cfg {String} labelAlign (top|left)
8903  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8904  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8905  * @cfg {String} indicatorpos (left|right) default left
8906  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8907  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8908
8909  * @cfg {String} align (left|center|right) Default left
8910  * @cfg {Boolean} forceFeedback (true|false) Default false
8911  * 
8912  * @constructor
8913  * Create a new Input
8914  * @param {Object} config The config object
8915  */
8916
8917 Roo.bootstrap.Input = function(config){
8918     
8919     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8920     
8921     this.addEvents({
8922         /**
8923          * @event focus
8924          * Fires when this field receives input focus.
8925          * @param {Roo.form.Field} this
8926          */
8927         focus : true,
8928         /**
8929          * @event blur
8930          * Fires when this field loses input focus.
8931          * @param {Roo.form.Field} this
8932          */
8933         blur : true,
8934         /**
8935          * @event specialkey
8936          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8937          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8938          * @param {Roo.form.Field} this
8939          * @param {Roo.EventObject} e The event object
8940          */
8941         specialkey : true,
8942         /**
8943          * @event change
8944          * Fires just before the field blurs if the field value has changed.
8945          * @param {Roo.form.Field} this
8946          * @param {Mixed} newValue The new value
8947          * @param {Mixed} oldValue The original value
8948          */
8949         change : true,
8950         /**
8951          * @event invalid
8952          * Fires after the field has been marked as invalid.
8953          * @param {Roo.form.Field} this
8954          * @param {String} msg The validation message
8955          */
8956         invalid : true,
8957         /**
8958          * @event valid
8959          * Fires after the field has been validated with no errors.
8960          * @param {Roo.form.Field} this
8961          */
8962         valid : true,
8963          /**
8964          * @event keyup
8965          * Fires after the key up
8966          * @param {Roo.form.Field} this
8967          * @param {Roo.EventObject}  e The event Object
8968          */
8969         keyup : true
8970     });
8971 };
8972
8973 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8974      /**
8975      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8976       automatic validation (defaults to "keyup").
8977      */
8978     validationEvent : "keyup",
8979      /**
8980      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8981      */
8982     validateOnBlur : true,
8983     /**
8984      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8985      */
8986     validationDelay : 250,
8987      /**
8988      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8989      */
8990     focusClass : "x-form-focus",  // not needed???
8991     
8992        
8993     /**
8994      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
8995      */
8996     invalidClass : "has-warning",
8997     
8998     /**
8999      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9000      */
9001     validClass : "has-success",
9002     
9003     /**
9004      * @cfg {Boolean} hasFeedback (true|false) default true
9005      */
9006     hasFeedback : true,
9007     
9008     /**
9009      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9010      */
9011     invalidFeedbackClass : "glyphicon-warning-sign",
9012     
9013     /**
9014      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9015      */
9016     validFeedbackClass : "glyphicon-ok",
9017     
9018     /**
9019      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9020      */
9021     selectOnFocus : false,
9022     
9023      /**
9024      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9025      */
9026     maskRe : null,
9027        /**
9028      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9029      */
9030     vtype : null,
9031     
9032       /**
9033      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9034      */
9035     disableKeyFilter : false,
9036     
9037        /**
9038      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9039      */
9040     disabled : false,
9041      /**
9042      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9043      */
9044     allowBlank : true,
9045     /**
9046      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9047      */
9048     blankText : "Please complete this mandatory field",
9049     
9050      /**
9051      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9052      */
9053     minLength : 0,
9054     /**
9055      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9056      */
9057     maxLength : Number.MAX_VALUE,
9058     /**
9059      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9060      */
9061     minLengthText : "The minimum length for this field is {0}",
9062     /**
9063      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9064      */
9065     maxLengthText : "The maximum length for this field is {0}",
9066   
9067     
9068     /**
9069      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9070      * If available, this function will be called only after the basic validators all return true, and will be passed the
9071      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9072      */
9073     validator : null,
9074     /**
9075      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9076      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9077      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9078      */
9079     regex : null,
9080     /**
9081      * @cfg {String} regexText -- Depricated - use Invalid Text
9082      */
9083     regexText : "",
9084     
9085     /**
9086      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9087      */
9088     invalidText : "",
9089     
9090     
9091     
9092     autocomplete: false,
9093     
9094     
9095     fieldLabel : '',
9096     inputType : 'text',
9097     
9098     name : false,
9099     placeholder: false,
9100     before : false,
9101     after : false,
9102     size : false,
9103     hasFocus : false,
9104     preventMark: false,
9105     isFormField : true,
9106     value : '',
9107     labelWidth : 2,
9108     labelAlign : false,
9109     readOnly : false,
9110     align : false,
9111     formatedValue : false,
9112     forceFeedback : false,
9113     
9114     indicatorpos : 'left',
9115     
9116     labellg : 0,
9117     labelmd : 0,
9118     labelsm : 0,
9119     labelxs : 0,
9120     
9121     capture : '',
9122     accept : '',
9123     
9124     parentLabelAlign : function()
9125     {
9126         var parent = this;
9127         while (parent.parent()) {
9128             parent = parent.parent();
9129             if (typeof(parent.labelAlign) !='undefined') {
9130                 return parent.labelAlign;
9131             }
9132         }
9133         return 'left';
9134         
9135     },
9136     
9137     getAutoCreate : function()
9138     {
9139         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9140         
9141         var id = Roo.id();
9142         
9143         var cfg = {};
9144         
9145         if(this.inputType != 'hidden'){
9146             cfg.cls = 'form-group' //input-group
9147         }
9148         
9149         var input =  {
9150             tag: 'input',
9151             id : id,
9152             type : this.inputType,
9153             value : this.value,
9154             cls : 'form-control',
9155             placeholder : this.placeholder || '',
9156             autocomplete : this.autocomplete || 'new-password'
9157         };
9158         
9159         if(this.capture.length){
9160             input.capture = this.capture;
9161         }
9162         
9163         if(this.accept.length){
9164             input.accept = this.accept + "/*";
9165         }
9166         
9167         if(this.align){
9168             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9169         }
9170         
9171         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9172             input.maxLength = this.maxLength;
9173         }
9174         
9175         if (this.disabled) {
9176             input.disabled=true;
9177         }
9178         
9179         if (this.readOnly) {
9180             input.readonly=true;
9181         }
9182         
9183         if (this.name) {
9184             input.name = this.name;
9185         }
9186         
9187         if (this.size) {
9188             input.cls += ' input-' + this.size;
9189         }
9190         
9191         var settings=this;
9192         ['xs','sm','md','lg'].map(function(size){
9193             if (settings[size]) {
9194                 cfg.cls += ' col-' + size + '-' + settings[size];
9195             }
9196         });
9197         
9198         var inputblock = input;
9199         
9200         var feedback = {
9201             tag: 'span',
9202             cls: 'glyphicon form-control-feedback'
9203         };
9204             
9205         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9206             
9207             inputblock = {
9208                 cls : 'has-feedback',
9209                 cn :  [
9210                     input,
9211                     feedback
9212                 ] 
9213             };  
9214         }
9215         
9216         if (this.before || this.after) {
9217             
9218             inputblock = {
9219                 cls : 'input-group',
9220                 cn :  [] 
9221             };
9222             
9223             if (this.before && typeof(this.before) == 'string') {
9224                 
9225                 inputblock.cn.push({
9226                     tag :'span',
9227                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9228                     html : this.before
9229                 });
9230             }
9231             if (this.before && typeof(this.before) == 'object') {
9232                 this.before = Roo.factory(this.before);
9233                 
9234                 inputblock.cn.push({
9235                     tag :'span',
9236                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9237                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9238                 });
9239             }
9240             
9241             inputblock.cn.push(input);
9242             
9243             if (this.after && typeof(this.after) == 'string') {
9244                 inputblock.cn.push({
9245                     tag :'span',
9246                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9247                     html : this.after
9248                 });
9249             }
9250             if (this.after && typeof(this.after) == 'object') {
9251                 this.after = Roo.factory(this.after);
9252                 
9253                 inputblock.cn.push({
9254                     tag :'span',
9255                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9256                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9257                 });
9258             }
9259             
9260             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9261                 inputblock.cls += ' has-feedback';
9262                 inputblock.cn.push(feedback);
9263             }
9264         };
9265         var indicator = {
9266             tag : 'i',
9267             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9268             tooltip : 'This field is required'
9269         };
9270         if (Roo.bootstrap.version == 4) {
9271             indicator = {
9272                 tag : 'i',
9273                 style : 'display-none'
9274             };
9275         }
9276         if (align ==='left' && this.fieldLabel.length) {
9277             
9278             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9279             
9280             cfg.cn = [
9281                 indicator,
9282                 {
9283                     tag: 'label',
9284                     'for' :  id,
9285                     cls : 'control-label col-form-label',
9286                     html : this.fieldLabel
9287
9288                 },
9289                 {
9290                     cls : "", 
9291                     cn: [
9292                         inputblock
9293                     ]
9294                 }
9295             ];
9296             
9297             var labelCfg = cfg.cn[1];
9298             var contentCfg = cfg.cn[2];
9299             
9300             if(this.indicatorpos == 'right'){
9301                 cfg.cn = [
9302                     {
9303                         tag: 'label',
9304                         'for' :  id,
9305                         cls : 'control-label col-form-label',
9306                         cn : [
9307                             {
9308                                 tag : 'span',
9309                                 html : this.fieldLabel
9310                             },
9311                             indicator
9312                         ]
9313                     },
9314                     {
9315                         cls : "",
9316                         cn: [
9317                             inputblock
9318                         ]
9319                     }
9320
9321                 ];
9322                 
9323                 labelCfg = cfg.cn[0];
9324                 contentCfg = cfg.cn[1];
9325             
9326             }
9327             
9328             if(this.labelWidth > 12){
9329                 labelCfg.style = "width: " + this.labelWidth + 'px';
9330             }
9331             
9332             if(this.labelWidth < 13 && this.labelmd == 0){
9333                 this.labelmd = this.labelWidth;
9334             }
9335             
9336             if(this.labellg > 0){
9337                 labelCfg.cls += ' col-lg-' + this.labellg;
9338                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9339             }
9340             
9341             if(this.labelmd > 0){
9342                 labelCfg.cls += ' col-md-' + this.labelmd;
9343                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9344             }
9345             
9346             if(this.labelsm > 0){
9347                 labelCfg.cls += ' col-sm-' + this.labelsm;
9348                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9349             }
9350             
9351             if(this.labelxs > 0){
9352                 labelCfg.cls += ' col-xs-' + this.labelxs;
9353                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9354             }
9355             
9356             
9357         } else if ( this.fieldLabel.length) {
9358                 
9359             cfg.cn = [
9360                 {
9361                     tag : 'i',
9362                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9363                     tooltip : 'This field is required'
9364                 },
9365                 {
9366                     tag: 'label',
9367                    //cls : 'input-group-addon',
9368                     html : this.fieldLabel
9369
9370                 },
9371
9372                inputblock
9373
9374            ];
9375            
9376            if(this.indicatorpos == 'right'){
9377                 
9378                 cfg.cn = [
9379                     {
9380                         tag: 'label',
9381                        //cls : 'input-group-addon',
9382                         html : this.fieldLabel
9383
9384                     },
9385                     {
9386                         tag : 'i',
9387                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9388                         tooltip : 'This field is required'
9389                     },
9390
9391                    inputblock
9392
9393                ];
9394
9395             }
9396
9397         } else {
9398             
9399             cfg.cn = [
9400
9401                     inputblock
9402
9403             ];
9404                 
9405                 
9406         };
9407         
9408         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9409            cfg.cls += ' navbar-form';
9410         }
9411         
9412         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9413             // on BS4 we do this only if not form 
9414             cfg.cls += ' navbar-form';
9415             cfg.tag = 'li';
9416         }
9417         
9418         return cfg;
9419         
9420     },
9421     /**
9422      * return the real input element.
9423      */
9424     inputEl: function ()
9425     {
9426         return this.el.select('input.form-control',true).first();
9427     },
9428     
9429     tooltipEl : function()
9430     {
9431         return this.inputEl();
9432     },
9433     
9434     indicatorEl : function()
9435     {
9436         if (Roo.bootstrap.version == 4) {
9437             return false; // not enabled in v4 yet.
9438         }
9439         
9440         var indicator = this.el.select('i.roo-required-indicator',true).first();
9441         
9442         if(!indicator){
9443             return false;
9444         }
9445         
9446         return indicator;
9447         
9448     },
9449     
9450     setDisabled : function(v)
9451     {
9452         var i  = this.inputEl().dom;
9453         if (!v) {
9454             i.removeAttribute('disabled');
9455             return;
9456             
9457         }
9458         i.setAttribute('disabled','true');
9459     },
9460     initEvents : function()
9461     {
9462           
9463         this.inputEl().on("keydown" , this.fireKey,  this);
9464         this.inputEl().on("focus", this.onFocus,  this);
9465         this.inputEl().on("blur", this.onBlur,  this);
9466         
9467         this.inputEl().relayEvent('keyup', this);
9468         
9469         this.indicator = this.indicatorEl();
9470         
9471         if(this.indicator){
9472             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9473         }
9474  
9475         // reference to original value for reset
9476         this.originalValue = this.getValue();
9477         //Roo.form.TextField.superclass.initEvents.call(this);
9478         if(this.validationEvent == 'keyup'){
9479             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9480             this.inputEl().on('keyup', this.filterValidation, this);
9481         }
9482         else if(this.validationEvent !== false){
9483             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9484         }
9485         
9486         if(this.selectOnFocus){
9487             this.on("focus", this.preFocus, this);
9488             
9489         }
9490         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9491             this.inputEl().on("keypress", this.filterKeys, this);
9492         } else {
9493             this.inputEl().relayEvent('keypress', this);
9494         }
9495        /* if(this.grow){
9496             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9497             this.el.on("click", this.autoSize,  this);
9498         }
9499         */
9500         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9501             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9502         }
9503         
9504         if (typeof(this.before) == 'object') {
9505             this.before.render(this.el.select('.roo-input-before',true).first());
9506         }
9507         if (typeof(this.after) == 'object') {
9508             this.after.render(this.el.select('.roo-input-after',true).first());
9509         }
9510         
9511         this.inputEl().on('change', this.onChange, this);
9512         
9513     },
9514     filterValidation : function(e){
9515         if(!e.isNavKeyPress()){
9516             this.validationTask.delay(this.validationDelay);
9517         }
9518     },
9519      /**
9520      * Validates the field value
9521      * @return {Boolean} True if the value is valid, else false
9522      */
9523     validate : function(){
9524         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9525         if(this.disabled || this.validateValue(this.getRawValue())){
9526             this.markValid();
9527             return true;
9528         }
9529         
9530         this.markInvalid();
9531         return false;
9532     },
9533     
9534     
9535     /**
9536      * Validates a value according to the field's validation rules and marks the field as invalid
9537      * if the validation fails
9538      * @param {Mixed} value The value to validate
9539      * @return {Boolean} True if the value is valid, else false
9540      */
9541     validateValue : function(value)
9542     {
9543         if(this.getVisibilityEl().hasClass('hidden')){
9544             return true;
9545         }
9546         
9547         if(value.length < 1)  { // if it's blank
9548             if(this.allowBlank){
9549                 return true;
9550             }
9551             return false;
9552         }
9553         
9554         if(value.length < this.minLength){
9555             return false;
9556         }
9557         if(value.length > this.maxLength){
9558             return false;
9559         }
9560         if(this.vtype){
9561             var vt = Roo.form.VTypes;
9562             if(!vt[this.vtype](value, this)){
9563                 return false;
9564             }
9565         }
9566         if(typeof this.validator == "function"){
9567             var msg = this.validator(value);
9568             if(msg !== true){
9569                 return false;
9570             }
9571             if (typeof(msg) == 'string') {
9572                 this.invalidText = msg;
9573             }
9574         }
9575         
9576         if(this.regex && !this.regex.test(value)){
9577             return false;
9578         }
9579         
9580         return true;
9581     },
9582     
9583      // private
9584     fireKey : function(e){
9585         //Roo.log('field ' + e.getKey());
9586         if(e.isNavKeyPress()){
9587             this.fireEvent("specialkey", this, e);
9588         }
9589     },
9590     focus : function (selectText){
9591         if(this.rendered){
9592             this.inputEl().focus();
9593             if(selectText === true){
9594                 this.inputEl().dom.select();
9595             }
9596         }
9597         return this;
9598     } ,
9599     
9600     onFocus : function(){
9601         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9602            // this.el.addClass(this.focusClass);
9603         }
9604         if(!this.hasFocus){
9605             this.hasFocus = true;
9606             this.startValue = this.getValue();
9607             this.fireEvent("focus", this);
9608         }
9609     },
9610     
9611     beforeBlur : Roo.emptyFn,
9612
9613     
9614     // private
9615     onBlur : function(){
9616         this.beforeBlur();
9617         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9618             //this.el.removeClass(this.focusClass);
9619         }
9620         this.hasFocus = false;
9621         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9622             this.validate();
9623         }
9624         var v = this.getValue();
9625         if(String(v) !== String(this.startValue)){
9626             this.fireEvent('change', this, v, this.startValue);
9627         }
9628         this.fireEvent("blur", this);
9629     },
9630     
9631     onChange : function(e)
9632     {
9633         var v = this.getValue();
9634         if(String(v) !== String(this.startValue)){
9635             this.fireEvent('change', this, v, this.startValue);
9636         }
9637         
9638     },
9639     
9640     /**
9641      * Resets the current field value to the originally loaded value and clears any validation messages
9642      */
9643     reset : function(){
9644         this.setValue(this.originalValue);
9645         this.validate();
9646     },
9647      /**
9648      * Returns the name of the field
9649      * @return {Mixed} name The name field
9650      */
9651     getName: function(){
9652         return this.name;
9653     },
9654      /**
9655      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9656      * @return {Mixed} value The field value
9657      */
9658     getValue : function(){
9659         
9660         var v = this.inputEl().getValue();
9661         
9662         return v;
9663     },
9664     /**
9665      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9666      * @return {Mixed} value The field value
9667      */
9668     getRawValue : function(){
9669         var v = this.inputEl().getValue();
9670         
9671         return v;
9672     },
9673     
9674     /**
9675      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9676      * @param {Mixed} value The value to set
9677      */
9678     setRawValue : function(v){
9679         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9680     },
9681     
9682     selectText : function(start, end){
9683         var v = this.getRawValue();
9684         if(v.length > 0){
9685             start = start === undefined ? 0 : start;
9686             end = end === undefined ? v.length : end;
9687             var d = this.inputEl().dom;
9688             if(d.setSelectionRange){
9689                 d.setSelectionRange(start, end);
9690             }else if(d.createTextRange){
9691                 var range = d.createTextRange();
9692                 range.moveStart("character", start);
9693                 range.moveEnd("character", v.length-end);
9694                 range.select();
9695             }
9696         }
9697     },
9698     
9699     /**
9700      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9701      * @param {Mixed} value The value to set
9702      */
9703     setValue : function(v){
9704         this.value = v;
9705         if(this.rendered){
9706             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9707             this.validate();
9708         }
9709     },
9710     
9711     /*
9712     processValue : function(value){
9713         if(this.stripCharsRe){
9714             var newValue = value.replace(this.stripCharsRe, '');
9715             if(newValue !== value){
9716                 this.setRawValue(newValue);
9717                 return newValue;
9718             }
9719         }
9720         return value;
9721     },
9722   */
9723     preFocus : function(){
9724         
9725         if(this.selectOnFocus){
9726             this.inputEl().dom.select();
9727         }
9728     },
9729     filterKeys : function(e){
9730         var k = e.getKey();
9731         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9732             return;
9733         }
9734         var c = e.getCharCode(), cc = String.fromCharCode(c);
9735         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9736             return;
9737         }
9738         if(!this.maskRe.test(cc)){
9739             e.stopEvent();
9740         }
9741     },
9742      /**
9743      * Clear any invalid styles/messages for this field
9744      */
9745     clearInvalid : function(){
9746         
9747         if(!this.el || this.preventMark){ // not rendered
9748             return;
9749         }
9750         
9751         
9752         this.el.removeClass([this.invalidClass, 'is-invalid']);
9753         
9754         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9755             
9756             var feedback = this.el.select('.form-control-feedback', true).first();
9757             
9758             if(feedback){
9759                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9760             }
9761             
9762         }
9763         
9764         if(this.indicator){
9765             this.indicator.removeClass('visible');
9766             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9767         }
9768         
9769         this.fireEvent('valid', this);
9770     },
9771     
9772      /**
9773      * Mark this field as valid
9774      */
9775     markValid : function()
9776     {
9777         if(!this.el  || this.preventMark){ // not rendered...
9778             return;
9779         }
9780         
9781         this.el.removeClass([this.invalidClass, this.validClass]);
9782         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9783
9784         var feedback = this.el.select('.form-control-feedback', true).first();
9785             
9786         if(feedback){
9787             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9788         }
9789         
9790         if(this.indicator){
9791             this.indicator.removeClass('visible');
9792             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9793         }
9794         
9795         if(this.disabled){
9796             return;
9797         }
9798         
9799         if(this.allowBlank && !this.getRawValue().length){
9800             return;
9801         }
9802         if (Roo.bootstrap.version == 3) {
9803             this.el.addClass(this.validClass);
9804         } else {
9805             this.inputEl().addClass('is-valid');
9806         }
9807
9808         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9809             
9810             var feedback = this.el.select('.form-control-feedback', true).first();
9811             
9812             if(feedback){
9813                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9814                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9815             }
9816             
9817         }
9818         
9819         this.fireEvent('valid', this);
9820     },
9821     
9822      /**
9823      * Mark this field as invalid
9824      * @param {String} msg The validation message
9825      */
9826     markInvalid : function(msg)
9827     {
9828         if(!this.el  || this.preventMark){ // not rendered
9829             return;
9830         }
9831         
9832         this.el.removeClass([this.invalidClass, this.validClass]);
9833         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9834         
9835         var feedback = this.el.select('.form-control-feedback', true).first();
9836             
9837         if(feedback){
9838             this.el.select('.form-control-feedback', true).first().removeClass(
9839                     [this.invalidFeedbackClass, this.validFeedbackClass]);
9840         }
9841
9842         if(this.disabled){
9843             return;
9844         }
9845         
9846         if(this.allowBlank && !this.getRawValue().length){
9847             return;
9848         }
9849         
9850         if(this.indicator){
9851             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9852             this.indicator.addClass('visible');
9853         }
9854         if (Roo.bootstrap.version == 3) {
9855             this.el.addClass(this.invalidClass);
9856         } else {
9857             this.inputEl().addClass('is-invalid');
9858         }
9859         
9860         
9861         
9862         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9863             
9864             var feedback = this.el.select('.form-control-feedback', true).first();
9865             
9866             if(feedback){
9867                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9868                 
9869                 if(this.getValue().length || this.forceFeedback){
9870                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9871                 }
9872                 
9873             }
9874             
9875         }
9876         
9877         this.fireEvent('invalid', this, msg);
9878     },
9879     // private
9880     SafariOnKeyDown : function(event)
9881     {
9882         // this is a workaround for a password hang bug on chrome/ webkit.
9883         if (this.inputEl().dom.type != 'password') {
9884             return;
9885         }
9886         
9887         var isSelectAll = false;
9888         
9889         if(this.inputEl().dom.selectionEnd > 0){
9890             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9891         }
9892         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9893             event.preventDefault();
9894             this.setValue('');
9895             return;
9896         }
9897         
9898         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9899             
9900             event.preventDefault();
9901             // this is very hacky as keydown always get's upper case.
9902             //
9903             var cc = String.fromCharCode(event.getCharCode());
9904             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9905             
9906         }
9907     },
9908     adjustWidth : function(tag, w){
9909         tag = tag.toLowerCase();
9910         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9911             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9912                 if(tag == 'input'){
9913                     return w + 2;
9914                 }
9915                 if(tag == 'textarea'){
9916                     return w-2;
9917                 }
9918             }else if(Roo.isOpera){
9919                 if(tag == 'input'){
9920                     return w + 2;
9921                 }
9922                 if(tag == 'textarea'){
9923                     return w-2;
9924                 }
9925             }
9926         }
9927         return w;
9928     },
9929     
9930     setFieldLabel : function(v)
9931     {
9932         if(!this.rendered){
9933             return;
9934         }
9935         
9936         if(this.indicatorEl()){
9937             var ar = this.el.select('label > span',true);
9938             
9939             if (ar.elements.length) {
9940                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9941                 this.fieldLabel = v;
9942                 return;
9943             }
9944             
9945             var br = this.el.select('label',true);
9946             
9947             if(br.elements.length) {
9948                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9949                 this.fieldLabel = v;
9950                 return;
9951             }
9952             
9953             Roo.log('Cannot Found any of label > span || label in input');
9954             return;
9955         }
9956         
9957         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9958         this.fieldLabel = v;
9959         
9960         
9961     }
9962 });
9963
9964  
9965 /*
9966  * - LGPL
9967  *
9968  * Input
9969  * 
9970  */
9971
9972 /**
9973  * @class Roo.bootstrap.TextArea
9974  * @extends Roo.bootstrap.Input
9975  * Bootstrap TextArea class
9976  * @cfg {Number} cols Specifies the visible width of a text area
9977  * @cfg {Number} rows Specifies the visible number of lines in a text area
9978  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9979  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9980  * @cfg {string} html text
9981  * 
9982  * @constructor
9983  * Create a new TextArea
9984  * @param {Object} config The config object
9985  */
9986
9987 Roo.bootstrap.TextArea = function(config){
9988     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9989    
9990 };
9991
9992 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9993      
9994     cols : false,
9995     rows : 5,
9996     readOnly : false,
9997     warp : 'soft',
9998     resize : false,
9999     value: false,
10000     html: false,
10001     
10002     getAutoCreate : function(){
10003         
10004         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10005         
10006         var id = Roo.id();
10007         
10008         var cfg = {};
10009         
10010         if(this.inputType != 'hidden'){
10011             cfg.cls = 'form-group' //input-group
10012         }
10013         
10014         var input =  {
10015             tag: 'textarea',
10016             id : id,
10017             warp : this.warp,
10018             rows : this.rows,
10019             value : this.value || '',
10020             html: this.html || '',
10021             cls : 'form-control',
10022             placeholder : this.placeholder || '' 
10023             
10024         };
10025         
10026         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10027             input.maxLength = this.maxLength;
10028         }
10029         
10030         if(this.resize){
10031             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10032         }
10033         
10034         if(this.cols){
10035             input.cols = this.cols;
10036         }
10037         
10038         if (this.readOnly) {
10039             input.readonly = true;
10040         }
10041         
10042         if (this.name) {
10043             input.name = this.name;
10044         }
10045         
10046         if (this.size) {
10047             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10048         }
10049         
10050         var settings=this;
10051         ['xs','sm','md','lg'].map(function(size){
10052             if (settings[size]) {
10053                 cfg.cls += ' col-' + size + '-' + settings[size];
10054             }
10055         });
10056         
10057         var inputblock = input;
10058         
10059         if(this.hasFeedback && !this.allowBlank){
10060             
10061             var feedback = {
10062                 tag: 'span',
10063                 cls: 'glyphicon form-control-feedback'
10064             };
10065
10066             inputblock = {
10067                 cls : 'has-feedback',
10068                 cn :  [
10069                     input,
10070                     feedback
10071                 ] 
10072             };  
10073         }
10074         
10075         
10076         if (this.before || this.after) {
10077             
10078             inputblock = {
10079                 cls : 'input-group',
10080                 cn :  [] 
10081             };
10082             if (this.before) {
10083                 inputblock.cn.push({
10084                     tag :'span',
10085                     cls : 'input-group-addon',
10086                     html : this.before
10087                 });
10088             }
10089             
10090             inputblock.cn.push(input);
10091             
10092             if(this.hasFeedback && !this.allowBlank){
10093                 inputblock.cls += ' has-feedback';
10094                 inputblock.cn.push(feedback);
10095             }
10096             
10097             if (this.after) {
10098                 inputblock.cn.push({
10099                     tag :'span',
10100                     cls : 'input-group-addon',
10101                     html : this.after
10102                 });
10103             }
10104             
10105         }
10106         
10107         if (align ==='left' && this.fieldLabel.length) {
10108             cfg.cn = [
10109                 {
10110                     tag: 'label',
10111                     'for' :  id,
10112                     cls : 'control-label',
10113                     html : this.fieldLabel
10114                 },
10115                 {
10116                     cls : "",
10117                     cn: [
10118                         inputblock
10119                     ]
10120                 }
10121
10122             ];
10123             
10124             if(this.labelWidth > 12){
10125                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10126             }
10127
10128             if(this.labelWidth < 13 && this.labelmd == 0){
10129                 this.labelmd = this.labelWidth;
10130             }
10131
10132             if(this.labellg > 0){
10133                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10134                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10135             }
10136
10137             if(this.labelmd > 0){
10138                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10139                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10140             }
10141
10142             if(this.labelsm > 0){
10143                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10144                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10145             }
10146
10147             if(this.labelxs > 0){
10148                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10149                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10150             }
10151             
10152         } else if ( this.fieldLabel.length) {
10153             cfg.cn = [
10154
10155                {
10156                    tag: 'label',
10157                    //cls : 'input-group-addon',
10158                    html : this.fieldLabel
10159
10160                },
10161
10162                inputblock
10163
10164            ];
10165
10166         } else {
10167
10168             cfg.cn = [
10169
10170                 inputblock
10171
10172             ];
10173                 
10174         }
10175         
10176         if (this.disabled) {
10177             input.disabled=true;
10178         }
10179         
10180         return cfg;
10181         
10182     },
10183     /**
10184      * return the real textarea element.
10185      */
10186     inputEl: function ()
10187     {
10188         return this.el.select('textarea.form-control',true).first();
10189     },
10190     
10191     /**
10192      * Clear any invalid styles/messages for this field
10193      */
10194     clearInvalid : function()
10195     {
10196         
10197         if(!this.el || this.preventMark){ // not rendered
10198             return;
10199         }
10200         
10201         var label = this.el.select('label', true).first();
10202         var icon = this.el.select('i.fa-star', true).first();
10203         
10204         if(label && icon){
10205             icon.remove();
10206         }
10207         this.el.removeClass( this.validClass);
10208         this.inputEl().removeClass('is-invalid');
10209          
10210         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10211             
10212             var feedback = this.el.select('.form-control-feedback', true).first();
10213             
10214             if(feedback){
10215                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10216             }
10217             
10218         }
10219         
10220         this.fireEvent('valid', this);
10221     },
10222     
10223      /**
10224      * Mark this field as valid
10225      */
10226     markValid : function()
10227     {
10228         if(!this.el  || this.preventMark){ // not rendered
10229             return;
10230         }
10231         
10232         this.el.removeClass([this.invalidClass, this.validClass]);
10233         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10234         
10235         var feedback = this.el.select('.form-control-feedback', true).first();
10236             
10237         if(feedback){
10238             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10239         }
10240
10241         if(this.disabled || this.allowBlank){
10242             return;
10243         }
10244         
10245         var label = this.el.select('label', true).first();
10246         var icon = this.el.select('i.fa-star', true).first();
10247         
10248         if(label && icon){
10249             icon.remove();
10250         }
10251         if (Roo.bootstrap.version == 3) {
10252             this.el.addClass(this.validClass);
10253         } else {
10254             this.inputEl().addClass('is-valid');
10255         }
10256         
10257         
10258         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10259             
10260             var feedback = this.el.select('.form-control-feedback', true).first();
10261             
10262             if(feedback){
10263                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10264                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10265             }
10266             
10267         }
10268         
10269         this.fireEvent('valid', this);
10270     },
10271     
10272      /**
10273      * Mark this field as invalid
10274      * @param {String} msg The validation message
10275      */
10276     markInvalid : function(msg)
10277     {
10278         if(!this.el  || this.preventMark){ // not rendered
10279             return;
10280         }
10281         
10282         this.el.removeClass([this.invalidClass, this.validClass]);
10283         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10284         
10285         var feedback = this.el.select('.form-control-feedback', true).first();
10286             
10287         if(feedback){
10288             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10289         }
10290
10291         if(this.disabled || this.allowBlank){
10292             return;
10293         }
10294         
10295         var label = this.el.select('label', true).first();
10296         var icon = this.el.select('i.fa-star', true).first();
10297         
10298         if(!this.getValue().length && label && !icon){
10299             this.el.createChild({
10300                 tag : 'i',
10301                 cls : 'text-danger fa fa-lg fa-star',
10302                 tooltip : 'This field is required',
10303                 style : 'margin-right:5px;'
10304             }, label, true);
10305         }
10306         
10307         if (Roo.bootstrap.version == 3) {
10308             this.el.addClass(this.invalidClass);
10309         } else {
10310             this.inputEl().addClass('is-invalid');
10311         }
10312         
10313         // fixme ... this may be depricated need to test..
10314         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10315             
10316             var feedback = this.el.select('.form-control-feedback', true).first();
10317             
10318             if(feedback){
10319                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10320                 
10321                 if(this.getValue().length || this.forceFeedback){
10322                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10323                 }
10324                 
10325             }
10326             
10327         }
10328         
10329         this.fireEvent('invalid', this, msg);
10330     }
10331 });
10332
10333  
10334 /*
10335  * - LGPL
10336  *
10337  * trigger field - base class for combo..
10338  * 
10339  */
10340  
10341 /**
10342  * @class Roo.bootstrap.TriggerField
10343  * @extends Roo.bootstrap.Input
10344  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10345  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10346  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10347  * for which you can provide a custom implementation.  For example:
10348  * <pre><code>
10349 var trigger = new Roo.bootstrap.TriggerField();
10350 trigger.onTriggerClick = myTriggerFn;
10351 trigger.applyTo('my-field');
10352 </code></pre>
10353  *
10354  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10355  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10356  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10357  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10358  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10359
10360  * @constructor
10361  * Create a new TriggerField.
10362  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10363  * to the base TextField)
10364  */
10365 Roo.bootstrap.TriggerField = function(config){
10366     this.mimicing = false;
10367     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10368 };
10369
10370 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10371     /**
10372      * @cfg {String} triggerClass A CSS class to apply to the trigger
10373      */
10374      /**
10375      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10376      */
10377     hideTrigger:false,
10378
10379     /**
10380      * @cfg {Boolean} removable (true|false) special filter default false
10381      */
10382     removable : false,
10383     
10384     /** @cfg {Boolean} grow @hide */
10385     /** @cfg {Number} growMin @hide */
10386     /** @cfg {Number} growMax @hide */
10387
10388     /**
10389      * @hide 
10390      * @method
10391      */
10392     autoSize: Roo.emptyFn,
10393     // private
10394     monitorTab : true,
10395     // private
10396     deferHeight : true,
10397
10398     
10399     actionMode : 'wrap',
10400     
10401     caret : false,
10402     
10403     
10404     getAutoCreate : function(){
10405        
10406         var align = this.labelAlign || this.parentLabelAlign();
10407         
10408         var id = Roo.id();
10409         
10410         var cfg = {
10411             cls: 'form-group' //input-group
10412         };
10413         
10414         
10415         var input =  {
10416             tag: 'input',
10417             id : id,
10418             type : this.inputType,
10419             cls : 'form-control',
10420             autocomplete: 'new-password',
10421             placeholder : this.placeholder || '' 
10422             
10423         };
10424         if (this.name) {
10425             input.name = this.name;
10426         }
10427         if (this.size) {
10428             input.cls += ' input-' + this.size;
10429         }
10430         
10431         if (this.disabled) {
10432             input.disabled=true;
10433         }
10434         
10435         var inputblock = input;
10436         
10437         if(this.hasFeedback && !this.allowBlank){
10438             
10439             var feedback = {
10440                 tag: 'span',
10441                 cls: 'glyphicon form-control-feedback'
10442             };
10443             
10444             if(this.removable && !this.editable && !this.tickable){
10445                 inputblock = {
10446                     cls : 'has-feedback',
10447                     cn :  [
10448                         inputblock,
10449                         {
10450                             tag: 'button',
10451                             html : 'x',
10452                             cls : 'roo-combo-removable-btn close'
10453                         },
10454                         feedback
10455                     ] 
10456                 };
10457             } else {
10458                 inputblock = {
10459                     cls : 'has-feedback',
10460                     cn :  [
10461                         inputblock,
10462                         feedback
10463                     ] 
10464                 };
10465             }
10466
10467         } else {
10468             if(this.removable && !this.editable && !this.tickable){
10469                 inputblock = {
10470                     cls : 'roo-removable',
10471                     cn :  [
10472                         inputblock,
10473                         {
10474                             tag: 'button',
10475                             html : 'x',
10476                             cls : 'roo-combo-removable-btn close'
10477                         }
10478                     ] 
10479                 };
10480             }
10481         }
10482         
10483         if (this.before || this.after) {
10484             
10485             inputblock = {
10486                 cls : 'input-group',
10487                 cn :  [] 
10488             };
10489             if (this.before) {
10490                 inputblock.cn.push({
10491                     tag :'span',
10492                     cls : 'input-group-addon input-group-prepend input-group-text',
10493                     html : this.before
10494                 });
10495             }
10496             
10497             inputblock.cn.push(input);
10498             
10499             if(this.hasFeedback && !this.allowBlank){
10500                 inputblock.cls += ' has-feedback';
10501                 inputblock.cn.push(feedback);
10502             }
10503             
10504             if (this.after) {
10505                 inputblock.cn.push({
10506                     tag :'span',
10507                     cls : 'input-group-addon input-group-append input-group-text',
10508                     html : this.after
10509                 });
10510             }
10511             
10512         };
10513         
10514       
10515         
10516         var ibwrap = inputblock;
10517         
10518         if(this.multiple){
10519             ibwrap = {
10520                 tag: 'ul',
10521                 cls: 'roo-select2-choices',
10522                 cn:[
10523                     {
10524                         tag: 'li',
10525                         cls: 'roo-select2-search-field',
10526                         cn: [
10527
10528                             inputblock
10529                         ]
10530                     }
10531                 ]
10532             };
10533                 
10534         }
10535         
10536         var combobox = {
10537             cls: 'roo-select2-container input-group',
10538             cn: [
10539                  {
10540                     tag: 'input',
10541                     type : 'hidden',
10542                     cls: 'form-hidden-field'
10543                 },
10544                 ibwrap
10545             ]
10546         };
10547         
10548         if(!this.multiple && this.showToggleBtn){
10549             
10550             var caret = {
10551                         tag: 'span',
10552                         cls: 'caret'
10553              };
10554             if (this.caret != false) {
10555                 caret = {
10556                      tag: 'i',
10557                      cls: 'fa fa-' + this.caret
10558                 };
10559                 
10560             }
10561             
10562             combobox.cn.push({
10563                 tag :'span',
10564                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10565                 cn : [
10566                     caret,
10567                     {
10568                         tag: 'span',
10569                         cls: 'combobox-clear',
10570                         cn  : [
10571                             {
10572                                 tag : 'i',
10573                                 cls: 'icon-remove'
10574                             }
10575                         ]
10576                     }
10577                 ]
10578
10579             })
10580         }
10581         
10582         if(this.multiple){
10583             combobox.cls += ' roo-select2-container-multi';
10584         }
10585          var indicator = {
10586             tag : 'i',
10587             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10588             tooltip : 'This field is required'
10589         };
10590         if (Roo.bootstrap.version == 4) {
10591             indicator = {
10592                 tag : 'i',
10593                 style : 'display:none'
10594             };
10595         }
10596         
10597         
10598         if (align ==='left' && this.fieldLabel.length) {
10599             
10600             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10601
10602             cfg.cn = [
10603                 indicator,
10604                 {
10605                     tag: 'label',
10606                     'for' :  id,
10607                     cls : 'control-label',
10608                     html : this.fieldLabel
10609
10610                 },
10611                 {
10612                     cls : "", 
10613                     cn: [
10614                         combobox
10615                     ]
10616                 }
10617
10618             ];
10619             
10620             var labelCfg = cfg.cn[1];
10621             var contentCfg = cfg.cn[2];
10622             
10623             if(this.indicatorpos == 'right'){
10624                 cfg.cn = [
10625                     {
10626                         tag: 'label',
10627                         'for' :  id,
10628                         cls : 'control-label',
10629                         cn : [
10630                             {
10631                                 tag : 'span',
10632                                 html : this.fieldLabel
10633                             },
10634                             indicator
10635                         ]
10636                     },
10637                     {
10638                         cls : "", 
10639                         cn: [
10640                             combobox
10641                         ]
10642                     }
10643
10644                 ];
10645                 
10646                 labelCfg = cfg.cn[0];
10647                 contentCfg = cfg.cn[1];
10648             }
10649             
10650             if(this.labelWidth > 12){
10651                 labelCfg.style = "width: " + this.labelWidth + 'px';
10652             }
10653             
10654             if(this.labelWidth < 13 && this.labelmd == 0){
10655                 this.labelmd = this.labelWidth;
10656             }
10657             
10658             if(this.labellg > 0){
10659                 labelCfg.cls += ' col-lg-' + this.labellg;
10660                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10661             }
10662             
10663             if(this.labelmd > 0){
10664                 labelCfg.cls += ' col-md-' + this.labelmd;
10665                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10666             }
10667             
10668             if(this.labelsm > 0){
10669                 labelCfg.cls += ' col-sm-' + this.labelsm;
10670                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10671             }
10672             
10673             if(this.labelxs > 0){
10674                 labelCfg.cls += ' col-xs-' + this.labelxs;
10675                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10676             }
10677             
10678         } else if ( this.fieldLabel.length) {
10679 //                Roo.log(" label");
10680             cfg.cn = [
10681                 indicator,
10682                {
10683                    tag: 'label',
10684                    //cls : 'input-group-addon',
10685                    html : this.fieldLabel
10686
10687                },
10688
10689                combobox
10690
10691             ];
10692             
10693             if(this.indicatorpos == 'right'){
10694                 
10695                 cfg.cn = [
10696                     {
10697                        tag: 'label',
10698                        cn : [
10699                            {
10700                                tag : 'span',
10701                                html : this.fieldLabel
10702                            },
10703                            indicator
10704                        ]
10705
10706                     },
10707                     combobox
10708
10709                 ];
10710
10711             }
10712
10713         } else {
10714             
10715 //                Roo.log(" no label && no align");
10716                 cfg = combobox
10717                      
10718                 
10719         }
10720         
10721         var settings=this;
10722         ['xs','sm','md','lg'].map(function(size){
10723             if (settings[size]) {
10724                 cfg.cls += ' col-' + size + '-' + settings[size];
10725             }
10726         });
10727         
10728         return cfg;
10729         
10730     },
10731     
10732     
10733     
10734     // private
10735     onResize : function(w, h){
10736 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10737 //        if(typeof w == 'number'){
10738 //            var x = w - this.trigger.getWidth();
10739 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10740 //            this.trigger.setStyle('left', x+'px');
10741 //        }
10742     },
10743
10744     // private
10745     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10746
10747     // private
10748     getResizeEl : function(){
10749         return this.inputEl();
10750     },
10751
10752     // private
10753     getPositionEl : function(){
10754         return this.inputEl();
10755     },
10756
10757     // private
10758     alignErrorIcon : function(){
10759         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10760     },
10761
10762     // private
10763     initEvents : function(){
10764         
10765         this.createList();
10766         
10767         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10768         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10769         if(!this.multiple && this.showToggleBtn){
10770             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10771             if(this.hideTrigger){
10772                 this.trigger.setDisplayed(false);
10773             }
10774             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10775         }
10776         
10777         if(this.multiple){
10778             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10779         }
10780         
10781         if(this.removable && !this.editable && !this.tickable){
10782             var close = this.closeTriggerEl();
10783             
10784             if(close){
10785                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10786                 close.on('click', this.removeBtnClick, this, close);
10787             }
10788         }
10789         
10790         //this.trigger.addClassOnOver('x-form-trigger-over');
10791         //this.trigger.addClassOnClick('x-form-trigger-click');
10792         
10793         //if(!this.width){
10794         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10795         //}
10796     },
10797     
10798     closeTriggerEl : function()
10799     {
10800         var close = this.el.select('.roo-combo-removable-btn', true).first();
10801         return close ? close : false;
10802     },
10803     
10804     removeBtnClick : function(e, h, el)
10805     {
10806         e.preventDefault();
10807         
10808         if(this.fireEvent("remove", this) !== false){
10809             this.reset();
10810             this.fireEvent("afterremove", this)
10811         }
10812     },
10813     
10814     createList : function()
10815     {
10816         this.list = Roo.get(document.body).createChild({
10817             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10818             cls: 'typeahead typeahead-long dropdown-menu',
10819             style: 'display:none'
10820         });
10821         
10822         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10823         
10824     },
10825
10826     // private
10827     initTrigger : function(){
10828        
10829     },
10830
10831     // private
10832     onDestroy : function(){
10833         if(this.trigger){
10834             this.trigger.removeAllListeners();
10835           //  this.trigger.remove();
10836         }
10837         //if(this.wrap){
10838         //    this.wrap.remove();
10839         //}
10840         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10841     },
10842
10843     // private
10844     onFocus : function(){
10845         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10846         /*
10847         if(!this.mimicing){
10848             this.wrap.addClass('x-trigger-wrap-focus');
10849             this.mimicing = true;
10850             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10851             if(this.monitorTab){
10852                 this.el.on("keydown", this.checkTab, this);
10853             }
10854         }
10855         */
10856     },
10857
10858     // private
10859     checkTab : function(e){
10860         if(e.getKey() == e.TAB){
10861             this.triggerBlur();
10862         }
10863     },
10864
10865     // private
10866     onBlur : function(){
10867         // do nothing
10868     },
10869
10870     // private
10871     mimicBlur : function(e, t){
10872         /*
10873         if(!this.wrap.contains(t) && this.validateBlur()){
10874             this.triggerBlur();
10875         }
10876         */
10877     },
10878
10879     // private
10880     triggerBlur : function(){
10881         this.mimicing = false;
10882         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10883         if(this.monitorTab){
10884             this.el.un("keydown", this.checkTab, this);
10885         }
10886         //this.wrap.removeClass('x-trigger-wrap-focus');
10887         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10888     },
10889
10890     // private
10891     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10892     validateBlur : function(e, t){
10893         return true;
10894     },
10895
10896     // private
10897     onDisable : function(){
10898         this.inputEl().dom.disabled = true;
10899         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10900         //if(this.wrap){
10901         //    this.wrap.addClass('x-item-disabled');
10902         //}
10903     },
10904
10905     // private
10906     onEnable : function(){
10907         this.inputEl().dom.disabled = false;
10908         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10909         //if(this.wrap){
10910         //    this.el.removeClass('x-item-disabled');
10911         //}
10912     },
10913
10914     // private
10915     onShow : function(){
10916         var ae = this.getActionEl();
10917         
10918         if(ae){
10919             ae.dom.style.display = '';
10920             ae.dom.style.visibility = 'visible';
10921         }
10922     },
10923
10924     // private
10925     
10926     onHide : function(){
10927         var ae = this.getActionEl();
10928         ae.dom.style.display = 'none';
10929     },
10930
10931     /**
10932      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10933      * by an implementing function.
10934      * @method
10935      * @param {EventObject} e
10936      */
10937     onTriggerClick : Roo.emptyFn
10938 });
10939  /*
10940  * Based on:
10941  * Ext JS Library 1.1.1
10942  * Copyright(c) 2006-2007, Ext JS, LLC.
10943  *
10944  * Originally Released Under LGPL - original licence link has changed is not relivant.
10945  *
10946  * Fork - LGPL
10947  * <script type="text/javascript">
10948  */
10949
10950
10951 /**
10952  * @class Roo.data.SortTypes
10953  * @singleton
10954  * Defines the default sorting (casting?) comparison functions used when sorting data.
10955  */
10956 Roo.data.SortTypes = {
10957     /**
10958      * Default sort that does nothing
10959      * @param {Mixed} s The value being converted
10960      * @return {Mixed} The comparison value
10961      */
10962     none : function(s){
10963         return s;
10964     },
10965     
10966     /**
10967      * The regular expression used to strip tags
10968      * @type {RegExp}
10969      * @property
10970      */
10971     stripTagsRE : /<\/?[^>]+>/gi,
10972     
10973     /**
10974      * Strips all HTML tags to sort on text only
10975      * @param {Mixed} s The value being converted
10976      * @return {String} The comparison value
10977      */
10978     asText : function(s){
10979         return String(s).replace(this.stripTagsRE, "");
10980     },
10981     
10982     /**
10983      * Strips all HTML tags to sort on text only - Case insensitive
10984      * @param {Mixed} s The value being converted
10985      * @return {String} The comparison value
10986      */
10987     asUCText : function(s){
10988         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10989     },
10990     
10991     /**
10992      * Case insensitive string
10993      * @param {Mixed} s The value being converted
10994      * @return {String} The comparison value
10995      */
10996     asUCString : function(s) {
10997         return String(s).toUpperCase();
10998     },
10999     
11000     /**
11001      * Date sorting
11002      * @param {Mixed} s The value being converted
11003      * @return {Number} The comparison value
11004      */
11005     asDate : function(s) {
11006         if(!s){
11007             return 0;
11008         }
11009         if(s instanceof Date){
11010             return s.getTime();
11011         }
11012         return Date.parse(String(s));
11013     },
11014     
11015     /**
11016      * Float sorting
11017      * @param {Mixed} s The value being converted
11018      * @return {Float} The comparison value
11019      */
11020     asFloat : function(s) {
11021         var val = parseFloat(String(s).replace(/,/g, ""));
11022         if(isNaN(val)) {
11023             val = 0;
11024         }
11025         return val;
11026     },
11027     
11028     /**
11029      * Integer sorting
11030      * @param {Mixed} s The value being converted
11031      * @return {Number} The comparison value
11032      */
11033     asInt : function(s) {
11034         var val = parseInt(String(s).replace(/,/g, ""));
11035         if(isNaN(val)) {
11036             val = 0;
11037         }
11038         return val;
11039     }
11040 };/*
11041  * Based on:
11042  * Ext JS Library 1.1.1
11043  * Copyright(c) 2006-2007, Ext JS, LLC.
11044  *
11045  * Originally Released Under LGPL - original licence link has changed is not relivant.
11046  *
11047  * Fork - LGPL
11048  * <script type="text/javascript">
11049  */
11050
11051 /**
11052 * @class Roo.data.Record
11053  * Instances of this class encapsulate both record <em>definition</em> information, and record
11054  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11055  * to access Records cached in an {@link Roo.data.Store} object.<br>
11056  * <p>
11057  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11058  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11059  * objects.<br>
11060  * <p>
11061  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11062  * @constructor
11063  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11064  * {@link #create}. The parameters are the same.
11065  * @param {Array} data An associative Array of data values keyed by the field name.
11066  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11067  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11068  * not specified an integer id is generated.
11069  */
11070 Roo.data.Record = function(data, id){
11071     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11072     this.data = data;
11073 };
11074
11075 /**
11076  * Generate a constructor for a specific record layout.
11077  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11078  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11079  * Each field definition object may contain the following properties: <ul>
11080  * <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,
11081  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11082  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11083  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11084  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11085  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11086  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11087  * this may be omitted.</p></li>
11088  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11089  * <ul><li>auto (Default, implies no conversion)</li>
11090  * <li>string</li>
11091  * <li>int</li>
11092  * <li>float</li>
11093  * <li>boolean</li>
11094  * <li>date</li></ul></p></li>
11095  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11096  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11097  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11098  * by the Reader into an object that will be stored in the Record. It is passed the
11099  * following parameters:<ul>
11100  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11101  * </ul></p></li>
11102  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11103  * </ul>
11104  * <br>usage:<br><pre><code>
11105 var TopicRecord = Roo.data.Record.create(
11106     {name: 'title', mapping: 'topic_title'},
11107     {name: 'author', mapping: 'username'},
11108     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11109     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11110     {name: 'lastPoster', mapping: 'user2'},
11111     {name: 'excerpt', mapping: 'post_text'}
11112 );
11113
11114 var myNewRecord = new TopicRecord({
11115     title: 'Do my job please',
11116     author: 'noobie',
11117     totalPosts: 1,
11118     lastPost: new Date(),
11119     lastPoster: 'Animal',
11120     excerpt: 'No way dude!'
11121 });
11122 myStore.add(myNewRecord);
11123 </code></pre>
11124  * @method create
11125  * @static
11126  */
11127 Roo.data.Record.create = function(o){
11128     var f = function(){
11129         f.superclass.constructor.apply(this, arguments);
11130     };
11131     Roo.extend(f, Roo.data.Record);
11132     var p = f.prototype;
11133     p.fields = new Roo.util.MixedCollection(false, function(field){
11134         return field.name;
11135     });
11136     for(var i = 0, len = o.length; i < len; i++){
11137         p.fields.add(new Roo.data.Field(o[i]));
11138     }
11139     f.getField = function(name){
11140         return p.fields.get(name);  
11141     };
11142     return f;
11143 };
11144
11145 Roo.data.Record.AUTO_ID = 1000;
11146 Roo.data.Record.EDIT = 'edit';
11147 Roo.data.Record.REJECT = 'reject';
11148 Roo.data.Record.COMMIT = 'commit';
11149
11150 Roo.data.Record.prototype = {
11151     /**
11152      * Readonly flag - true if this record has been modified.
11153      * @type Boolean
11154      */
11155     dirty : false,
11156     editing : false,
11157     error: null,
11158     modified: null,
11159
11160     // private
11161     join : function(store){
11162         this.store = store;
11163     },
11164
11165     /**
11166      * Set the named field to the specified value.
11167      * @param {String} name The name of the field to set.
11168      * @param {Object} value The value to set the field to.
11169      */
11170     set : function(name, value){
11171         if(this.data[name] == value){
11172             return;
11173         }
11174         this.dirty = true;
11175         if(!this.modified){
11176             this.modified = {};
11177         }
11178         if(typeof this.modified[name] == 'undefined'){
11179             this.modified[name] = this.data[name];
11180         }
11181         this.data[name] = value;
11182         if(!this.editing && this.store){
11183             this.store.afterEdit(this);
11184         }       
11185     },
11186
11187     /**
11188      * Get the value of the named field.
11189      * @param {String} name The name of the field to get the value of.
11190      * @return {Object} The value of the field.
11191      */
11192     get : function(name){
11193         return this.data[name]; 
11194     },
11195
11196     // private
11197     beginEdit : function(){
11198         this.editing = true;
11199         this.modified = {}; 
11200     },
11201
11202     // private
11203     cancelEdit : function(){
11204         this.editing = false;
11205         delete this.modified;
11206     },
11207
11208     // private
11209     endEdit : function(){
11210         this.editing = false;
11211         if(this.dirty && this.store){
11212             this.store.afterEdit(this);
11213         }
11214     },
11215
11216     /**
11217      * Usually called by the {@link Roo.data.Store} which owns the Record.
11218      * Rejects all changes made to the Record since either creation, or the last commit operation.
11219      * Modified fields are reverted to their original values.
11220      * <p>
11221      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11222      * of reject operations.
11223      */
11224     reject : function(){
11225         var m = this.modified;
11226         for(var n in m){
11227             if(typeof m[n] != "function"){
11228                 this.data[n] = m[n];
11229             }
11230         }
11231         this.dirty = false;
11232         delete this.modified;
11233         this.editing = false;
11234         if(this.store){
11235             this.store.afterReject(this);
11236         }
11237     },
11238
11239     /**
11240      * Usually called by the {@link Roo.data.Store} which owns the Record.
11241      * Commits all changes made to the Record since either creation, or the last commit operation.
11242      * <p>
11243      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11244      * of commit operations.
11245      */
11246     commit : function(){
11247         this.dirty = false;
11248         delete this.modified;
11249         this.editing = false;
11250         if(this.store){
11251             this.store.afterCommit(this);
11252         }
11253     },
11254
11255     // private
11256     hasError : function(){
11257         return this.error != null;
11258     },
11259
11260     // private
11261     clearError : function(){
11262         this.error = null;
11263     },
11264
11265     /**
11266      * Creates a copy of this record.
11267      * @param {String} id (optional) A new record id if you don't want to use this record's id
11268      * @return {Record}
11269      */
11270     copy : function(newId) {
11271         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11272     }
11273 };/*
11274  * Based on:
11275  * Ext JS Library 1.1.1
11276  * Copyright(c) 2006-2007, Ext JS, LLC.
11277  *
11278  * Originally Released Under LGPL - original licence link has changed is not relivant.
11279  *
11280  * Fork - LGPL
11281  * <script type="text/javascript">
11282  */
11283
11284
11285
11286 /**
11287  * @class Roo.data.Store
11288  * @extends Roo.util.Observable
11289  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11290  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11291  * <p>
11292  * 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
11293  * has no knowledge of the format of the data returned by the Proxy.<br>
11294  * <p>
11295  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11296  * instances from the data object. These records are cached and made available through accessor functions.
11297  * @constructor
11298  * Creates a new Store.
11299  * @param {Object} config A config object containing the objects needed for the Store to access data,
11300  * and read the data into Records.
11301  */
11302 Roo.data.Store = function(config){
11303     this.data = new Roo.util.MixedCollection(false);
11304     this.data.getKey = function(o){
11305         return o.id;
11306     };
11307     this.baseParams = {};
11308     // private
11309     this.paramNames = {
11310         "start" : "start",
11311         "limit" : "limit",
11312         "sort" : "sort",
11313         "dir" : "dir",
11314         "multisort" : "_multisort"
11315     };
11316
11317     if(config && config.data){
11318         this.inlineData = config.data;
11319         delete config.data;
11320     }
11321
11322     Roo.apply(this, config);
11323     
11324     if(this.reader){ // reader passed
11325         this.reader = Roo.factory(this.reader, Roo.data);
11326         this.reader.xmodule = this.xmodule || false;
11327         if(!this.recordType){
11328             this.recordType = this.reader.recordType;
11329         }
11330         if(this.reader.onMetaChange){
11331             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11332         }
11333     }
11334
11335     if(this.recordType){
11336         this.fields = this.recordType.prototype.fields;
11337     }
11338     this.modified = [];
11339
11340     this.addEvents({
11341         /**
11342          * @event datachanged
11343          * Fires when the data cache has changed, and a widget which is using this Store
11344          * as a Record cache should refresh its view.
11345          * @param {Store} this
11346          */
11347         datachanged : true,
11348         /**
11349          * @event metachange
11350          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11351          * @param {Store} this
11352          * @param {Object} meta The JSON metadata
11353          */
11354         metachange : true,
11355         /**
11356          * @event add
11357          * Fires when Records have been added to the Store
11358          * @param {Store} this
11359          * @param {Roo.data.Record[]} records The array of Records added
11360          * @param {Number} index The index at which the record(s) were added
11361          */
11362         add : true,
11363         /**
11364          * @event remove
11365          * Fires when a Record has been removed from the Store
11366          * @param {Store} this
11367          * @param {Roo.data.Record} record The Record that was removed
11368          * @param {Number} index The index at which the record was removed
11369          */
11370         remove : true,
11371         /**
11372          * @event update
11373          * Fires when a Record has been updated
11374          * @param {Store} this
11375          * @param {Roo.data.Record} record The Record that was updated
11376          * @param {String} operation The update operation being performed.  Value may be one of:
11377          * <pre><code>
11378  Roo.data.Record.EDIT
11379  Roo.data.Record.REJECT
11380  Roo.data.Record.COMMIT
11381          * </code></pre>
11382          */
11383         update : true,
11384         /**
11385          * @event clear
11386          * Fires when the data cache has been cleared.
11387          * @param {Store} this
11388          */
11389         clear : true,
11390         /**
11391          * @event beforeload
11392          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11393          * the load action will be canceled.
11394          * @param {Store} this
11395          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11396          */
11397         beforeload : true,
11398         /**
11399          * @event beforeloadadd
11400          * Fires after a new set of Records has been loaded.
11401          * @param {Store} this
11402          * @param {Roo.data.Record[]} records The Records that were loaded
11403          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11404          */
11405         beforeloadadd : true,
11406         /**
11407          * @event load
11408          * Fires after a new set of Records has been loaded, before they are added to the store.
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          * @params {Object} return from reader
11413          */
11414         load : true,
11415         /**
11416          * @event loadexception
11417          * Fires if an exception occurs in the Proxy during loading.
11418          * Called with the signature of the Proxy's "loadexception" event.
11419          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11420          * 
11421          * @param {Proxy} 
11422          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11423          * @param {Object} load options 
11424          * @param {Object} jsonData from your request (normally this contains the Exception)
11425          */
11426         loadexception : true
11427     });
11428     
11429     if(this.proxy){
11430         this.proxy = Roo.factory(this.proxy, Roo.data);
11431         this.proxy.xmodule = this.xmodule || false;
11432         this.relayEvents(this.proxy,  ["loadexception"]);
11433     }
11434     this.sortToggle = {};
11435     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11436
11437     Roo.data.Store.superclass.constructor.call(this);
11438
11439     if(this.inlineData){
11440         this.loadData(this.inlineData);
11441         delete this.inlineData;
11442     }
11443 };
11444
11445 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11446      /**
11447     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11448     * without a remote query - used by combo/forms at present.
11449     */
11450     
11451     /**
11452     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11453     */
11454     /**
11455     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11456     */
11457     /**
11458     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11459     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11460     */
11461     /**
11462     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11463     * on any HTTP request
11464     */
11465     /**
11466     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11467     */
11468     /**
11469     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11470     */
11471     multiSort: false,
11472     /**
11473     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11474     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11475     */
11476     remoteSort : false,
11477
11478     /**
11479     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11480      * loaded or when a record is removed. (defaults to false).
11481     */
11482     pruneModifiedRecords : false,
11483
11484     // private
11485     lastOptions : null,
11486
11487     /**
11488      * Add Records to the Store and fires the add event.
11489      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11490      */
11491     add : function(records){
11492         records = [].concat(records);
11493         for(var i = 0, len = records.length; i < len; i++){
11494             records[i].join(this);
11495         }
11496         var index = this.data.length;
11497         this.data.addAll(records);
11498         this.fireEvent("add", this, records, index);
11499     },
11500
11501     /**
11502      * Remove a Record from the Store and fires the remove event.
11503      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11504      */
11505     remove : function(record){
11506         var index = this.data.indexOf(record);
11507         this.data.removeAt(index);
11508  
11509         if(this.pruneModifiedRecords){
11510             this.modified.remove(record);
11511         }
11512         this.fireEvent("remove", this, record, index);
11513     },
11514
11515     /**
11516      * Remove all Records from the Store and fires the clear event.
11517      */
11518     removeAll : function(){
11519         this.data.clear();
11520         if(this.pruneModifiedRecords){
11521             this.modified = [];
11522         }
11523         this.fireEvent("clear", this);
11524     },
11525
11526     /**
11527      * Inserts Records to the Store at the given index and fires the add event.
11528      * @param {Number} index The start index at which to insert the passed Records.
11529      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11530      */
11531     insert : function(index, records){
11532         records = [].concat(records);
11533         for(var i = 0, len = records.length; i < len; i++){
11534             this.data.insert(index, records[i]);
11535             records[i].join(this);
11536         }
11537         this.fireEvent("add", this, records, index);
11538     },
11539
11540     /**
11541      * Get the index within the cache of the passed Record.
11542      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11543      * @return {Number} The index of the passed Record. Returns -1 if not found.
11544      */
11545     indexOf : function(record){
11546         return this.data.indexOf(record);
11547     },
11548
11549     /**
11550      * Get the index within the cache of the Record with the passed id.
11551      * @param {String} id The id of the Record to find.
11552      * @return {Number} The index of the Record. Returns -1 if not found.
11553      */
11554     indexOfId : function(id){
11555         return this.data.indexOfKey(id);
11556     },
11557
11558     /**
11559      * Get the Record with the specified id.
11560      * @param {String} id The id of the Record to find.
11561      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11562      */
11563     getById : function(id){
11564         return this.data.key(id);
11565     },
11566
11567     /**
11568      * Get the Record at the specified index.
11569      * @param {Number} index The index of the Record to find.
11570      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11571      */
11572     getAt : function(index){
11573         return this.data.itemAt(index);
11574     },
11575
11576     /**
11577      * Returns a range of Records between specified indices.
11578      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11579      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11580      * @return {Roo.data.Record[]} An array of Records
11581      */
11582     getRange : function(start, end){
11583         return this.data.getRange(start, end);
11584     },
11585
11586     // private
11587     storeOptions : function(o){
11588         o = Roo.apply({}, o);
11589         delete o.callback;
11590         delete o.scope;
11591         this.lastOptions = o;
11592     },
11593
11594     /**
11595      * Loads the Record cache from the configured Proxy using the configured Reader.
11596      * <p>
11597      * If using remote paging, then the first load call must specify the <em>start</em>
11598      * and <em>limit</em> properties in the options.params property to establish the initial
11599      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11600      * <p>
11601      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11602      * and this call will return before the new data has been loaded. Perform any post-processing
11603      * in a callback function, or in a "load" event handler.</strong>
11604      * <p>
11605      * @param {Object} options An object containing properties which control loading options:<ul>
11606      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11607      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11608      * passed the following arguments:<ul>
11609      * <li>r : Roo.data.Record[]</li>
11610      * <li>options: Options object from the load call</li>
11611      * <li>success: Boolean success indicator</li></ul></li>
11612      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11613      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11614      * </ul>
11615      */
11616     load : function(options){
11617         options = options || {};
11618         if(this.fireEvent("beforeload", this, options) !== false){
11619             this.storeOptions(options);
11620             var p = Roo.apply(options.params || {}, this.baseParams);
11621             // if meta was not loaded from remote source.. try requesting it.
11622             if (!this.reader.metaFromRemote) {
11623                 p._requestMeta = 1;
11624             }
11625             if(this.sortInfo && this.remoteSort){
11626                 var pn = this.paramNames;
11627                 p[pn["sort"]] = this.sortInfo.field;
11628                 p[pn["dir"]] = this.sortInfo.direction;
11629             }
11630             if (this.multiSort) {
11631                 var pn = this.paramNames;
11632                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11633             }
11634             
11635             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11636         }
11637     },
11638
11639     /**
11640      * Reloads the Record cache from the configured Proxy using the configured Reader and
11641      * the options from the last load operation performed.
11642      * @param {Object} options (optional) An object containing properties which may override the options
11643      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11644      * the most recently used options are reused).
11645      */
11646     reload : function(options){
11647         this.load(Roo.applyIf(options||{}, this.lastOptions));
11648     },
11649
11650     // private
11651     // Called as a callback by the Reader during a load operation.
11652     loadRecords : function(o, options, success){
11653         if(!o || success === false){
11654             if(success !== false){
11655                 this.fireEvent("load", this, [], options, o);
11656             }
11657             if(options.callback){
11658                 options.callback.call(options.scope || this, [], options, false);
11659             }
11660             return;
11661         }
11662         // if data returned failure - throw an exception.
11663         if (o.success === false) {
11664             // show a message if no listener is registered.
11665             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11666                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11667             }
11668             // loadmask wil be hooked into this..
11669             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11670             return;
11671         }
11672         var r = o.records, t = o.totalRecords || r.length;
11673         
11674         this.fireEvent("beforeloadadd", this, r, options, o);
11675         
11676         if(!options || options.add !== true){
11677             if(this.pruneModifiedRecords){
11678                 this.modified = [];
11679             }
11680             for(var i = 0, len = r.length; i < len; i++){
11681                 r[i].join(this);
11682             }
11683             if(this.snapshot){
11684                 this.data = this.snapshot;
11685                 delete this.snapshot;
11686             }
11687             this.data.clear();
11688             this.data.addAll(r);
11689             this.totalLength = t;
11690             this.applySort();
11691             this.fireEvent("datachanged", this);
11692         }else{
11693             this.totalLength = Math.max(t, this.data.length+r.length);
11694             this.add(r);
11695         }
11696         
11697         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11698                 
11699             var e = new Roo.data.Record({});
11700
11701             e.set(this.parent.displayField, this.parent.emptyTitle);
11702             e.set(this.parent.valueField, '');
11703
11704             this.insert(0, e);
11705         }
11706             
11707         this.fireEvent("load", this, r, options, o);
11708         if(options.callback){
11709             options.callback.call(options.scope || this, r, options, true);
11710         }
11711     },
11712
11713
11714     /**
11715      * Loads data from a passed data block. A Reader which understands the format of the data
11716      * must have been configured in the constructor.
11717      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11718      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11719      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11720      */
11721     loadData : function(o, append){
11722         var r = this.reader.readRecords(o);
11723         this.loadRecords(r, {add: append}, true);
11724     },
11725
11726     /**
11727      * Gets the number of cached records.
11728      * <p>
11729      * <em>If using paging, this may not be the total size of the dataset. If the data object
11730      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11731      * the data set size</em>
11732      */
11733     getCount : function(){
11734         return this.data.length || 0;
11735     },
11736
11737     /**
11738      * Gets the total number of records in the dataset as returned by the server.
11739      * <p>
11740      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11741      * the dataset size</em>
11742      */
11743     getTotalCount : function(){
11744         return this.totalLength || 0;
11745     },
11746
11747     /**
11748      * Returns the sort state of the Store as an object with two properties:
11749      * <pre><code>
11750  field {String} The name of the field by which the Records are sorted
11751  direction {String} The sort order, "ASC" or "DESC"
11752      * </code></pre>
11753      */
11754     getSortState : function(){
11755         return this.sortInfo;
11756     },
11757
11758     // private
11759     applySort : function(){
11760         if(this.sortInfo && !this.remoteSort){
11761             var s = this.sortInfo, f = s.field;
11762             var st = this.fields.get(f).sortType;
11763             var fn = function(r1, r2){
11764                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11765                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11766             };
11767             this.data.sort(s.direction, fn);
11768             if(this.snapshot && this.snapshot != this.data){
11769                 this.snapshot.sort(s.direction, fn);
11770             }
11771         }
11772     },
11773
11774     /**
11775      * Sets the default sort column and order to be used by the next load operation.
11776      * @param {String} fieldName The name of the field to sort by.
11777      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11778      */
11779     setDefaultSort : function(field, dir){
11780         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11781     },
11782
11783     /**
11784      * Sort the Records.
11785      * If remote sorting is used, the sort is performed on the server, and the cache is
11786      * reloaded. If local sorting is used, the cache is sorted internally.
11787      * @param {String} fieldName The name of the field to sort by.
11788      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11789      */
11790     sort : function(fieldName, dir){
11791         var f = this.fields.get(fieldName);
11792         if(!dir){
11793             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11794             
11795             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11796                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11797             }else{
11798                 dir = f.sortDir;
11799             }
11800         }
11801         this.sortToggle[f.name] = dir;
11802         this.sortInfo = {field: f.name, direction: dir};
11803         if(!this.remoteSort){
11804             this.applySort();
11805             this.fireEvent("datachanged", this);
11806         }else{
11807             this.load(this.lastOptions);
11808         }
11809     },
11810
11811     /**
11812      * Calls the specified function for each of the Records in the cache.
11813      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11814      * Returning <em>false</em> aborts and exits the iteration.
11815      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11816      */
11817     each : function(fn, scope){
11818         this.data.each(fn, scope);
11819     },
11820
11821     /**
11822      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11823      * (e.g., during paging).
11824      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11825      */
11826     getModifiedRecords : function(){
11827         return this.modified;
11828     },
11829
11830     // private
11831     createFilterFn : function(property, value, anyMatch){
11832         if(!value.exec){ // not a regex
11833             value = String(value);
11834             if(value.length == 0){
11835                 return false;
11836             }
11837             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11838         }
11839         return function(r){
11840             return value.test(r.data[property]);
11841         };
11842     },
11843
11844     /**
11845      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11846      * @param {String} property A field on your records
11847      * @param {Number} start The record index to start at (defaults to 0)
11848      * @param {Number} end The last record index to include (defaults to length - 1)
11849      * @return {Number} The sum
11850      */
11851     sum : function(property, start, end){
11852         var rs = this.data.items, v = 0;
11853         start = start || 0;
11854         end = (end || end === 0) ? end : rs.length-1;
11855
11856         for(var i = start; i <= end; i++){
11857             v += (rs[i].data[property] || 0);
11858         }
11859         return v;
11860     },
11861
11862     /**
11863      * Filter the records by a specified property.
11864      * @param {String} field A field on your records
11865      * @param {String/RegExp} value Either a string that the field
11866      * should start with or a RegExp to test against the field
11867      * @param {Boolean} anyMatch True to match any part not just the beginning
11868      */
11869     filter : function(property, value, anyMatch){
11870         var fn = this.createFilterFn(property, value, anyMatch);
11871         return fn ? this.filterBy(fn) : this.clearFilter();
11872     },
11873
11874     /**
11875      * Filter by a function. The specified function will be called with each
11876      * record in this data source. If the function returns true the record is included,
11877      * otherwise it is filtered.
11878      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11879      * @param {Object} scope (optional) The scope of the function (defaults to this)
11880      */
11881     filterBy : function(fn, scope){
11882         this.snapshot = this.snapshot || this.data;
11883         this.data = this.queryBy(fn, scope||this);
11884         this.fireEvent("datachanged", this);
11885     },
11886
11887     /**
11888      * Query the records by a specified property.
11889      * @param {String} field A field on your records
11890      * @param {String/RegExp} value Either a string that the field
11891      * should start with or a RegExp to test against the field
11892      * @param {Boolean} anyMatch True to match any part not just the beginning
11893      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11894      */
11895     query : function(property, value, anyMatch){
11896         var fn = this.createFilterFn(property, value, anyMatch);
11897         return fn ? this.queryBy(fn) : this.data.clone();
11898     },
11899
11900     /**
11901      * Query by a function. The specified function will be called with each
11902      * record in this data source. If the function returns true the record is included
11903      * in the results.
11904      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11905      * @param {Object} scope (optional) The scope of the function (defaults to this)
11906       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11907      **/
11908     queryBy : function(fn, scope){
11909         var data = this.snapshot || this.data;
11910         return data.filterBy(fn, scope||this);
11911     },
11912
11913     /**
11914      * Collects unique values for a particular dataIndex from this store.
11915      * @param {String} dataIndex The property to collect
11916      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11917      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11918      * @return {Array} An array of the unique values
11919      **/
11920     collect : function(dataIndex, allowNull, bypassFilter){
11921         var d = (bypassFilter === true && this.snapshot) ?
11922                 this.snapshot.items : this.data.items;
11923         var v, sv, r = [], l = {};
11924         for(var i = 0, len = d.length; i < len; i++){
11925             v = d[i].data[dataIndex];
11926             sv = String(v);
11927             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11928                 l[sv] = true;
11929                 r[r.length] = v;
11930             }
11931         }
11932         return r;
11933     },
11934
11935     /**
11936      * Revert to a view of the Record cache with no filtering applied.
11937      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11938      */
11939     clearFilter : function(suppressEvent){
11940         if(this.snapshot && this.snapshot != this.data){
11941             this.data = this.snapshot;
11942             delete this.snapshot;
11943             if(suppressEvent !== true){
11944                 this.fireEvent("datachanged", this);
11945             }
11946         }
11947     },
11948
11949     // private
11950     afterEdit : function(record){
11951         if(this.modified.indexOf(record) == -1){
11952             this.modified.push(record);
11953         }
11954         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11955     },
11956     
11957     // private
11958     afterReject : function(record){
11959         this.modified.remove(record);
11960         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11961     },
11962
11963     // private
11964     afterCommit : function(record){
11965         this.modified.remove(record);
11966         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11967     },
11968
11969     /**
11970      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11971      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11972      */
11973     commitChanges : function(){
11974         var m = this.modified.slice(0);
11975         this.modified = [];
11976         for(var i = 0, len = m.length; i < len; i++){
11977             m[i].commit();
11978         }
11979     },
11980
11981     /**
11982      * Cancel outstanding changes on all changed records.
11983      */
11984     rejectChanges : function(){
11985         var m = this.modified.slice(0);
11986         this.modified = [];
11987         for(var i = 0, len = m.length; i < len; i++){
11988             m[i].reject();
11989         }
11990     },
11991
11992     onMetaChange : function(meta, rtype, o){
11993         this.recordType = rtype;
11994         this.fields = rtype.prototype.fields;
11995         delete this.snapshot;
11996         this.sortInfo = meta.sortInfo || this.sortInfo;
11997         this.modified = [];
11998         this.fireEvent('metachange', this, this.reader.meta);
11999     },
12000     
12001     moveIndex : function(data, type)
12002     {
12003         var index = this.indexOf(data);
12004         
12005         var newIndex = index + type;
12006         
12007         this.remove(data);
12008         
12009         this.insert(newIndex, data);
12010         
12011     }
12012 });/*
12013  * Based on:
12014  * Ext JS Library 1.1.1
12015  * Copyright(c) 2006-2007, Ext JS, LLC.
12016  *
12017  * Originally Released Under LGPL - original licence link has changed is not relivant.
12018  *
12019  * Fork - LGPL
12020  * <script type="text/javascript">
12021  */
12022
12023 /**
12024  * @class Roo.data.SimpleStore
12025  * @extends Roo.data.Store
12026  * Small helper class to make creating Stores from Array data easier.
12027  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12028  * @cfg {Array} fields An array of field definition objects, or field name strings.
12029  * @cfg {Array} data The multi-dimensional array of data
12030  * @constructor
12031  * @param {Object} config
12032  */
12033 Roo.data.SimpleStore = function(config){
12034     Roo.data.SimpleStore.superclass.constructor.call(this, {
12035         isLocal : true,
12036         reader: new Roo.data.ArrayReader({
12037                 id: config.id
12038             },
12039             Roo.data.Record.create(config.fields)
12040         ),
12041         proxy : new Roo.data.MemoryProxy(config.data)
12042     });
12043     this.load();
12044 };
12045 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12046  * Based on:
12047  * Ext JS Library 1.1.1
12048  * Copyright(c) 2006-2007, Ext JS, LLC.
12049  *
12050  * Originally Released Under LGPL - original licence link has changed is not relivant.
12051  *
12052  * Fork - LGPL
12053  * <script type="text/javascript">
12054  */
12055
12056 /**
12057 /**
12058  * @extends Roo.data.Store
12059  * @class Roo.data.JsonStore
12060  * Small helper class to make creating Stores for JSON data easier. <br/>
12061 <pre><code>
12062 var store = new Roo.data.JsonStore({
12063     url: 'get-images.php',
12064     root: 'images',
12065     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12066 });
12067 </code></pre>
12068  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12069  * JsonReader and HttpProxy (unless inline data is provided).</b>
12070  * @cfg {Array} fields An array of field definition objects, or field name strings.
12071  * @constructor
12072  * @param {Object} config
12073  */
12074 Roo.data.JsonStore = function(c){
12075     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12076         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12077         reader: new Roo.data.JsonReader(c, c.fields)
12078     }));
12079 };
12080 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12081  * Based on:
12082  * Ext JS Library 1.1.1
12083  * Copyright(c) 2006-2007, Ext JS, LLC.
12084  *
12085  * Originally Released Under LGPL - original licence link has changed is not relivant.
12086  *
12087  * Fork - LGPL
12088  * <script type="text/javascript">
12089  */
12090
12091  
12092 Roo.data.Field = function(config){
12093     if(typeof config == "string"){
12094         config = {name: config};
12095     }
12096     Roo.apply(this, config);
12097     
12098     if(!this.type){
12099         this.type = "auto";
12100     }
12101     
12102     var st = Roo.data.SortTypes;
12103     // named sortTypes are supported, here we look them up
12104     if(typeof this.sortType == "string"){
12105         this.sortType = st[this.sortType];
12106     }
12107     
12108     // set default sortType for strings and dates
12109     if(!this.sortType){
12110         switch(this.type){
12111             case "string":
12112                 this.sortType = st.asUCString;
12113                 break;
12114             case "date":
12115                 this.sortType = st.asDate;
12116                 break;
12117             default:
12118                 this.sortType = st.none;
12119         }
12120     }
12121
12122     // define once
12123     var stripRe = /[\$,%]/g;
12124
12125     // prebuilt conversion function for this field, instead of
12126     // switching every time we're reading a value
12127     if(!this.convert){
12128         var cv, dateFormat = this.dateFormat;
12129         switch(this.type){
12130             case "":
12131             case "auto":
12132             case undefined:
12133                 cv = function(v){ return v; };
12134                 break;
12135             case "string":
12136                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12137                 break;
12138             case "int":
12139                 cv = function(v){
12140                     return v !== undefined && v !== null && v !== '' ?
12141                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12142                     };
12143                 break;
12144             case "float":
12145                 cv = function(v){
12146                     return v !== undefined && v !== null && v !== '' ?
12147                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12148                     };
12149                 break;
12150             case "bool":
12151             case "boolean":
12152                 cv = function(v){ return v === true || v === "true" || v == 1; };
12153                 break;
12154             case "date":
12155                 cv = function(v){
12156                     if(!v){
12157                         return '';
12158                     }
12159                     if(v instanceof Date){
12160                         return v;
12161                     }
12162                     if(dateFormat){
12163                         if(dateFormat == "timestamp"){
12164                             return new Date(v*1000);
12165                         }
12166                         return Date.parseDate(v, dateFormat);
12167                     }
12168                     var parsed = Date.parse(v);
12169                     return parsed ? new Date(parsed) : null;
12170                 };
12171              break;
12172             
12173         }
12174         this.convert = cv;
12175     }
12176 };
12177
12178 Roo.data.Field.prototype = {
12179     dateFormat: null,
12180     defaultValue: "",
12181     mapping: null,
12182     sortType : null,
12183     sortDir : "ASC"
12184 };/*
12185  * Based on:
12186  * Ext JS Library 1.1.1
12187  * Copyright(c) 2006-2007, Ext JS, LLC.
12188  *
12189  * Originally Released Under LGPL - original licence link has changed is not relivant.
12190  *
12191  * Fork - LGPL
12192  * <script type="text/javascript">
12193  */
12194  
12195 // Base class for reading structured data from a data source.  This class is intended to be
12196 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12197
12198 /**
12199  * @class Roo.data.DataReader
12200  * Base class for reading structured data from a data source.  This class is intended to be
12201  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12202  */
12203
12204 Roo.data.DataReader = function(meta, recordType){
12205     
12206     this.meta = meta;
12207     
12208     this.recordType = recordType instanceof Array ? 
12209         Roo.data.Record.create(recordType) : recordType;
12210 };
12211
12212 Roo.data.DataReader.prototype = {
12213      /**
12214      * Create an empty record
12215      * @param {Object} data (optional) - overlay some values
12216      * @return {Roo.data.Record} record created.
12217      */
12218     newRow :  function(d) {
12219         var da =  {};
12220         this.recordType.prototype.fields.each(function(c) {
12221             switch( c.type) {
12222                 case 'int' : da[c.name] = 0; break;
12223                 case 'date' : da[c.name] = new Date(); break;
12224                 case 'float' : da[c.name] = 0.0; break;
12225                 case 'boolean' : da[c.name] = false; break;
12226                 default : da[c.name] = ""; break;
12227             }
12228             
12229         });
12230         return new this.recordType(Roo.apply(da, d));
12231     }
12232     
12233 };/*
12234  * Based on:
12235  * Ext JS Library 1.1.1
12236  * Copyright(c) 2006-2007, Ext JS, LLC.
12237  *
12238  * Originally Released Under LGPL - original licence link has changed is not relivant.
12239  *
12240  * Fork - LGPL
12241  * <script type="text/javascript">
12242  */
12243
12244 /**
12245  * @class Roo.data.DataProxy
12246  * @extends Roo.data.Observable
12247  * This class is an abstract base class for implementations which provide retrieval of
12248  * unformatted data objects.<br>
12249  * <p>
12250  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12251  * (of the appropriate type which knows how to parse the data object) to provide a block of
12252  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12253  * <p>
12254  * Custom implementations must implement the load method as described in
12255  * {@link Roo.data.HttpProxy#load}.
12256  */
12257 Roo.data.DataProxy = function(){
12258     this.addEvents({
12259         /**
12260          * @event beforeload
12261          * Fires before a network request is made to retrieve a data object.
12262          * @param {Object} This DataProxy object.
12263          * @param {Object} params The params parameter to the load function.
12264          */
12265         beforeload : true,
12266         /**
12267          * @event load
12268          * Fires before the load method's callback is called.
12269          * @param {Object} This DataProxy object.
12270          * @param {Object} o The data object.
12271          * @param {Object} arg The callback argument object passed to the load function.
12272          */
12273         load : true,
12274         /**
12275          * @event loadexception
12276          * Fires if an Exception occurs during data retrieval.
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          * @param {Object} e The Exception.
12281          */
12282         loadexception : true
12283     });
12284     Roo.data.DataProxy.superclass.constructor.call(this);
12285 };
12286
12287 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12288
12289     /**
12290      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12291      */
12292 /*
12293  * Based on:
12294  * Ext JS Library 1.1.1
12295  * Copyright(c) 2006-2007, Ext JS, LLC.
12296  *
12297  * Originally Released Under LGPL - original licence link has changed is not relivant.
12298  *
12299  * Fork - LGPL
12300  * <script type="text/javascript">
12301  */
12302 /**
12303  * @class Roo.data.MemoryProxy
12304  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12305  * to the Reader when its load method is called.
12306  * @constructor
12307  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12308  */
12309 Roo.data.MemoryProxy = function(data){
12310     if (data.data) {
12311         data = data.data;
12312     }
12313     Roo.data.MemoryProxy.superclass.constructor.call(this);
12314     this.data = data;
12315 };
12316
12317 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12318     
12319     /**
12320      * Load data from the requested source (in this case an in-memory
12321      * data object passed to the constructor), read the data object into
12322      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12323      * process that block using the passed callback.
12324      * @param {Object} params This parameter is not used by the MemoryProxy class.
12325      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12326      * object into a block of Roo.data.Records.
12327      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12328      * The function must be passed <ul>
12329      * <li>The Record block object</li>
12330      * <li>The "arg" argument from the load function</li>
12331      * <li>A boolean success indicator</li>
12332      * </ul>
12333      * @param {Object} scope The scope in which to call the callback
12334      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12335      */
12336     load : function(params, reader, callback, scope, arg){
12337         params = params || {};
12338         var result;
12339         try {
12340             result = reader.readRecords(this.data);
12341         }catch(e){
12342             this.fireEvent("loadexception", this, arg, null, e);
12343             callback.call(scope, null, arg, false);
12344             return;
12345         }
12346         callback.call(scope, result, arg, true);
12347     },
12348     
12349     // private
12350     update : function(params, records){
12351         
12352     }
12353 });/*
12354  * Based on:
12355  * Ext JS Library 1.1.1
12356  * Copyright(c) 2006-2007, Ext JS, LLC.
12357  *
12358  * Originally Released Under LGPL - original licence link has changed is not relivant.
12359  *
12360  * Fork - LGPL
12361  * <script type="text/javascript">
12362  */
12363 /**
12364  * @class Roo.data.HttpProxy
12365  * @extends Roo.data.DataProxy
12366  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12367  * configured to reference a certain URL.<br><br>
12368  * <p>
12369  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12370  * from which the running page was served.<br><br>
12371  * <p>
12372  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12373  * <p>
12374  * Be aware that to enable the browser to parse an XML document, the server must set
12375  * the Content-Type header in the HTTP response to "text/xml".
12376  * @constructor
12377  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12378  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12379  * will be used to make the request.
12380  */
12381 Roo.data.HttpProxy = function(conn){
12382     Roo.data.HttpProxy.superclass.constructor.call(this);
12383     // is conn a conn config or a real conn?
12384     this.conn = conn;
12385     this.useAjax = !conn || !conn.events;
12386   
12387 };
12388
12389 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12390     // thse are take from connection...
12391     
12392     /**
12393      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12394      */
12395     /**
12396      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12397      * extra parameters to each request made by this object. (defaults to undefined)
12398      */
12399     /**
12400      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12401      *  to each request made by this object. (defaults to undefined)
12402      */
12403     /**
12404      * @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)
12405      */
12406     /**
12407      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12408      */
12409      /**
12410      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12411      * @type Boolean
12412      */
12413   
12414
12415     /**
12416      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12417      * @type Boolean
12418      */
12419     /**
12420      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12421      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12422      * a finer-grained basis than the DataProxy events.
12423      */
12424     getConnection : function(){
12425         return this.useAjax ? Roo.Ajax : this.conn;
12426     },
12427
12428     /**
12429      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12430      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12431      * process that block using the passed callback.
12432      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12433      * for the request to the remote server.
12434      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12435      * object into a block of Roo.data.Records.
12436      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12437      * The function must be passed <ul>
12438      * <li>The Record block object</li>
12439      * <li>The "arg" argument from the load function</li>
12440      * <li>A boolean success indicator</li>
12441      * </ul>
12442      * @param {Object} scope The scope in which to call the callback
12443      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12444      */
12445     load : function(params, reader, callback, scope, arg){
12446         if(this.fireEvent("beforeload", this, params) !== false){
12447             var  o = {
12448                 params : params || {},
12449                 request: {
12450                     callback : callback,
12451                     scope : scope,
12452                     arg : arg
12453                 },
12454                 reader: reader,
12455                 callback : this.loadResponse,
12456                 scope: this
12457             };
12458             if(this.useAjax){
12459                 Roo.applyIf(o, this.conn);
12460                 if(this.activeRequest){
12461                     Roo.Ajax.abort(this.activeRequest);
12462                 }
12463                 this.activeRequest = Roo.Ajax.request(o);
12464             }else{
12465                 this.conn.request(o);
12466             }
12467         }else{
12468             callback.call(scope||this, null, arg, false);
12469         }
12470     },
12471
12472     // private
12473     loadResponse : function(o, success, response){
12474         delete this.activeRequest;
12475         if(!success){
12476             this.fireEvent("loadexception", this, o, response);
12477             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12478             return;
12479         }
12480         var result;
12481         try {
12482             result = o.reader.read(response);
12483         }catch(e){
12484             this.fireEvent("loadexception", this, o, response, e);
12485             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12486             return;
12487         }
12488         
12489         this.fireEvent("load", this, o, o.request.arg);
12490         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12491     },
12492
12493     // private
12494     update : function(dataSet){
12495
12496     },
12497
12498     // private
12499     updateResponse : function(dataSet){
12500
12501     }
12502 });/*
12503  * Based on:
12504  * Ext JS Library 1.1.1
12505  * Copyright(c) 2006-2007, Ext JS, LLC.
12506  *
12507  * Originally Released Under LGPL - original licence link has changed is not relivant.
12508  *
12509  * Fork - LGPL
12510  * <script type="text/javascript">
12511  */
12512
12513 /**
12514  * @class Roo.data.ScriptTagProxy
12515  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12516  * other than the originating domain of the running page.<br><br>
12517  * <p>
12518  * <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
12519  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12520  * <p>
12521  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12522  * source code that is used as the source inside a &lt;script> tag.<br><br>
12523  * <p>
12524  * In order for the browser to process the returned data, the server must wrap the data object
12525  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12526  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12527  * depending on whether the callback name was passed:
12528  * <p>
12529  * <pre><code>
12530 boolean scriptTag = false;
12531 String cb = request.getParameter("callback");
12532 if (cb != null) {
12533     scriptTag = true;
12534     response.setContentType("text/javascript");
12535 } else {
12536     response.setContentType("application/x-json");
12537 }
12538 Writer out = response.getWriter();
12539 if (scriptTag) {
12540     out.write(cb + "(");
12541 }
12542 out.print(dataBlock.toJsonString());
12543 if (scriptTag) {
12544     out.write(");");
12545 }
12546 </pre></code>
12547  *
12548  * @constructor
12549  * @param {Object} config A configuration object.
12550  */
12551 Roo.data.ScriptTagProxy = function(config){
12552     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12553     Roo.apply(this, config);
12554     this.head = document.getElementsByTagName("head")[0];
12555 };
12556
12557 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12558
12559 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12560     /**
12561      * @cfg {String} url The URL from which to request the data object.
12562      */
12563     /**
12564      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12565      */
12566     timeout : 30000,
12567     /**
12568      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12569      * the server the name of the callback function set up by the load call to process the returned data object.
12570      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12571      * javascript output which calls this named function passing the data object as its only parameter.
12572      */
12573     callbackParam : "callback",
12574     /**
12575      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12576      * name to the request.
12577      */
12578     nocache : true,
12579
12580     /**
12581      * Load data from the configured URL, read the data object into
12582      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12583      * process that block using the passed callback.
12584      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12585      * for the request to the remote server.
12586      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12587      * object into a block of Roo.data.Records.
12588      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12589      * The function must be passed <ul>
12590      * <li>The Record block object</li>
12591      * <li>The "arg" argument from the load function</li>
12592      * <li>A boolean success indicator</li>
12593      * </ul>
12594      * @param {Object} scope The scope in which to call the callback
12595      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12596      */
12597     load : function(params, reader, callback, scope, arg){
12598         if(this.fireEvent("beforeload", this, params) !== false){
12599
12600             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12601
12602             var url = this.url;
12603             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12604             if(this.nocache){
12605                 url += "&_dc=" + (new Date().getTime());
12606             }
12607             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12608             var trans = {
12609                 id : transId,
12610                 cb : "stcCallback"+transId,
12611                 scriptId : "stcScript"+transId,
12612                 params : params,
12613                 arg : arg,
12614                 url : url,
12615                 callback : callback,
12616                 scope : scope,
12617                 reader : reader
12618             };
12619             var conn = this;
12620
12621             window[trans.cb] = function(o){
12622                 conn.handleResponse(o, trans);
12623             };
12624
12625             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12626
12627             if(this.autoAbort !== false){
12628                 this.abort();
12629             }
12630
12631             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12632
12633             var script = document.createElement("script");
12634             script.setAttribute("src", url);
12635             script.setAttribute("type", "text/javascript");
12636             script.setAttribute("id", trans.scriptId);
12637             this.head.appendChild(script);
12638
12639             this.trans = trans;
12640         }else{
12641             callback.call(scope||this, null, arg, false);
12642         }
12643     },
12644
12645     // private
12646     isLoading : function(){
12647         return this.trans ? true : false;
12648     },
12649
12650     /**
12651      * Abort the current server request.
12652      */
12653     abort : function(){
12654         if(this.isLoading()){
12655             this.destroyTrans(this.trans);
12656         }
12657     },
12658
12659     // private
12660     destroyTrans : function(trans, isLoaded){
12661         this.head.removeChild(document.getElementById(trans.scriptId));
12662         clearTimeout(trans.timeoutId);
12663         if(isLoaded){
12664             window[trans.cb] = undefined;
12665             try{
12666                 delete window[trans.cb];
12667             }catch(e){}
12668         }else{
12669             // if hasn't been loaded, wait for load to remove it to prevent script error
12670             window[trans.cb] = function(){
12671                 window[trans.cb] = undefined;
12672                 try{
12673                     delete window[trans.cb];
12674                 }catch(e){}
12675             };
12676         }
12677     },
12678
12679     // private
12680     handleResponse : function(o, trans){
12681         this.trans = false;
12682         this.destroyTrans(trans, true);
12683         var result;
12684         try {
12685             result = trans.reader.readRecords(o);
12686         }catch(e){
12687             this.fireEvent("loadexception", this, o, trans.arg, e);
12688             trans.callback.call(trans.scope||window, null, trans.arg, false);
12689             return;
12690         }
12691         this.fireEvent("load", this, o, trans.arg);
12692         trans.callback.call(trans.scope||window, result, trans.arg, true);
12693     },
12694
12695     // private
12696     handleFailure : function(trans){
12697         this.trans = false;
12698         this.destroyTrans(trans, false);
12699         this.fireEvent("loadexception", this, null, trans.arg);
12700         trans.callback.call(trans.scope||window, null, trans.arg, false);
12701     }
12702 });/*
12703  * Based on:
12704  * Ext JS Library 1.1.1
12705  * Copyright(c) 2006-2007, Ext JS, LLC.
12706  *
12707  * Originally Released Under LGPL - original licence link has changed is not relivant.
12708  *
12709  * Fork - LGPL
12710  * <script type="text/javascript">
12711  */
12712
12713 /**
12714  * @class Roo.data.JsonReader
12715  * @extends Roo.data.DataReader
12716  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12717  * based on mappings in a provided Roo.data.Record constructor.
12718  * 
12719  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12720  * in the reply previously. 
12721  * 
12722  * <p>
12723  * Example code:
12724  * <pre><code>
12725 var RecordDef = Roo.data.Record.create([
12726     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12727     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12728 ]);
12729 var myReader = new Roo.data.JsonReader({
12730     totalProperty: "results",    // The property which contains the total dataset size (optional)
12731     root: "rows",                // The property which contains an Array of row objects
12732     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12733 }, RecordDef);
12734 </code></pre>
12735  * <p>
12736  * This would consume a JSON file like this:
12737  * <pre><code>
12738 { 'results': 2, 'rows': [
12739     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12740     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12741 }
12742 </code></pre>
12743  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12744  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12745  * paged from the remote server.
12746  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12747  * @cfg {String} root name of the property which contains the Array of row objects.
12748  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12749  * @cfg {Array} fields Array of field definition objects
12750  * @constructor
12751  * Create a new JsonReader
12752  * @param {Object} meta Metadata configuration options
12753  * @param {Object} recordType Either an Array of field definition objects,
12754  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12755  */
12756 Roo.data.JsonReader = function(meta, recordType){
12757     
12758     meta = meta || {};
12759     // set some defaults:
12760     Roo.applyIf(meta, {
12761         totalProperty: 'total',
12762         successProperty : 'success',
12763         root : 'data',
12764         id : 'id'
12765     });
12766     
12767     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12768 };
12769 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12770     
12771     /**
12772      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12773      * Used by Store query builder to append _requestMeta to params.
12774      * 
12775      */
12776     metaFromRemote : false,
12777     /**
12778      * This method is only used by a DataProxy which has retrieved data from a remote server.
12779      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12780      * @return {Object} data A data block which is used by an Roo.data.Store object as
12781      * a cache of Roo.data.Records.
12782      */
12783     read : function(response){
12784         var json = response.responseText;
12785        
12786         var o = /* eval:var:o */ eval("("+json+")");
12787         if(!o) {
12788             throw {message: "JsonReader.read: Json object not found"};
12789         }
12790         
12791         if(o.metaData){
12792             
12793             delete this.ef;
12794             this.metaFromRemote = true;
12795             this.meta = o.metaData;
12796             this.recordType = Roo.data.Record.create(o.metaData.fields);
12797             this.onMetaChange(this.meta, this.recordType, o);
12798         }
12799         return this.readRecords(o);
12800     },
12801
12802     // private function a store will implement
12803     onMetaChange : function(meta, recordType, o){
12804
12805     },
12806
12807     /**
12808          * @ignore
12809          */
12810     simpleAccess: function(obj, subsc) {
12811         return obj[subsc];
12812     },
12813
12814         /**
12815          * @ignore
12816          */
12817     getJsonAccessor: function(){
12818         var re = /[\[\.]/;
12819         return function(expr) {
12820             try {
12821                 return(re.test(expr))
12822                     ? new Function("obj", "return obj." + expr)
12823                     : function(obj){
12824                         return obj[expr];
12825                     };
12826             } catch(e){}
12827             return Roo.emptyFn;
12828         };
12829     }(),
12830
12831     /**
12832      * Create a data block containing Roo.data.Records from an XML document.
12833      * @param {Object} o An object which contains an Array of row objects in the property specified
12834      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12835      * which contains the total size of the dataset.
12836      * @return {Object} data A data block which is used by an Roo.data.Store object as
12837      * a cache of Roo.data.Records.
12838      */
12839     readRecords : function(o){
12840         /**
12841          * After any data loads, the raw JSON data is available for further custom processing.
12842          * @type Object
12843          */
12844         this.o = o;
12845         var s = this.meta, Record = this.recordType,
12846             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12847
12848 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12849         if (!this.ef) {
12850             if(s.totalProperty) {
12851                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12852                 }
12853                 if(s.successProperty) {
12854                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12855                 }
12856                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12857                 if (s.id) {
12858                         var g = this.getJsonAccessor(s.id);
12859                         this.getId = function(rec) {
12860                                 var r = g(rec);  
12861                                 return (r === undefined || r === "") ? null : r;
12862                         };
12863                 } else {
12864                         this.getId = function(){return null;};
12865                 }
12866             this.ef = [];
12867             for(var jj = 0; jj < fl; jj++){
12868                 f = fi[jj];
12869                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12870                 this.ef[jj] = this.getJsonAccessor(map);
12871             }
12872         }
12873
12874         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12875         if(s.totalProperty){
12876             var vt = parseInt(this.getTotal(o), 10);
12877             if(!isNaN(vt)){
12878                 totalRecords = vt;
12879             }
12880         }
12881         if(s.successProperty){
12882             var vs = this.getSuccess(o);
12883             if(vs === false || vs === 'false'){
12884                 success = false;
12885             }
12886         }
12887         var records = [];
12888         for(var i = 0; i < c; i++){
12889                 var n = root[i];
12890             var values = {};
12891             var id = this.getId(n);
12892             for(var j = 0; j < fl; j++){
12893                 f = fi[j];
12894             var v = this.ef[j](n);
12895             if (!f.convert) {
12896                 Roo.log('missing convert for ' + f.name);
12897                 Roo.log(f);
12898                 continue;
12899             }
12900             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12901             }
12902             var record = new Record(values, id);
12903             record.json = n;
12904             records[i] = record;
12905         }
12906         return {
12907             raw : o,
12908             success : success,
12909             records : records,
12910             totalRecords : totalRecords
12911         };
12912     }
12913 });/*
12914  * Based on:
12915  * Ext JS Library 1.1.1
12916  * Copyright(c) 2006-2007, Ext JS, LLC.
12917  *
12918  * Originally Released Under LGPL - original licence link has changed is not relivant.
12919  *
12920  * Fork - LGPL
12921  * <script type="text/javascript">
12922  */
12923
12924 /**
12925  * @class Roo.data.ArrayReader
12926  * @extends Roo.data.DataReader
12927  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12928  * Each element of that Array represents a row of data fields. The
12929  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12930  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12931  * <p>
12932  * Example code:.
12933  * <pre><code>
12934 var RecordDef = Roo.data.Record.create([
12935     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12936     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12937 ]);
12938 var myReader = new Roo.data.ArrayReader({
12939     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12940 }, RecordDef);
12941 </code></pre>
12942  * <p>
12943  * This would consume an Array like this:
12944  * <pre><code>
12945 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12946   </code></pre>
12947  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12948  * @constructor
12949  * Create a new JsonReader
12950  * @param {Object} meta Metadata configuration options.
12951  * @param {Object} recordType Either an Array of field definition objects
12952  * as specified to {@link Roo.data.Record#create},
12953  * or an {@link Roo.data.Record} object
12954  * created using {@link Roo.data.Record#create}.
12955  */
12956 Roo.data.ArrayReader = function(meta, recordType){
12957     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12958 };
12959
12960 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12961     /**
12962      * Create a data block containing Roo.data.Records from an XML document.
12963      * @param {Object} o An Array of row objects which represents the dataset.
12964      * @return {Object} data A data block which is used by an Roo.data.Store object as
12965      * a cache of Roo.data.Records.
12966      */
12967     readRecords : function(o){
12968         var sid = this.meta ? this.meta.id : null;
12969         var recordType = this.recordType, fields = recordType.prototype.fields;
12970         var records = [];
12971         var root = o;
12972             for(var i = 0; i < root.length; i++){
12973                     var n = root[i];
12974                 var values = {};
12975                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12976                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12977                 var f = fields.items[j];
12978                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12979                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12980                 v = f.convert(v);
12981                 values[f.name] = v;
12982             }
12983                 var record = new recordType(values, id);
12984                 record.json = n;
12985                 records[records.length] = record;
12986             }
12987             return {
12988                 records : records,
12989                 totalRecords : records.length
12990             };
12991     }
12992 });/*
12993  * - LGPL
12994  * * 
12995  */
12996
12997 /**
12998  * @class Roo.bootstrap.ComboBox
12999  * @extends Roo.bootstrap.TriggerField
13000  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13001  * @cfg {Boolean} append (true|false) default false
13002  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13003  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13004  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13005  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13006  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13007  * @cfg {Boolean} animate default true
13008  * @cfg {Boolean} emptyResultText only for touch device
13009  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13010  * @cfg {String} emptyTitle default ''
13011  * @constructor
13012  * Create a new ComboBox.
13013  * @param {Object} config Configuration options
13014  */
13015 Roo.bootstrap.ComboBox = function(config){
13016     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13017     this.addEvents({
13018         /**
13019          * @event expand
13020          * Fires when the dropdown list is expanded
13021         * @param {Roo.bootstrap.ComboBox} combo This combo box
13022         */
13023         'expand' : true,
13024         /**
13025          * @event collapse
13026          * Fires when the dropdown list is collapsed
13027         * @param {Roo.bootstrap.ComboBox} combo This combo box
13028         */
13029         'collapse' : true,
13030         /**
13031          * @event beforeselect
13032          * Fires before a list item is selected. Return false to cancel the selection.
13033         * @param {Roo.bootstrap.ComboBox} combo This combo box
13034         * @param {Roo.data.Record} record The data record returned from the underlying store
13035         * @param {Number} index The index of the selected item in the dropdown list
13036         */
13037         'beforeselect' : true,
13038         /**
13039          * @event select
13040          * Fires when a list item is selected
13041         * @param {Roo.bootstrap.ComboBox} combo This combo box
13042         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13043         * @param {Number} index The index of the selected item in the dropdown list
13044         */
13045         'select' : true,
13046         /**
13047          * @event beforequery
13048          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13049          * The event object passed has these properties:
13050         * @param {Roo.bootstrap.ComboBox} combo This combo box
13051         * @param {String} query The query
13052         * @param {Boolean} forceAll true to force "all" query
13053         * @param {Boolean} cancel true to cancel the query
13054         * @param {Object} e The query event object
13055         */
13056         'beforequery': true,
13057          /**
13058          * @event add
13059          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13060         * @param {Roo.bootstrap.ComboBox} combo This combo box
13061         */
13062         'add' : true,
13063         /**
13064          * @event edit
13065          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13066         * @param {Roo.bootstrap.ComboBox} combo This combo box
13067         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13068         */
13069         'edit' : true,
13070         /**
13071          * @event remove
13072          * Fires when the remove value from the combobox array
13073         * @param {Roo.bootstrap.ComboBox} combo This combo box
13074         */
13075         'remove' : true,
13076         /**
13077          * @event afterremove
13078          * Fires when the remove value from the combobox array
13079         * @param {Roo.bootstrap.ComboBox} combo This combo box
13080         */
13081         'afterremove' : true,
13082         /**
13083          * @event specialfilter
13084          * Fires when specialfilter
13085             * @param {Roo.bootstrap.ComboBox} combo This combo box
13086             */
13087         'specialfilter' : true,
13088         /**
13089          * @event tick
13090          * Fires when tick the element
13091             * @param {Roo.bootstrap.ComboBox} combo This combo box
13092             */
13093         'tick' : true,
13094         /**
13095          * @event touchviewdisplay
13096          * Fires when touch view require special display (default is using displayField)
13097             * @param {Roo.bootstrap.ComboBox} combo This combo box
13098             * @param {Object} cfg set html .
13099             */
13100         'touchviewdisplay' : true
13101         
13102     });
13103     
13104     this.item = [];
13105     this.tickItems = [];
13106     
13107     this.selectedIndex = -1;
13108     if(this.mode == 'local'){
13109         if(config.queryDelay === undefined){
13110             this.queryDelay = 10;
13111         }
13112         if(config.minChars === undefined){
13113             this.minChars = 0;
13114         }
13115     }
13116 };
13117
13118 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13119      
13120     /**
13121      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13122      * rendering into an Roo.Editor, defaults to false)
13123      */
13124     /**
13125      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13126      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13127      */
13128     /**
13129      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13130      */
13131     /**
13132      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13133      * the dropdown list (defaults to undefined, with no header element)
13134      */
13135
13136      /**
13137      * @cfg {String/Roo.Template} tpl The template to use to render the output
13138      */
13139      
13140      /**
13141      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13142      */
13143     listWidth: undefined,
13144     /**
13145      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13146      * mode = 'remote' or 'text' if mode = 'local')
13147      */
13148     displayField: undefined,
13149     
13150     /**
13151      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13152      * mode = 'remote' or 'value' if mode = 'local'). 
13153      * Note: use of a valueField requires the user make a selection
13154      * in order for a value to be mapped.
13155      */
13156     valueField: undefined,
13157     /**
13158      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13159      */
13160     modalTitle : '',
13161     
13162     /**
13163      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13164      * field's data value (defaults to the underlying DOM element's name)
13165      */
13166     hiddenName: undefined,
13167     /**
13168      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13169      */
13170     listClass: '',
13171     /**
13172      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13173      */
13174     selectedClass: 'active',
13175     
13176     /**
13177      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13178      */
13179     shadow:'sides',
13180     /**
13181      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13182      * anchor positions (defaults to 'tl-bl')
13183      */
13184     listAlign: 'tl-bl?',
13185     /**
13186      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13187      */
13188     maxHeight: 300,
13189     /**
13190      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13191      * query specified by the allQuery config option (defaults to 'query')
13192      */
13193     triggerAction: 'query',
13194     /**
13195      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13196      * (defaults to 4, does not apply if editable = false)
13197      */
13198     minChars : 4,
13199     /**
13200      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13201      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13202      */
13203     typeAhead: false,
13204     /**
13205      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13206      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13207      */
13208     queryDelay: 500,
13209     /**
13210      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13211      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13212      */
13213     pageSize: 0,
13214     /**
13215      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13216      * when editable = true (defaults to false)
13217      */
13218     selectOnFocus:false,
13219     /**
13220      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13221      */
13222     queryParam: 'query',
13223     /**
13224      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13225      * when mode = 'remote' (defaults to 'Loading...')
13226      */
13227     loadingText: 'Loading...',
13228     /**
13229      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13230      */
13231     resizable: false,
13232     /**
13233      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13234      */
13235     handleHeight : 8,
13236     /**
13237      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13238      * traditional select (defaults to true)
13239      */
13240     editable: true,
13241     /**
13242      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13243      */
13244     allQuery: '',
13245     /**
13246      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13247      */
13248     mode: 'remote',
13249     /**
13250      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13251      * listWidth has a higher value)
13252      */
13253     minListWidth : 70,
13254     /**
13255      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13256      * allow the user to set arbitrary text into the field (defaults to false)
13257      */
13258     forceSelection:false,
13259     /**
13260      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13261      * if typeAhead = true (defaults to 250)
13262      */
13263     typeAheadDelay : 250,
13264     /**
13265      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13266      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13267      */
13268     valueNotFoundText : undefined,
13269     /**
13270      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13271      */
13272     blockFocus : false,
13273     
13274     /**
13275      * @cfg {Boolean} disableClear Disable showing of clear button.
13276      */
13277     disableClear : false,
13278     /**
13279      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13280      */
13281     alwaysQuery : false,
13282     
13283     /**
13284      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13285      */
13286     multiple : false,
13287     
13288     /**
13289      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13290      */
13291     invalidClass : "has-warning",
13292     
13293     /**
13294      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13295      */
13296     validClass : "has-success",
13297     
13298     /**
13299      * @cfg {Boolean} specialFilter (true|false) special filter default false
13300      */
13301     specialFilter : false,
13302     
13303     /**
13304      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13305      */
13306     mobileTouchView : true,
13307     
13308     /**
13309      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13310      */
13311     useNativeIOS : false,
13312     
13313     /**
13314      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13315      */
13316     mobile_restrict_height : false,
13317     
13318     ios_options : false,
13319     
13320     //private
13321     addicon : false,
13322     editicon: false,
13323     
13324     page: 0,
13325     hasQuery: false,
13326     append: false,
13327     loadNext: false,
13328     autoFocus : true,
13329     tickable : false,
13330     btnPosition : 'right',
13331     triggerList : true,
13332     showToggleBtn : true,
13333     animate : true,
13334     emptyResultText: 'Empty',
13335     triggerText : 'Select',
13336     emptyTitle : '',
13337     
13338     // element that contains real text value.. (when hidden is used..)
13339     
13340     getAutoCreate : function()
13341     {   
13342         var cfg = false;
13343         //render
13344         /*
13345          * Render classic select for iso
13346          */
13347         
13348         if(Roo.isIOS && this.useNativeIOS){
13349             cfg = this.getAutoCreateNativeIOS();
13350             return cfg;
13351         }
13352         
13353         /*
13354          * Touch Devices
13355          */
13356         
13357         if(Roo.isTouch && this.mobileTouchView){
13358             cfg = this.getAutoCreateTouchView();
13359             return cfg;;
13360         }
13361         
13362         /*
13363          *  Normal ComboBox
13364          */
13365         if(!this.tickable){
13366             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13367             return cfg;
13368         }
13369         
13370         /*
13371          *  ComboBox with tickable selections
13372          */
13373              
13374         var align = this.labelAlign || this.parentLabelAlign();
13375         
13376         cfg = {
13377             cls : 'form-group roo-combobox-tickable' //input-group
13378         };
13379         
13380         var btn_text_select = '';
13381         var btn_text_done = '';
13382         var btn_text_cancel = '';
13383         
13384         if (this.btn_text_show) {
13385             btn_text_select = 'Select';
13386             btn_text_done = 'Done';
13387             btn_text_cancel = 'Cancel'; 
13388         }
13389         
13390         var buttons = {
13391             tag : 'div',
13392             cls : 'tickable-buttons',
13393             cn : [
13394                 {
13395                     tag : 'button',
13396                     type : 'button',
13397                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13398                     //html : this.triggerText
13399                     html: btn_text_select
13400                 },
13401                 {
13402                     tag : 'button',
13403                     type : 'button',
13404                     name : 'ok',
13405                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13406                     //html : 'Done'
13407                     html: btn_text_done
13408                 },
13409                 {
13410                     tag : 'button',
13411                     type : 'button',
13412                     name : 'cancel',
13413                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13414                     //html : 'Cancel'
13415                     html: btn_text_cancel
13416                 }
13417             ]
13418         };
13419         
13420         if(this.editable){
13421             buttons.cn.unshift({
13422                 tag: 'input',
13423                 cls: 'roo-select2-search-field-input'
13424             });
13425         }
13426         
13427         var _this = this;
13428         
13429         Roo.each(buttons.cn, function(c){
13430             if (_this.size) {
13431                 c.cls += ' btn-' + _this.size;
13432             }
13433
13434             if (_this.disabled) {
13435                 c.disabled = true;
13436             }
13437         });
13438         
13439         var box = {
13440             tag: 'div',
13441             style : 'display: contents',
13442             cn: [
13443                 {
13444                     tag: 'input',
13445                     type : 'hidden',
13446                     cls: 'form-hidden-field'
13447                 },
13448                 {
13449                     tag: 'ul',
13450                     cls: 'roo-select2-choices',
13451                     cn:[
13452                         {
13453                             tag: 'li',
13454                             cls: 'roo-select2-search-field',
13455                             cn: [
13456                                 buttons
13457                             ]
13458                         }
13459                     ]
13460                 }
13461             ]
13462         };
13463         
13464         var combobox = {
13465             cls: 'roo-select2-container input-group roo-select2-container-multi',
13466             cn: [
13467                 
13468                 box
13469 //                {
13470 //                    tag: 'ul',
13471 //                    cls: 'typeahead typeahead-long dropdown-menu',
13472 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13473 //                }
13474             ]
13475         };
13476         
13477         if(this.hasFeedback && !this.allowBlank){
13478             
13479             var feedback = {
13480                 tag: 'span',
13481                 cls: 'glyphicon form-control-feedback'
13482             };
13483
13484             combobox.cn.push(feedback);
13485         }
13486         
13487         var indicator = {
13488             tag : 'i',
13489             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13490             tooltip : 'This field is required'
13491         };
13492         if (Roo.bootstrap.version == 4) {
13493             indicator = {
13494                 tag : 'i',
13495                 style : 'display:none'
13496             };
13497         }
13498         if (align ==='left' && this.fieldLabel.length) {
13499             
13500             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13501             
13502             cfg.cn = [
13503                 indicator,
13504                 {
13505                     tag: 'label',
13506                     'for' :  id,
13507                     cls : 'control-label col-form-label',
13508                     html : this.fieldLabel
13509
13510                 },
13511                 {
13512                     cls : "", 
13513                     cn: [
13514                         combobox
13515                     ]
13516                 }
13517
13518             ];
13519             
13520             var labelCfg = cfg.cn[1];
13521             var contentCfg = cfg.cn[2];
13522             
13523
13524             if(this.indicatorpos == 'right'){
13525                 
13526                 cfg.cn = [
13527                     {
13528                         tag: 'label',
13529                         'for' :  id,
13530                         cls : 'control-label col-form-label',
13531                         cn : [
13532                             {
13533                                 tag : 'span',
13534                                 html : this.fieldLabel
13535                             },
13536                             indicator
13537                         ]
13538                     },
13539                     {
13540                         cls : "",
13541                         cn: [
13542                             combobox
13543                         ]
13544                     }
13545
13546                 ];
13547                 
13548                 
13549                 
13550                 labelCfg = cfg.cn[0];
13551                 contentCfg = cfg.cn[1];
13552             
13553             }
13554             
13555             if(this.labelWidth > 12){
13556                 labelCfg.style = "width: " + this.labelWidth + 'px';
13557             }
13558             
13559             if(this.labelWidth < 13 && this.labelmd == 0){
13560                 this.labelmd = this.labelWidth;
13561             }
13562             
13563             if(this.labellg > 0){
13564                 labelCfg.cls += ' col-lg-' + this.labellg;
13565                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13566             }
13567             
13568             if(this.labelmd > 0){
13569                 labelCfg.cls += ' col-md-' + this.labelmd;
13570                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13571             }
13572             
13573             if(this.labelsm > 0){
13574                 labelCfg.cls += ' col-sm-' + this.labelsm;
13575                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13576             }
13577             
13578             if(this.labelxs > 0){
13579                 labelCfg.cls += ' col-xs-' + this.labelxs;
13580                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13581             }
13582                 
13583                 
13584         } else if ( this.fieldLabel.length) {
13585 //                Roo.log(" label");
13586                  cfg.cn = [
13587                    indicator,
13588                     {
13589                         tag: 'label',
13590                         //cls : 'input-group-addon',
13591                         html : this.fieldLabel
13592                     },
13593                     combobox
13594                 ];
13595                 
13596                 if(this.indicatorpos == 'right'){
13597                     cfg.cn = [
13598                         {
13599                             tag: 'label',
13600                             //cls : 'input-group-addon',
13601                             html : this.fieldLabel
13602                         },
13603                         indicator,
13604                         combobox
13605                     ];
13606                     
13607                 }
13608
13609         } else {
13610             
13611 //                Roo.log(" no label && no align");
13612                 cfg = combobox
13613                      
13614                 
13615         }
13616          
13617         var settings=this;
13618         ['xs','sm','md','lg'].map(function(size){
13619             if (settings[size]) {
13620                 cfg.cls += ' col-' + size + '-' + settings[size];
13621             }
13622         });
13623         
13624         return cfg;
13625         
13626     },
13627     
13628     _initEventsCalled : false,
13629     
13630     // private
13631     initEvents: function()
13632     {   
13633         if (this._initEventsCalled) { // as we call render... prevent looping...
13634             return;
13635         }
13636         this._initEventsCalled = true;
13637         
13638         if (!this.store) {
13639             throw "can not find store for combo";
13640         }
13641         
13642         this.indicator = this.indicatorEl();
13643         
13644         this.store = Roo.factory(this.store, Roo.data);
13645         this.store.parent = this;
13646         
13647         // if we are building from html. then this element is so complex, that we can not really
13648         // use the rendered HTML.
13649         // so we have to trash and replace the previous code.
13650         if (Roo.XComponent.build_from_html) {
13651             // remove this element....
13652             var e = this.el.dom, k=0;
13653             while (e ) { e = e.previousSibling;  ++k;}
13654
13655             this.el.remove();
13656             
13657             this.el=false;
13658             this.rendered = false;
13659             
13660             this.render(this.parent().getChildContainer(true), k);
13661         }
13662         
13663         if(Roo.isIOS && this.useNativeIOS){
13664             this.initIOSView();
13665             return;
13666         }
13667         
13668         /*
13669          * Touch Devices
13670          */
13671         
13672         if(Roo.isTouch && this.mobileTouchView){
13673             this.initTouchView();
13674             return;
13675         }
13676         
13677         if(this.tickable){
13678             this.initTickableEvents();
13679             return;
13680         }
13681         
13682         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13683         
13684         if(this.hiddenName){
13685             
13686             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13687             
13688             this.hiddenField.dom.value =
13689                 this.hiddenValue !== undefined ? this.hiddenValue :
13690                 this.value !== undefined ? this.value : '';
13691
13692             // prevent input submission
13693             this.el.dom.removeAttribute('name');
13694             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13695              
13696              
13697         }
13698         //if(Roo.isGecko){
13699         //    this.el.dom.setAttribute('autocomplete', 'off');
13700         //}
13701         
13702         var cls = 'x-combo-list';
13703         
13704         //this.list = new Roo.Layer({
13705         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13706         //});
13707         
13708         var _this = this;
13709         
13710         (function(){
13711             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13712             _this.list.setWidth(lw);
13713         }).defer(100);
13714         
13715         this.list.on('mouseover', this.onViewOver, this);
13716         this.list.on('mousemove', this.onViewMove, this);
13717         this.list.on('scroll', this.onViewScroll, this);
13718         
13719         /*
13720         this.list.swallowEvent('mousewheel');
13721         this.assetHeight = 0;
13722
13723         if(this.title){
13724             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13725             this.assetHeight += this.header.getHeight();
13726         }
13727
13728         this.innerList = this.list.createChild({cls:cls+'-inner'});
13729         this.innerList.on('mouseover', this.onViewOver, this);
13730         this.innerList.on('mousemove', this.onViewMove, this);
13731         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13732         
13733         if(this.allowBlank && !this.pageSize && !this.disableClear){
13734             this.footer = this.list.createChild({cls:cls+'-ft'});
13735             this.pageTb = new Roo.Toolbar(this.footer);
13736            
13737         }
13738         if(this.pageSize){
13739             this.footer = this.list.createChild({cls:cls+'-ft'});
13740             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13741                     {pageSize: this.pageSize});
13742             
13743         }
13744         
13745         if (this.pageTb && this.allowBlank && !this.disableClear) {
13746             var _this = this;
13747             this.pageTb.add(new Roo.Toolbar.Fill(), {
13748                 cls: 'x-btn-icon x-btn-clear',
13749                 text: '&#160;',
13750                 handler: function()
13751                 {
13752                     _this.collapse();
13753                     _this.clearValue();
13754                     _this.onSelect(false, -1);
13755                 }
13756             });
13757         }
13758         if (this.footer) {
13759             this.assetHeight += this.footer.getHeight();
13760         }
13761         */
13762             
13763         if(!this.tpl){
13764             this.tpl = Roo.bootstrap.version == 4 ?
13765                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13766                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13767         }
13768
13769         this.view = new Roo.View(this.list, this.tpl, {
13770             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13771         });
13772         //this.view.wrapEl.setDisplayed(false);
13773         this.view.on('click', this.onViewClick, this);
13774         
13775         
13776         this.store.on('beforeload', this.onBeforeLoad, this);
13777         this.store.on('load', this.onLoad, this);
13778         this.store.on('loadexception', this.onLoadException, this);
13779         /*
13780         if(this.resizable){
13781             this.resizer = new Roo.Resizable(this.list,  {
13782                pinned:true, handles:'se'
13783             });
13784             this.resizer.on('resize', function(r, w, h){
13785                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13786                 this.listWidth = w;
13787                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13788                 this.restrictHeight();
13789             }, this);
13790             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13791         }
13792         */
13793         if(!this.editable){
13794             this.editable = true;
13795             this.setEditable(false);
13796         }
13797         
13798         /*
13799         
13800         if (typeof(this.events.add.listeners) != 'undefined') {
13801             
13802             this.addicon = this.wrap.createChild(
13803                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13804        
13805             this.addicon.on('click', function(e) {
13806                 this.fireEvent('add', this);
13807             }, this);
13808         }
13809         if (typeof(this.events.edit.listeners) != 'undefined') {
13810             
13811             this.editicon = this.wrap.createChild(
13812                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13813             if (this.addicon) {
13814                 this.editicon.setStyle('margin-left', '40px');
13815             }
13816             this.editicon.on('click', function(e) {
13817                 
13818                 // we fire even  if inothing is selected..
13819                 this.fireEvent('edit', this, this.lastData );
13820                 
13821             }, this);
13822         }
13823         */
13824         
13825         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13826             "up" : function(e){
13827                 this.inKeyMode = true;
13828                 this.selectPrev();
13829             },
13830
13831             "down" : function(e){
13832                 if(!this.isExpanded()){
13833                     this.onTriggerClick();
13834                 }else{
13835                     this.inKeyMode = true;
13836                     this.selectNext();
13837                 }
13838             },
13839
13840             "enter" : function(e){
13841 //                this.onViewClick();
13842                 //return true;
13843                 this.collapse();
13844                 
13845                 if(this.fireEvent("specialkey", this, e)){
13846                     this.onViewClick(false);
13847                 }
13848                 
13849                 return true;
13850             },
13851
13852             "esc" : function(e){
13853                 this.collapse();
13854             },
13855
13856             "tab" : function(e){
13857                 this.collapse();
13858                 
13859                 if(this.fireEvent("specialkey", this, e)){
13860                     this.onViewClick(false);
13861                 }
13862                 
13863                 return true;
13864             },
13865
13866             scope : this,
13867
13868             doRelay : function(foo, bar, hname){
13869                 if(hname == 'down' || this.scope.isExpanded()){
13870                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13871                 }
13872                 return true;
13873             },
13874
13875             forceKeyDown: true
13876         });
13877         
13878         
13879         this.queryDelay = Math.max(this.queryDelay || 10,
13880                 this.mode == 'local' ? 10 : 250);
13881         
13882         
13883         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13884         
13885         if(this.typeAhead){
13886             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13887         }
13888         if(this.editable !== false){
13889             this.inputEl().on("keyup", this.onKeyUp, this);
13890         }
13891         if(this.forceSelection){
13892             this.inputEl().on('blur', this.doForce, this);
13893         }
13894         
13895         if(this.multiple){
13896             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13897             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13898         }
13899     },
13900     
13901     initTickableEvents: function()
13902     {   
13903         this.createList();
13904         
13905         if(this.hiddenName){
13906             
13907             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13908             
13909             this.hiddenField.dom.value =
13910                 this.hiddenValue !== undefined ? this.hiddenValue :
13911                 this.value !== undefined ? this.value : '';
13912
13913             // prevent input submission
13914             this.el.dom.removeAttribute('name');
13915             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13916              
13917              
13918         }
13919         
13920 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13921         
13922         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13923         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13924         if(this.triggerList){
13925             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13926         }
13927          
13928         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13929         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13930         
13931         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13932         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13933         
13934         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13935         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13936         
13937         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13938         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13939         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13940         
13941         this.okBtn.hide();
13942         this.cancelBtn.hide();
13943         
13944         var _this = this;
13945         
13946         (function(){
13947             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13948             _this.list.setWidth(lw);
13949         }).defer(100);
13950         
13951         this.list.on('mouseover', this.onViewOver, this);
13952         this.list.on('mousemove', this.onViewMove, this);
13953         
13954         this.list.on('scroll', this.onViewScroll, this);
13955         
13956         if(!this.tpl){
13957             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13958                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13959         }
13960
13961         this.view = new Roo.View(this.list, this.tpl, {
13962             singleSelect:true,
13963             tickable:true,
13964             parent:this,
13965             store: this.store,
13966             selectedClass: this.selectedClass
13967         });
13968         
13969         //this.view.wrapEl.setDisplayed(false);
13970         this.view.on('click', this.onViewClick, this);
13971         
13972         
13973         
13974         this.store.on('beforeload', this.onBeforeLoad, this);
13975         this.store.on('load', this.onLoad, this);
13976         this.store.on('loadexception', this.onLoadException, this);
13977         
13978         if(this.editable){
13979             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13980                 "up" : function(e){
13981                     this.inKeyMode = true;
13982                     this.selectPrev();
13983                 },
13984
13985                 "down" : function(e){
13986                     this.inKeyMode = true;
13987                     this.selectNext();
13988                 },
13989
13990                 "enter" : function(e){
13991                     if(this.fireEvent("specialkey", this, e)){
13992                         this.onViewClick(false);
13993                     }
13994                     
13995                     return true;
13996                 },
13997
13998                 "esc" : function(e){
13999                     this.onTickableFooterButtonClick(e, false, false);
14000                 },
14001
14002                 "tab" : function(e){
14003                     this.fireEvent("specialkey", this, e);
14004                     
14005                     this.onTickableFooterButtonClick(e, false, false);
14006                     
14007                     return true;
14008                 },
14009
14010                 scope : this,
14011
14012                 doRelay : function(e, fn, key){
14013                     if(this.scope.isExpanded()){
14014                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14015                     }
14016                     return true;
14017                 },
14018
14019                 forceKeyDown: true
14020             });
14021         }
14022         
14023         this.queryDelay = Math.max(this.queryDelay || 10,
14024                 this.mode == 'local' ? 10 : 250);
14025         
14026         
14027         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14028         
14029         if(this.typeAhead){
14030             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14031         }
14032         
14033         if(this.editable !== false){
14034             this.tickableInputEl().on("keyup", this.onKeyUp, this);
14035         }
14036         
14037         this.indicator = this.indicatorEl();
14038         
14039         if(this.indicator){
14040             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14041             this.indicator.hide();
14042         }
14043         
14044     },
14045
14046     onDestroy : function(){
14047         if(this.view){
14048             this.view.setStore(null);
14049             this.view.el.removeAllListeners();
14050             this.view.el.remove();
14051             this.view.purgeListeners();
14052         }
14053         if(this.list){
14054             this.list.dom.innerHTML  = '';
14055         }
14056         
14057         if(this.store){
14058             this.store.un('beforeload', this.onBeforeLoad, this);
14059             this.store.un('load', this.onLoad, this);
14060             this.store.un('loadexception', this.onLoadException, this);
14061         }
14062         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14063     },
14064
14065     // private
14066     fireKey : function(e){
14067         if(e.isNavKeyPress() && !this.list.isVisible()){
14068             this.fireEvent("specialkey", this, e);
14069         }
14070     },
14071
14072     // private
14073     onResize: function(w, h){
14074 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14075 //        
14076 //        if(typeof w != 'number'){
14077 //            // we do not handle it!?!?
14078 //            return;
14079 //        }
14080 //        var tw = this.trigger.getWidth();
14081 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14082 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14083 //        var x = w - tw;
14084 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14085 //            
14086 //        //this.trigger.setStyle('left', x+'px');
14087 //        
14088 //        if(this.list && this.listWidth === undefined){
14089 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14090 //            this.list.setWidth(lw);
14091 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14092 //        }
14093         
14094     
14095         
14096     },
14097
14098     /**
14099      * Allow or prevent the user from directly editing the field text.  If false is passed,
14100      * the user will only be able to select from the items defined in the dropdown list.  This method
14101      * is the runtime equivalent of setting the 'editable' config option at config time.
14102      * @param {Boolean} value True to allow the user to directly edit the field text
14103      */
14104     setEditable : function(value){
14105         if(value == this.editable){
14106             return;
14107         }
14108         this.editable = value;
14109         if(!value){
14110             this.inputEl().dom.setAttribute('readOnly', true);
14111             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14112             this.inputEl().addClass('x-combo-noedit');
14113         }else{
14114             this.inputEl().dom.setAttribute('readOnly', false);
14115             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14116             this.inputEl().removeClass('x-combo-noedit');
14117         }
14118     },
14119
14120     // private
14121     
14122     onBeforeLoad : function(combo,opts){
14123         if(!this.hasFocus){
14124             return;
14125         }
14126          if (!opts.add) {
14127             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14128          }
14129         this.restrictHeight();
14130         this.selectedIndex = -1;
14131     },
14132
14133     // private
14134     onLoad : function(){
14135         
14136         this.hasQuery = false;
14137         
14138         if(!this.hasFocus){
14139             return;
14140         }
14141         
14142         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14143             this.loading.hide();
14144         }
14145         
14146         if(this.store.getCount() > 0){
14147             
14148             this.expand();
14149             this.restrictHeight();
14150             if(this.lastQuery == this.allQuery){
14151                 if(this.editable && !this.tickable){
14152                     this.inputEl().dom.select();
14153                 }
14154                 
14155                 if(
14156                     !this.selectByValue(this.value, true) &&
14157                     this.autoFocus && 
14158                     (
14159                         !this.store.lastOptions ||
14160                         typeof(this.store.lastOptions.add) == 'undefined' || 
14161                         this.store.lastOptions.add != true
14162                     )
14163                 ){
14164                     this.select(0, true);
14165                 }
14166             }else{
14167                 if(this.autoFocus){
14168                     this.selectNext();
14169                 }
14170                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14171                     this.taTask.delay(this.typeAheadDelay);
14172                 }
14173             }
14174         }else{
14175             this.onEmptyResults();
14176         }
14177         
14178         //this.el.focus();
14179     },
14180     // private
14181     onLoadException : function()
14182     {
14183         this.hasQuery = false;
14184         
14185         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14186             this.loading.hide();
14187         }
14188         
14189         if(this.tickable && this.editable){
14190             return;
14191         }
14192         
14193         this.collapse();
14194         // only causes errors at present
14195         //Roo.log(this.store.reader.jsonData);
14196         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14197             // fixme
14198             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14199         //}
14200         
14201         
14202     },
14203     // private
14204     onTypeAhead : function(){
14205         if(this.store.getCount() > 0){
14206             var r = this.store.getAt(0);
14207             var newValue = r.data[this.displayField];
14208             var len = newValue.length;
14209             var selStart = this.getRawValue().length;
14210             
14211             if(selStart != len){
14212                 this.setRawValue(newValue);
14213                 this.selectText(selStart, newValue.length);
14214             }
14215         }
14216     },
14217
14218     // private
14219     onSelect : function(record, index){
14220         
14221         if(this.fireEvent('beforeselect', this, record, index) !== false){
14222         
14223             this.setFromData(index > -1 ? record.data : false);
14224             
14225             this.collapse();
14226             this.fireEvent('select', this, record, index);
14227         }
14228     },
14229
14230     /**
14231      * Returns the currently selected field value or empty string if no value is set.
14232      * @return {String} value The selected value
14233      */
14234     getValue : function()
14235     {
14236         if(Roo.isIOS && this.useNativeIOS){
14237             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14238         }
14239         
14240         if(this.multiple){
14241             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14242         }
14243         
14244         if(this.valueField){
14245             return typeof this.value != 'undefined' ? this.value : '';
14246         }else{
14247             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14248         }
14249     },
14250     
14251     getRawValue : function()
14252     {
14253         if(Roo.isIOS && this.useNativeIOS){
14254             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14255         }
14256         
14257         var v = this.inputEl().getValue();
14258         
14259         return v;
14260     },
14261
14262     /**
14263      * Clears any text/value currently set in the field
14264      */
14265     clearValue : function(){
14266         
14267         if(this.hiddenField){
14268             this.hiddenField.dom.value = '';
14269         }
14270         this.value = '';
14271         this.setRawValue('');
14272         this.lastSelectionText = '';
14273         this.lastData = false;
14274         
14275         var close = this.closeTriggerEl();
14276         
14277         if(close){
14278             close.hide();
14279         }
14280         
14281         this.validate();
14282         
14283     },
14284
14285     /**
14286      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14287      * will be displayed in the field.  If the value does not match the data value of an existing item,
14288      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14289      * Otherwise the field will be blank (although the value will still be set).
14290      * @param {String} value The value to match
14291      */
14292     setValue : function(v)
14293     {
14294         if(Roo.isIOS && this.useNativeIOS){
14295             this.setIOSValue(v);
14296             return;
14297         }
14298         
14299         if(this.multiple){
14300             this.syncValue();
14301             return;
14302         }
14303         
14304         var text = v;
14305         if(this.valueField){
14306             var r = this.findRecord(this.valueField, v);
14307             if(r){
14308                 text = r.data[this.displayField];
14309             }else if(this.valueNotFoundText !== undefined){
14310                 text = this.valueNotFoundText;
14311             }
14312         }
14313         this.lastSelectionText = text;
14314         if(this.hiddenField){
14315             this.hiddenField.dom.value = v;
14316         }
14317         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14318         this.value = v;
14319         
14320         var close = this.closeTriggerEl();
14321         
14322         if(close){
14323             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14324         }
14325         
14326         this.validate();
14327     },
14328     /**
14329      * @property {Object} the last set data for the element
14330      */
14331     
14332     lastData : false,
14333     /**
14334      * Sets the value of the field based on a object which is related to the record format for the store.
14335      * @param {Object} value the value to set as. or false on reset?
14336      */
14337     setFromData : function(o){
14338         
14339         if(this.multiple){
14340             this.addItem(o);
14341             return;
14342         }
14343             
14344         var dv = ''; // display value
14345         var vv = ''; // value value..
14346         this.lastData = o;
14347         if (this.displayField) {
14348             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14349         } else {
14350             // this is an error condition!!!
14351             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14352         }
14353         
14354         if(this.valueField){
14355             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14356         }
14357         
14358         var close = this.closeTriggerEl();
14359         
14360         if(close){
14361             if(dv.length || vv * 1 > 0){
14362                 close.show() ;
14363                 this.blockFocus=true;
14364             } else {
14365                 close.hide();
14366             }             
14367         }
14368         
14369         if(this.hiddenField){
14370             this.hiddenField.dom.value = vv;
14371             
14372             this.lastSelectionText = dv;
14373             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14374             this.value = vv;
14375             return;
14376         }
14377         // no hidden field.. - we store the value in 'value', but still display
14378         // display field!!!!
14379         this.lastSelectionText = dv;
14380         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14381         this.value = vv;
14382         
14383         
14384         
14385     },
14386     // private
14387     reset : function(){
14388         // overridden so that last data is reset..
14389         
14390         if(this.multiple){
14391             this.clearItem();
14392             return;
14393         }
14394         
14395         this.setValue(this.originalValue);
14396         //this.clearInvalid();
14397         this.lastData = false;
14398         if (this.view) {
14399             this.view.clearSelections();
14400         }
14401         
14402         this.validate();
14403     },
14404     // private
14405     findRecord : function(prop, value){
14406         var record;
14407         if(this.store.getCount() > 0){
14408             this.store.each(function(r){
14409                 if(r.data[prop] == value){
14410                     record = r;
14411                     return false;
14412                 }
14413                 return true;
14414             });
14415         }
14416         return record;
14417     },
14418     
14419     getName: function()
14420     {
14421         // returns hidden if it's set..
14422         if (!this.rendered) {return ''};
14423         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14424         
14425     },
14426     // private
14427     onViewMove : function(e, t){
14428         this.inKeyMode = false;
14429     },
14430
14431     // private
14432     onViewOver : function(e, t){
14433         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14434             return;
14435         }
14436         var item = this.view.findItemFromChild(t);
14437         
14438         if(item){
14439             var index = this.view.indexOf(item);
14440             this.select(index, false);
14441         }
14442     },
14443
14444     // private
14445     onViewClick : function(view, doFocus, el, e)
14446     {
14447         var index = this.view.getSelectedIndexes()[0];
14448         
14449         var r = this.store.getAt(index);
14450         
14451         if(this.tickable){
14452             
14453             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14454                 return;
14455             }
14456             
14457             var rm = false;
14458             var _this = this;
14459             
14460             Roo.each(this.tickItems, function(v,k){
14461                 
14462                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14463                     Roo.log(v);
14464                     _this.tickItems.splice(k, 1);
14465                     
14466                     if(typeof(e) == 'undefined' && view == false){
14467                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14468                     }
14469                     
14470                     rm = true;
14471                     return;
14472                 }
14473             });
14474             
14475             if(rm){
14476                 return;
14477             }
14478             
14479             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14480                 this.tickItems.push(r.data);
14481             }
14482             
14483             if(typeof(e) == 'undefined' && view == false){
14484                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14485             }
14486                     
14487             return;
14488         }
14489         
14490         if(r){
14491             this.onSelect(r, index);
14492         }
14493         if(doFocus !== false && !this.blockFocus){
14494             this.inputEl().focus();
14495         }
14496     },
14497
14498     // private
14499     restrictHeight : function(){
14500         //this.innerList.dom.style.height = '';
14501         //var inner = this.innerList.dom;
14502         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14503         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14504         //this.list.beginUpdate();
14505         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14506         this.list.alignTo(this.inputEl(), this.listAlign);
14507         this.list.alignTo(this.inputEl(), this.listAlign);
14508         //this.list.endUpdate();
14509     },
14510
14511     // private
14512     onEmptyResults : function(){
14513         
14514         if(this.tickable && this.editable){
14515             this.hasFocus = false;
14516             this.restrictHeight();
14517             return;
14518         }
14519         
14520         this.collapse();
14521     },
14522
14523     /**
14524      * Returns true if the dropdown list is expanded, else false.
14525      */
14526     isExpanded : function(){
14527         return this.list.isVisible();
14528     },
14529
14530     /**
14531      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14532      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14533      * @param {String} value The data value of the item to select
14534      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14535      * selected item if it is not currently in view (defaults to true)
14536      * @return {Boolean} True if the value matched an item in the list, else false
14537      */
14538     selectByValue : function(v, scrollIntoView){
14539         if(v !== undefined && v !== null){
14540             var r = this.findRecord(this.valueField || this.displayField, v);
14541             if(r){
14542                 this.select(this.store.indexOf(r), scrollIntoView);
14543                 return true;
14544             }
14545         }
14546         return false;
14547     },
14548
14549     /**
14550      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14551      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14552      * @param {Number} index The zero-based index of the list item to select
14553      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14554      * selected item if it is not currently in view (defaults to true)
14555      */
14556     select : function(index, scrollIntoView){
14557         this.selectedIndex = index;
14558         this.view.select(index);
14559         if(scrollIntoView !== false){
14560             var el = this.view.getNode(index);
14561             /*
14562              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14563              */
14564             if(el){
14565                 this.list.scrollChildIntoView(el, false);
14566             }
14567         }
14568     },
14569
14570     // private
14571     selectNext : function(){
14572         var ct = this.store.getCount();
14573         if(ct > 0){
14574             if(this.selectedIndex == -1){
14575                 this.select(0);
14576             }else if(this.selectedIndex < ct-1){
14577                 this.select(this.selectedIndex+1);
14578             }
14579         }
14580     },
14581
14582     // private
14583     selectPrev : function(){
14584         var ct = this.store.getCount();
14585         if(ct > 0){
14586             if(this.selectedIndex == -1){
14587                 this.select(0);
14588             }else if(this.selectedIndex != 0){
14589                 this.select(this.selectedIndex-1);
14590             }
14591         }
14592     },
14593
14594     // private
14595     onKeyUp : function(e){
14596         if(this.editable !== false && !e.isSpecialKey()){
14597             this.lastKey = e.getKey();
14598             this.dqTask.delay(this.queryDelay);
14599         }
14600     },
14601
14602     // private
14603     validateBlur : function(){
14604         return !this.list || !this.list.isVisible();   
14605     },
14606
14607     // private
14608     initQuery : function(){
14609         
14610         var v = this.getRawValue();
14611         
14612         if(this.tickable && this.editable){
14613             v = this.tickableInputEl().getValue();
14614         }
14615         
14616         this.doQuery(v);
14617     },
14618
14619     // private
14620     doForce : function(){
14621         if(this.inputEl().dom.value.length > 0){
14622             this.inputEl().dom.value =
14623                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14624              
14625         }
14626     },
14627
14628     /**
14629      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14630      * query allowing the query action to be canceled if needed.
14631      * @param {String} query The SQL query to execute
14632      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14633      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14634      * saved in the current store (defaults to false)
14635      */
14636     doQuery : function(q, forceAll){
14637         
14638         if(q === undefined || q === null){
14639             q = '';
14640         }
14641         var qe = {
14642             query: q,
14643             forceAll: forceAll,
14644             combo: this,
14645             cancel:false
14646         };
14647         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14648             return false;
14649         }
14650         q = qe.query;
14651         
14652         forceAll = qe.forceAll;
14653         if(forceAll === true || (q.length >= this.minChars)){
14654             
14655             this.hasQuery = true;
14656             
14657             if(this.lastQuery != q || this.alwaysQuery){
14658                 this.lastQuery = q;
14659                 if(this.mode == 'local'){
14660                     this.selectedIndex = -1;
14661                     if(forceAll){
14662                         this.store.clearFilter();
14663                     }else{
14664                         
14665                         if(this.specialFilter){
14666                             this.fireEvent('specialfilter', this);
14667                             this.onLoad();
14668                             return;
14669                         }
14670                         
14671                         this.store.filter(this.displayField, q);
14672                     }
14673                     
14674                     this.store.fireEvent("datachanged", this.store);
14675                     
14676                     this.onLoad();
14677                     
14678                     
14679                 }else{
14680                     
14681                     this.store.baseParams[this.queryParam] = q;
14682                     
14683                     var options = {params : this.getParams(q)};
14684                     
14685                     if(this.loadNext){
14686                         options.add = true;
14687                         options.params.start = this.page * this.pageSize;
14688                     }
14689                     
14690                     this.store.load(options);
14691                     
14692                     /*
14693                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14694                      *  we should expand the list on onLoad
14695                      *  so command out it
14696                      */
14697 //                    this.expand();
14698                 }
14699             }else{
14700                 this.selectedIndex = -1;
14701                 this.onLoad();   
14702             }
14703         }
14704         
14705         this.loadNext = false;
14706     },
14707     
14708     // private
14709     getParams : function(q){
14710         var p = {};
14711         //p[this.queryParam] = q;
14712         
14713         if(this.pageSize){
14714             p.start = 0;
14715             p.limit = this.pageSize;
14716         }
14717         return p;
14718     },
14719
14720     /**
14721      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14722      */
14723     collapse : function(){
14724         if(!this.isExpanded()){
14725             return;
14726         }
14727         
14728         this.list.hide();
14729         
14730         this.hasFocus = false;
14731         
14732         if(this.tickable){
14733             this.okBtn.hide();
14734             this.cancelBtn.hide();
14735             this.trigger.show();
14736             
14737             if(this.editable){
14738                 this.tickableInputEl().dom.value = '';
14739                 this.tickableInputEl().blur();
14740             }
14741             
14742         }
14743         
14744         Roo.get(document).un('mousedown', this.collapseIf, this);
14745         Roo.get(document).un('mousewheel', this.collapseIf, this);
14746         if (!this.editable) {
14747             Roo.get(document).un('keydown', this.listKeyPress, this);
14748         }
14749         this.fireEvent('collapse', this);
14750         
14751         this.validate();
14752     },
14753
14754     // private
14755     collapseIf : function(e){
14756         var in_combo  = e.within(this.el);
14757         var in_list =  e.within(this.list);
14758         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14759         
14760         if (in_combo || in_list || is_list) {
14761             //e.stopPropagation();
14762             return;
14763         }
14764         
14765         if(this.tickable){
14766             this.onTickableFooterButtonClick(e, false, false);
14767         }
14768
14769         this.collapse();
14770         
14771     },
14772
14773     /**
14774      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14775      */
14776     expand : function(){
14777        
14778         if(this.isExpanded() || !this.hasFocus){
14779             return;
14780         }
14781         
14782         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14783         this.list.setWidth(lw);
14784         
14785         Roo.log('expand');
14786         
14787         this.list.show();
14788         
14789         this.restrictHeight();
14790         
14791         if(this.tickable){
14792             
14793             this.tickItems = Roo.apply([], this.item);
14794             
14795             this.okBtn.show();
14796             this.cancelBtn.show();
14797             this.trigger.hide();
14798             
14799             if(this.editable){
14800                 this.tickableInputEl().focus();
14801             }
14802             
14803         }
14804         
14805         Roo.get(document).on('mousedown', this.collapseIf, this);
14806         Roo.get(document).on('mousewheel', this.collapseIf, this);
14807         if (!this.editable) {
14808             Roo.get(document).on('keydown', this.listKeyPress, this);
14809         }
14810         
14811         this.fireEvent('expand', this);
14812     },
14813
14814     // private
14815     // Implements the default empty TriggerField.onTriggerClick function
14816     onTriggerClick : function(e)
14817     {
14818         Roo.log('trigger click');
14819         
14820         if(this.disabled || !this.triggerList){
14821             return;
14822         }
14823         
14824         this.page = 0;
14825         this.loadNext = false;
14826         
14827         if(this.isExpanded()){
14828             this.collapse();
14829             if (!this.blockFocus) {
14830                 this.inputEl().focus();
14831             }
14832             
14833         }else {
14834             this.hasFocus = true;
14835             if(this.triggerAction == 'all') {
14836                 this.doQuery(this.allQuery, true);
14837             } else {
14838                 this.doQuery(this.getRawValue());
14839             }
14840             if (!this.blockFocus) {
14841                 this.inputEl().focus();
14842             }
14843         }
14844     },
14845     
14846     onTickableTriggerClick : function(e)
14847     {
14848         if(this.disabled){
14849             return;
14850         }
14851         
14852         this.page = 0;
14853         this.loadNext = false;
14854         this.hasFocus = true;
14855         
14856         if(this.triggerAction == 'all') {
14857             this.doQuery(this.allQuery, true);
14858         } else {
14859             this.doQuery(this.getRawValue());
14860         }
14861     },
14862     
14863     onSearchFieldClick : function(e)
14864     {
14865         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14866             this.onTickableFooterButtonClick(e, false, false);
14867             return;
14868         }
14869         
14870         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14871             return;
14872         }
14873         
14874         this.page = 0;
14875         this.loadNext = false;
14876         this.hasFocus = true;
14877         
14878         if(this.triggerAction == 'all') {
14879             this.doQuery(this.allQuery, true);
14880         } else {
14881             this.doQuery(this.getRawValue());
14882         }
14883     },
14884     
14885     listKeyPress : function(e)
14886     {
14887         //Roo.log('listkeypress');
14888         // scroll to first matching element based on key pres..
14889         if (e.isSpecialKey()) {
14890             return false;
14891         }
14892         var k = String.fromCharCode(e.getKey()).toUpperCase();
14893         //Roo.log(k);
14894         var match  = false;
14895         var csel = this.view.getSelectedNodes();
14896         var cselitem = false;
14897         if (csel.length) {
14898             var ix = this.view.indexOf(csel[0]);
14899             cselitem  = this.store.getAt(ix);
14900             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14901                 cselitem = false;
14902             }
14903             
14904         }
14905         
14906         this.store.each(function(v) { 
14907             if (cselitem) {
14908                 // start at existing selection.
14909                 if (cselitem.id == v.id) {
14910                     cselitem = false;
14911                 }
14912                 return true;
14913             }
14914                 
14915             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14916                 match = this.store.indexOf(v);
14917                 return false;
14918             }
14919             return true;
14920         }, this);
14921         
14922         if (match === false) {
14923             return true; // no more action?
14924         }
14925         // scroll to?
14926         this.view.select(match);
14927         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14928         sn.scrollIntoView(sn.dom.parentNode, false);
14929     },
14930     
14931     onViewScroll : function(e, t){
14932         
14933         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){
14934             return;
14935         }
14936         
14937         this.hasQuery = true;
14938         
14939         this.loading = this.list.select('.loading', true).first();
14940         
14941         if(this.loading === null){
14942             this.list.createChild({
14943                 tag: 'div',
14944                 cls: 'loading roo-select2-more-results roo-select2-active',
14945                 html: 'Loading more results...'
14946             });
14947             
14948             this.loading = this.list.select('.loading', true).first();
14949             
14950             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14951             
14952             this.loading.hide();
14953         }
14954         
14955         this.loading.show();
14956         
14957         var _combo = this;
14958         
14959         this.page++;
14960         this.loadNext = true;
14961         
14962         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14963         
14964         return;
14965     },
14966     
14967     addItem : function(o)
14968     {   
14969         var dv = ''; // display value
14970         
14971         if (this.displayField) {
14972             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14973         } else {
14974             // this is an error condition!!!
14975             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14976         }
14977         
14978         if(!dv.length){
14979             return;
14980         }
14981         
14982         var choice = this.choices.createChild({
14983             tag: 'li',
14984             cls: 'roo-select2-search-choice',
14985             cn: [
14986                 {
14987                     tag: 'div',
14988                     html: dv
14989                 },
14990                 {
14991                     tag: 'a',
14992                     href: '#',
14993                     cls: 'roo-select2-search-choice-close fa fa-times',
14994                     tabindex: '-1'
14995                 }
14996             ]
14997             
14998         }, this.searchField);
14999         
15000         var close = choice.select('a.roo-select2-search-choice-close', true).first();
15001         
15002         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15003         
15004         this.item.push(o);
15005         
15006         this.lastData = o;
15007         
15008         this.syncValue();
15009         
15010         this.inputEl().dom.value = '';
15011         
15012         this.validate();
15013     },
15014     
15015     onRemoveItem : function(e, _self, o)
15016     {
15017         e.preventDefault();
15018         
15019         this.lastItem = Roo.apply([], this.item);
15020         
15021         var index = this.item.indexOf(o.data) * 1;
15022         
15023         if( index < 0){
15024             Roo.log('not this item?!');
15025             return;
15026         }
15027         
15028         this.item.splice(index, 1);
15029         o.item.remove();
15030         
15031         this.syncValue();
15032         
15033         this.fireEvent('remove', this, e);
15034         
15035         this.validate();
15036         
15037     },
15038     
15039     syncValue : function()
15040     {
15041         if(!this.item.length){
15042             this.clearValue();
15043             return;
15044         }
15045             
15046         var value = [];
15047         var _this = this;
15048         Roo.each(this.item, function(i){
15049             if(_this.valueField){
15050                 value.push(i[_this.valueField]);
15051                 return;
15052             }
15053
15054             value.push(i);
15055         });
15056
15057         this.value = value.join(',');
15058
15059         if(this.hiddenField){
15060             this.hiddenField.dom.value = this.value;
15061         }
15062         
15063         this.store.fireEvent("datachanged", this.store);
15064         
15065         this.validate();
15066     },
15067     
15068     clearItem : function()
15069     {
15070         if(!this.multiple){
15071             return;
15072         }
15073         
15074         this.item = [];
15075         
15076         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15077            c.remove();
15078         });
15079         
15080         this.syncValue();
15081         
15082         this.validate();
15083         
15084         if(this.tickable && !Roo.isTouch){
15085             this.view.refresh();
15086         }
15087     },
15088     
15089     inputEl: function ()
15090     {
15091         if(Roo.isIOS && this.useNativeIOS){
15092             return this.el.select('select.roo-ios-select', true).first();
15093         }
15094         
15095         if(Roo.isTouch && this.mobileTouchView){
15096             return this.el.select('input.form-control',true).first();
15097         }
15098         
15099         if(this.tickable){
15100             return this.searchField;
15101         }
15102         
15103         return this.el.select('input.form-control',true).first();
15104     },
15105     
15106     onTickableFooterButtonClick : function(e, btn, el)
15107     {
15108         e.preventDefault();
15109         
15110         this.lastItem = Roo.apply([], this.item);
15111         
15112         if(btn && btn.name == 'cancel'){
15113             this.tickItems = Roo.apply([], this.item);
15114             this.collapse();
15115             return;
15116         }
15117         
15118         this.clearItem();
15119         
15120         var _this = this;
15121         
15122         Roo.each(this.tickItems, function(o){
15123             _this.addItem(o);
15124         });
15125         
15126         this.collapse();
15127         
15128     },
15129     
15130     validate : function()
15131     {
15132         if(this.getVisibilityEl().hasClass('hidden')){
15133             return true;
15134         }
15135         
15136         var v = this.getRawValue();
15137         
15138         if(this.multiple){
15139             v = this.getValue();
15140         }
15141         
15142         if(this.disabled || this.allowBlank || v.length){
15143             this.markValid();
15144             return true;
15145         }
15146         
15147         this.markInvalid();
15148         return false;
15149     },
15150     
15151     tickableInputEl : function()
15152     {
15153         if(!this.tickable || !this.editable){
15154             return this.inputEl();
15155         }
15156         
15157         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15158     },
15159     
15160     
15161     getAutoCreateTouchView : function()
15162     {
15163         var id = Roo.id();
15164         
15165         var cfg = {
15166             cls: 'form-group' //input-group
15167         };
15168         
15169         var input =  {
15170             tag: 'input',
15171             id : id,
15172             type : this.inputType,
15173             cls : 'form-control x-combo-noedit',
15174             autocomplete: 'new-password',
15175             placeholder : this.placeholder || '',
15176             readonly : true
15177         };
15178         
15179         if (this.name) {
15180             input.name = this.name;
15181         }
15182         
15183         if (this.size) {
15184             input.cls += ' input-' + this.size;
15185         }
15186         
15187         if (this.disabled) {
15188             input.disabled = true;
15189         }
15190         
15191         var inputblock = {
15192             cls : '',
15193             cn : [
15194                 input
15195             ]
15196         };
15197         
15198         if(this.before){
15199             inputblock.cls += ' input-group';
15200             
15201             inputblock.cn.unshift({
15202                 tag :'span',
15203                 cls : 'input-group-addon input-group-prepend input-group-text',
15204                 html : this.before
15205             });
15206         }
15207         
15208         if(this.removable && !this.multiple){
15209             inputblock.cls += ' roo-removable';
15210             
15211             inputblock.cn.push({
15212                 tag: 'button',
15213                 html : 'x',
15214                 cls : 'roo-combo-removable-btn close'
15215             });
15216         }
15217
15218         if(this.hasFeedback && !this.allowBlank){
15219             
15220             inputblock.cls += ' has-feedback';
15221             
15222             inputblock.cn.push({
15223                 tag: 'span',
15224                 cls: 'glyphicon form-control-feedback'
15225             });
15226             
15227         }
15228         
15229         if (this.after) {
15230             
15231             inputblock.cls += (this.before) ? '' : ' input-group';
15232             
15233             inputblock.cn.push({
15234                 tag :'span',
15235                 cls : 'input-group-addon input-group-append input-group-text',
15236                 html : this.after
15237             });
15238         }
15239
15240         
15241         var ibwrap = inputblock;
15242         
15243         if(this.multiple){
15244             ibwrap = {
15245                 tag: 'ul',
15246                 cls: 'roo-select2-choices',
15247                 cn:[
15248                     {
15249                         tag: 'li',
15250                         cls: 'roo-select2-search-field',
15251                         cn: [
15252
15253                             inputblock
15254                         ]
15255                     }
15256                 ]
15257             };
15258         
15259             
15260         }
15261         
15262         var combobox = {
15263             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15264             cn: [
15265                 {
15266                     tag: 'input',
15267                     type : 'hidden',
15268                     cls: 'form-hidden-field'
15269                 },
15270                 ibwrap
15271             ]
15272         };
15273         
15274         if(!this.multiple && this.showToggleBtn){
15275             
15276             var caret = {
15277                         tag: 'span',
15278                         cls: 'caret'
15279             };
15280             
15281             if (this.caret != false) {
15282                 caret = {
15283                      tag: 'i',
15284                      cls: 'fa fa-' + this.caret
15285                 };
15286                 
15287             }
15288             
15289             combobox.cn.push({
15290                 tag :'span',
15291                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15292                 cn : [
15293                     caret,
15294                     {
15295                         tag: 'span',
15296                         cls: 'combobox-clear',
15297                         cn  : [
15298                             {
15299                                 tag : 'i',
15300                                 cls: 'icon-remove'
15301                             }
15302                         ]
15303                     }
15304                 ]
15305
15306             })
15307         }
15308         
15309         if(this.multiple){
15310             combobox.cls += ' roo-select2-container-multi';
15311         }
15312         
15313         var align = this.labelAlign || this.parentLabelAlign();
15314         
15315         if (align ==='left' && this.fieldLabel.length) {
15316
15317             cfg.cn = [
15318                 {
15319                    tag : 'i',
15320                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15321                    tooltip : 'This field is required'
15322                 },
15323                 {
15324                     tag: 'label',
15325                     cls : 'control-label col-form-label',
15326                     html : this.fieldLabel
15327
15328                 },
15329                 {
15330                     cls : '', 
15331                     cn: [
15332                         combobox
15333                     ]
15334                 }
15335             ];
15336             
15337             var labelCfg = cfg.cn[1];
15338             var contentCfg = cfg.cn[2];
15339             
15340
15341             if(this.indicatorpos == 'right'){
15342                 cfg.cn = [
15343                     {
15344                         tag: 'label',
15345                         'for' :  id,
15346                         cls : 'control-label col-form-label',
15347                         cn : [
15348                             {
15349                                 tag : 'span',
15350                                 html : this.fieldLabel
15351                             },
15352                             {
15353                                 tag : 'i',
15354                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15355                                 tooltip : 'This field is required'
15356                             }
15357                         ]
15358                     },
15359                     {
15360                         cls : "",
15361                         cn: [
15362                             combobox
15363                         ]
15364                     }
15365
15366                 ];
15367                 
15368                 labelCfg = cfg.cn[0];
15369                 contentCfg = cfg.cn[1];
15370             }
15371             
15372            
15373             
15374             if(this.labelWidth > 12){
15375                 labelCfg.style = "width: " + this.labelWidth + 'px';
15376             }
15377             
15378             if(this.labelWidth < 13 && this.labelmd == 0){
15379                 this.labelmd = this.labelWidth;
15380             }
15381             
15382             if(this.labellg > 0){
15383                 labelCfg.cls += ' col-lg-' + this.labellg;
15384                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15385             }
15386             
15387             if(this.labelmd > 0){
15388                 labelCfg.cls += ' col-md-' + this.labelmd;
15389                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15390             }
15391             
15392             if(this.labelsm > 0){
15393                 labelCfg.cls += ' col-sm-' + this.labelsm;
15394                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15395             }
15396             
15397             if(this.labelxs > 0){
15398                 labelCfg.cls += ' col-xs-' + this.labelxs;
15399                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15400             }
15401                 
15402                 
15403         } else if ( this.fieldLabel.length) {
15404             cfg.cn = [
15405                 {
15406                    tag : 'i',
15407                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15408                    tooltip : 'This field is required'
15409                 },
15410                 {
15411                     tag: 'label',
15412                     cls : 'control-label',
15413                     html : this.fieldLabel
15414
15415                 },
15416                 {
15417                     cls : '', 
15418                     cn: [
15419                         combobox
15420                     ]
15421                 }
15422             ];
15423             
15424             if(this.indicatorpos == 'right'){
15425                 cfg.cn = [
15426                     {
15427                         tag: 'label',
15428                         cls : 'control-label',
15429                         html : this.fieldLabel,
15430                         cn : [
15431                             {
15432                                tag : 'i',
15433                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15434                                tooltip : 'This field is required'
15435                             }
15436                         ]
15437                     },
15438                     {
15439                         cls : '', 
15440                         cn: [
15441                             combobox
15442                         ]
15443                     }
15444                 ];
15445             }
15446         } else {
15447             cfg.cn = combobox;    
15448         }
15449         
15450         
15451         var settings = this;
15452         
15453         ['xs','sm','md','lg'].map(function(size){
15454             if (settings[size]) {
15455                 cfg.cls += ' col-' + size + '-' + settings[size];
15456             }
15457         });
15458         
15459         return cfg;
15460     },
15461     
15462     initTouchView : function()
15463     {
15464         this.renderTouchView();
15465         
15466         this.touchViewEl.on('scroll', function(){
15467             this.el.dom.scrollTop = 0;
15468         }, this);
15469         
15470         this.originalValue = this.getValue();
15471         
15472         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15473         
15474         this.inputEl().on("click", this.showTouchView, this);
15475         if (this.triggerEl) {
15476             this.triggerEl.on("click", this.showTouchView, this);
15477         }
15478         
15479         
15480         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15481         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15482         
15483         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15484         
15485         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15486         this.store.on('load', this.onTouchViewLoad, this);
15487         this.store.on('loadexception', this.onTouchViewLoadException, this);
15488         
15489         if(this.hiddenName){
15490             
15491             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15492             
15493             this.hiddenField.dom.value =
15494                 this.hiddenValue !== undefined ? this.hiddenValue :
15495                 this.value !== undefined ? this.value : '';
15496         
15497             this.el.dom.removeAttribute('name');
15498             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15499         }
15500         
15501         if(this.multiple){
15502             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15503             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15504         }
15505         
15506         if(this.removable && !this.multiple){
15507             var close = this.closeTriggerEl();
15508             if(close){
15509                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15510                 close.on('click', this.removeBtnClick, this, close);
15511             }
15512         }
15513         /*
15514          * fix the bug in Safari iOS8
15515          */
15516         this.inputEl().on("focus", function(e){
15517             document.activeElement.blur();
15518         }, this);
15519         
15520         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15521         
15522         return;
15523         
15524         
15525     },
15526     
15527     renderTouchView : function()
15528     {
15529         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15530         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15531         
15532         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15533         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15534         
15535         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15536         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15537         this.touchViewBodyEl.setStyle('overflow', 'auto');
15538         
15539         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15540         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15541         
15542         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15543         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15544         
15545     },
15546     
15547     showTouchView : function()
15548     {
15549         if(this.disabled){
15550             return;
15551         }
15552         
15553         this.touchViewHeaderEl.hide();
15554
15555         if(this.modalTitle.length){
15556             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15557             this.touchViewHeaderEl.show();
15558         }
15559
15560         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15561         this.touchViewEl.show();
15562
15563         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15564         
15565         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15566         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15567
15568         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15569
15570         if(this.modalTitle.length){
15571             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15572         }
15573         
15574         this.touchViewBodyEl.setHeight(bodyHeight);
15575
15576         if(this.animate){
15577             var _this = this;
15578             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15579         }else{
15580             this.touchViewEl.addClass('in');
15581         }
15582         
15583         if(this._touchViewMask){
15584             Roo.get(document.body).addClass("x-body-masked");
15585             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15586             this._touchViewMask.setStyle('z-index', 10000);
15587             this._touchViewMask.addClass('show');
15588         }
15589         
15590         this.doTouchViewQuery();
15591         
15592     },
15593     
15594     hideTouchView : function()
15595     {
15596         this.touchViewEl.removeClass('in');
15597
15598         if(this.animate){
15599             var _this = this;
15600             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15601         }else{
15602             this.touchViewEl.setStyle('display', 'none');
15603         }
15604         
15605         if(this._touchViewMask){
15606             this._touchViewMask.removeClass('show');
15607             Roo.get(document.body).removeClass("x-body-masked");
15608         }
15609     },
15610     
15611     setTouchViewValue : function()
15612     {
15613         if(this.multiple){
15614             this.clearItem();
15615         
15616             var _this = this;
15617
15618             Roo.each(this.tickItems, function(o){
15619                 this.addItem(o);
15620             }, this);
15621         }
15622         
15623         this.hideTouchView();
15624     },
15625     
15626     doTouchViewQuery : function()
15627     {
15628         var qe = {
15629             query: '',
15630             forceAll: true,
15631             combo: this,
15632             cancel:false
15633         };
15634         
15635         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15636             return false;
15637         }
15638         
15639         if(!this.alwaysQuery || this.mode == 'local'){
15640             this.onTouchViewLoad();
15641             return;
15642         }
15643         
15644         this.store.load();
15645     },
15646     
15647     onTouchViewBeforeLoad : function(combo,opts)
15648     {
15649         return;
15650     },
15651
15652     // private
15653     onTouchViewLoad : function()
15654     {
15655         if(this.store.getCount() < 1){
15656             this.onTouchViewEmptyResults();
15657             return;
15658         }
15659         
15660         this.clearTouchView();
15661         
15662         var rawValue = this.getRawValue();
15663         
15664         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15665         
15666         this.tickItems = [];
15667         
15668         this.store.data.each(function(d, rowIndex){
15669             var row = this.touchViewListGroup.createChild(template);
15670             
15671             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15672                 row.addClass(d.data.cls);
15673             }
15674             
15675             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15676                 var cfg = {
15677                     data : d.data,
15678                     html : d.data[this.displayField]
15679                 };
15680                 
15681                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15682                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15683                 }
15684             }
15685             row.removeClass('selected');
15686             if(!this.multiple && this.valueField &&
15687                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15688             {
15689                 // radio buttons..
15690                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15691                 row.addClass('selected');
15692             }
15693             
15694             if(this.multiple && this.valueField &&
15695                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15696             {
15697                 
15698                 // checkboxes...
15699                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15700                 this.tickItems.push(d.data);
15701             }
15702             
15703             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15704             
15705         }, this);
15706         
15707         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15708         
15709         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15710
15711         if(this.modalTitle.length){
15712             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15713         }
15714
15715         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15716         
15717         if(this.mobile_restrict_height && listHeight < bodyHeight){
15718             this.touchViewBodyEl.setHeight(listHeight);
15719         }
15720         
15721         var _this = this;
15722         
15723         if(firstChecked && listHeight > bodyHeight){
15724             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15725         }
15726         
15727     },
15728     
15729     onTouchViewLoadException : function()
15730     {
15731         this.hideTouchView();
15732     },
15733     
15734     onTouchViewEmptyResults : function()
15735     {
15736         this.clearTouchView();
15737         
15738         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15739         
15740         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15741         
15742     },
15743     
15744     clearTouchView : function()
15745     {
15746         this.touchViewListGroup.dom.innerHTML = '';
15747     },
15748     
15749     onTouchViewClick : function(e, el, o)
15750     {
15751         e.preventDefault();
15752         
15753         var row = o.row;
15754         var rowIndex = o.rowIndex;
15755         
15756         var r = this.store.getAt(rowIndex);
15757         
15758         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15759             
15760             if(!this.multiple){
15761                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15762                     c.dom.removeAttribute('checked');
15763                 }, this);
15764
15765                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15766
15767                 this.setFromData(r.data);
15768
15769                 var close = this.closeTriggerEl();
15770
15771                 if(close){
15772                     close.show();
15773                 }
15774
15775                 this.hideTouchView();
15776
15777                 this.fireEvent('select', this, r, rowIndex);
15778
15779                 return;
15780             }
15781
15782             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15783                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15784                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15785                 return;
15786             }
15787
15788             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15789             this.addItem(r.data);
15790             this.tickItems.push(r.data);
15791         }
15792     },
15793     
15794     getAutoCreateNativeIOS : function()
15795     {
15796         var cfg = {
15797             cls: 'form-group' //input-group,
15798         };
15799         
15800         var combobox =  {
15801             tag: 'select',
15802             cls : 'roo-ios-select'
15803         };
15804         
15805         if (this.name) {
15806             combobox.name = this.name;
15807         }
15808         
15809         if (this.disabled) {
15810             combobox.disabled = true;
15811         }
15812         
15813         var settings = this;
15814         
15815         ['xs','sm','md','lg'].map(function(size){
15816             if (settings[size]) {
15817                 cfg.cls += ' col-' + size + '-' + settings[size];
15818             }
15819         });
15820         
15821         cfg.cn = combobox;
15822         
15823         return cfg;
15824         
15825     },
15826     
15827     initIOSView : function()
15828     {
15829         this.store.on('load', this.onIOSViewLoad, this);
15830         
15831         return;
15832     },
15833     
15834     onIOSViewLoad : function()
15835     {
15836         if(this.store.getCount() < 1){
15837             return;
15838         }
15839         
15840         this.clearIOSView();
15841         
15842         if(this.allowBlank) {
15843             
15844             var default_text = '-- SELECT --';
15845             
15846             if(this.placeholder.length){
15847                 default_text = this.placeholder;
15848             }
15849             
15850             if(this.emptyTitle.length){
15851                 default_text += ' - ' + this.emptyTitle + ' -';
15852             }
15853             
15854             var opt = this.inputEl().createChild({
15855                 tag: 'option',
15856                 value : 0,
15857                 html : default_text
15858             });
15859             
15860             var o = {};
15861             o[this.valueField] = 0;
15862             o[this.displayField] = default_text;
15863             
15864             this.ios_options.push({
15865                 data : o,
15866                 el : opt
15867             });
15868             
15869         }
15870         
15871         this.store.data.each(function(d, rowIndex){
15872             
15873             var html = '';
15874             
15875             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15876                 html = d.data[this.displayField];
15877             }
15878             
15879             var value = '';
15880             
15881             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15882                 value = d.data[this.valueField];
15883             }
15884             
15885             var option = {
15886                 tag: 'option',
15887                 value : value,
15888                 html : html
15889             };
15890             
15891             if(this.value == d.data[this.valueField]){
15892                 option['selected'] = true;
15893             }
15894             
15895             var opt = this.inputEl().createChild(option);
15896             
15897             this.ios_options.push({
15898                 data : d.data,
15899                 el : opt
15900             });
15901             
15902         }, this);
15903         
15904         this.inputEl().on('change', function(){
15905            this.fireEvent('select', this);
15906         }, this);
15907         
15908     },
15909     
15910     clearIOSView: function()
15911     {
15912         this.inputEl().dom.innerHTML = '';
15913         
15914         this.ios_options = [];
15915     },
15916     
15917     setIOSValue: function(v)
15918     {
15919         this.value = v;
15920         
15921         if(!this.ios_options){
15922             return;
15923         }
15924         
15925         Roo.each(this.ios_options, function(opts){
15926            
15927            opts.el.dom.removeAttribute('selected');
15928            
15929            if(opts.data[this.valueField] != v){
15930                return;
15931            }
15932            
15933            opts.el.dom.setAttribute('selected', true);
15934            
15935         }, this);
15936     }
15937
15938     /** 
15939     * @cfg {Boolean} grow 
15940     * @hide 
15941     */
15942     /** 
15943     * @cfg {Number} growMin 
15944     * @hide 
15945     */
15946     /** 
15947     * @cfg {Number} growMax 
15948     * @hide 
15949     */
15950     /**
15951      * @hide
15952      * @method autoSize
15953      */
15954 });
15955
15956 Roo.apply(Roo.bootstrap.ComboBox,  {
15957     
15958     header : {
15959         tag: 'div',
15960         cls: 'modal-header',
15961         cn: [
15962             {
15963                 tag: 'h4',
15964                 cls: 'modal-title'
15965             }
15966         ]
15967     },
15968     
15969     body : {
15970         tag: 'div',
15971         cls: 'modal-body',
15972         cn: [
15973             {
15974                 tag: 'ul',
15975                 cls: 'list-group'
15976             }
15977         ]
15978     },
15979     
15980     listItemRadio : {
15981         tag: 'li',
15982         cls: 'list-group-item',
15983         cn: [
15984             {
15985                 tag: 'span',
15986                 cls: 'roo-combobox-list-group-item-value'
15987             },
15988             {
15989                 tag: 'div',
15990                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15991                 cn: [
15992                     {
15993                         tag: 'input',
15994                         type: 'radio'
15995                     },
15996                     {
15997                         tag: 'label'
15998                     }
15999                 ]
16000             }
16001         ]
16002     },
16003     
16004     listItemCheckbox : {
16005         tag: 'li',
16006         cls: 'list-group-item',
16007         cn: [
16008             {
16009                 tag: 'span',
16010                 cls: 'roo-combobox-list-group-item-value'
16011             },
16012             {
16013                 tag: 'div',
16014                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16015                 cn: [
16016                     {
16017                         tag: 'input',
16018                         type: 'checkbox'
16019                     },
16020                     {
16021                         tag: 'label'
16022                     }
16023                 ]
16024             }
16025         ]
16026     },
16027     
16028     emptyResult : {
16029         tag: 'div',
16030         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16031     },
16032     
16033     footer : {
16034         tag: 'div',
16035         cls: 'modal-footer',
16036         cn: [
16037             {
16038                 tag: 'div',
16039                 cls: 'row',
16040                 cn: [
16041                     {
16042                         tag: 'div',
16043                         cls: 'col-xs-6 text-left',
16044                         cn: {
16045                             tag: 'button',
16046                             cls: 'btn btn-danger roo-touch-view-cancel',
16047                             html: 'Cancel'
16048                         }
16049                     },
16050                     {
16051                         tag: 'div',
16052                         cls: 'col-xs-6 text-right',
16053                         cn: {
16054                             tag: 'button',
16055                             cls: 'btn btn-success roo-touch-view-ok',
16056                             html: 'OK'
16057                         }
16058                     }
16059                 ]
16060             }
16061         ]
16062         
16063     }
16064 });
16065
16066 Roo.apply(Roo.bootstrap.ComboBox,  {
16067     
16068     touchViewTemplate : {
16069         tag: 'div',
16070         cls: 'modal fade roo-combobox-touch-view',
16071         cn: [
16072             {
16073                 tag: 'div',
16074                 cls: 'modal-dialog',
16075                 style : 'position:fixed', // we have to fix position....
16076                 cn: [
16077                     {
16078                         tag: 'div',
16079                         cls: 'modal-content',
16080                         cn: [
16081                             Roo.bootstrap.ComboBox.header,
16082                             Roo.bootstrap.ComboBox.body,
16083                             Roo.bootstrap.ComboBox.footer
16084                         ]
16085                     }
16086                 ]
16087             }
16088         ]
16089     }
16090 });/*
16091  * Based on:
16092  * Ext JS Library 1.1.1
16093  * Copyright(c) 2006-2007, Ext JS, LLC.
16094  *
16095  * Originally Released Under LGPL - original licence link has changed is not relivant.
16096  *
16097  * Fork - LGPL
16098  * <script type="text/javascript">
16099  */
16100
16101 /**
16102  * @class Roo.View
16103  * @extends Roo.util.Observable
16104  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16105  * This class also supports single and multi selection modes. <br>
16106  * Create a data model bound view:
16107  <pre><code>
16108  var store = new Roo.data.Store(...);
16109
16110  var view = new Roo.View({
16111     el : "my-element",
16112     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16113  
16114     singleSelect: true,
16115     selectedClass: "ydataview-selected",
16116     store: store
16117  });
16118
16119  // listen for node click?
16120  view.on("click", function(vw, index, node, e){
16121  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16122  });
16123
16124  // load XML data
16125  dataModel.load("foobar.xml");
16126  </code></pre>
16127  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16128  * <br><br>
16129  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16130  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16131  * 
16132  * Note: old style constructor is still suported (container, template, config)
16133  * 
16134  * @constructor
16135  * Create a new View
16136  * @param {Object} config The config object
16137  * 
16138  */
16139 Roo.View = function(config, depreciated_tpl, depreciated_config){
16140     
16141     this.parent = false;
16142     
16143     if (typeof(depreciated_tpl) == 'undefined') {
16144         // new way.. - universal constructor.
16145         Roo.apply(this, config);
16146         this.el  = Roo.get(this.el);
16147     } else {
16148         // old format..
16149         this.el  = Roo.get(config);
16150         this.tpl = depreciated_tpl;
16151         Roo.apply(this, depreciated_config);
16152     }
16153     this.wrapEl  = this.el.wrap().wrap();
16154     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16155     
16156     
16157     if(typeof(this.tpl) == "string"){
16158         this.tpl = new Roo.Template(this.tpl);
16159     } else {
16160         // support xtype ctors..
16161         this.tpl = new Roo.factory(this.tpl, Roo);
16162     }
16163     
16164     
16165     this.tpl.compile();
16166     
16167     /** @private */
16168     this.addEvents({
16169         /**
16170          * @event beforeclick
16171          * Fires before a click is processed. Returns false to cancel the default action.
16172          * @param {Roo.View} this
16173          * @param {Number} index The index of the target node
16174          * @param {HTMLElement} node The target node
16175          * @param {Roo.EventObject} e The raw event object
16176          */
16177             "beforeclick" : true,
16178         /**
16179          * @event click
16180          * Fires when a template node is clicked.
16181          * @param {Roo.View} this
16182          * @param {Number} index The index of the target node
16183          * @param {HTMLElement} node The target node
16184          * @param {Roo.EventObject} e The raw event object
16185          */
16186             "click" : true,
16187         /**
16188          * @event dblclick
16189          * Fires when a template node is double clicked.
16190          * @param {Roo.View} this
16191          * @param {Number} index The index of the target node
16192          * @param {HTMLElement} node The target node
16193          * @param {Roo.EventObject} e The raw event object
16194          */
16195             "dblclick" : true,
16196         /**
16197          * @event contextmenu
16198          * Fires when a template node is right clicked.
16199          * @param {Roo.View} this
16200          * @param {Number} index The index of the target node
16201          * @param {HTMLElement} node The target node
16202          * @param {Roo.EventObject} e The raw event object
16203          */
16204             "contextmenu" : true,
16205         /**
16206          * @event selectionchange
16207          * Fires when the selected nodes change.
16208          * @param {Roo.View} this
16209          * @param {Array} selections Array of the selected nodes
16210          */
16211             "selectionchange" : true,
16212     
16213         /**
16214          * @event beforeselect
16215          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16216          * @param {Roo.View} this
16217          * @param {HTMLElement} node The node to be selected
16218          * @param {Array} selections Array of currently selected nodes
16219          */
16220             "beforeselect" : true,
16221         /**
16222          * @event preparedata
16223          * Fires on every row to render, to allow you to change the data.
16224          * @param {Roo.View} this
16225          * @param {Object} data to be rendered (change this)
16226          */
16227           "preparedata" : true
16228           
16229           
16230         });
16231
16232
16233
16234     this.el.on({
16235         "click": this.onClick,
16236         "dblclick": this.onDblClick,
16237         "contextmenu": this.onContextMenu,
16238         scope:this
16239     });
16240
16241     this.selections = [];
16242     this.nodes = [];
16243     this.cmp = new Roo.CompositeElementLite([]);
16244     if(this.store){
16245         this.store = Roo.factory(this.store, Roo.data);
16246         this.setStore(this.store, true);
16247     }
16248     
16249     if ( this.footer && this.footer.xtype) {
16250            
16251          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16252         
16253         this.footer.dataSource = this.store;
16254         this.footer.container = fctr;
16255         this.footer = Roo.factory(this.footer, Roo);
16256         fctr.insertFirst(this.el);
16257         
16258         // this is a bit insane - as the paging toolbar seems to detach the el..
16259 //        dom.parentNode.parentNode.parentNode
16260          // they get detached?
16261     }
16262     
16263     
16264     Roo.View.superclass.constructor.call(this);
16265     
16266     
16267 };
16268
16269 Roo.extend(Roo.View, Roo.util.Observable, {
16270     
16271      /**
16272      * @cfg {Roo.data.Store} store Data store to load data from.
16273      */
16274     store : false,
16275     
16276     /**
16277      * @cfg {String|Roo.Element} el The container element.
16278      */
16279     el : '',
16280     
16281     /**
16282      * @cfg {String|Roo.Template} tpl The template used by this View 
16283      */
16284     tpl : false,
16285     /**
16286      * @cfg {String} dataName the named area of the template to use as the data area
16287      *                          Works with domtemplates roo-name="name"
16288      */
16289     dataName: false,
16290     /**
16291      * @cfg {String} selectedClass The css class to add to selected nodes
16292      */
16293     selectedClass : "x-view-selected",
16294      /**
16295      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16296      */
16297     emptyText : "",
16298     
16299     /**
16300      * @cfg {String} text to display on mask (default Loading)
16301      */
16302     mask : false,
16303     /**
16304      * @cfg {Boolean} multiSelect Allow multiple selection
16305      */
16306     multiSelect : false,
16307     /**
16308      * @cfg {Boolean} singleSelect Allow single selection
16309      */
16310     singleSelect:  false,
16311     
16312     /**
16313      * @cfg {Boolean} toggleSelect - selecting 
16314      */
16315     toggleSelect : false,
16316     
16317     /**
16318      * @cfg {Boolean} tickable - selecting 
16319      */
16320     tickable : false,
16321     
16322     /**
16323      * Returns the element this view is bound to.
16324      * @return {Roo.Element}
16325      */
16326     getEl : function(){
16327         return this.wrapEl;
16328     },
16329     
16330     
16331
16332     /**
16333      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16334      */
16335     refresh : function(){
16336         //Roo.log('refresh');
16337         var t = this.tpl;
16338         
16339         // if we are using something like 'domtemplate', then
16340         // the what gets used is:
16341         // t.applySubtemplate(NAME, data, wrapping data..)
16342         // the outer template then get' applied with
16343         //     the store 'extra data'
16344         // and the body get's added to the
16345         //      roo-name="data" node?
16346         //      <span class='roo-tpl-{name}'></span> ?????
16347         
16348         
16349         
16350         this.clearSelections();
16351         this.el.update("");
16352         var html = [];
16353         var records = this.store.getRange();
16354         if(records.length < 1) {
16355             
16356             // is this valid??  = should it render a template??
16357             
16358             this.el.update(this.emptyText);
16359             return;
16360         }
16361         var el = this.el;
16362         if (this.dataName) {
16363             this.el.update(t.apply(this.store.meta)); //????
16364             el = this.el.child('.roo-tpl-' + this.dataName);
16365         }
16366         
16367         for(var i = 0, len = records.length; i < len; i++){
16368             var data = this.prepareData(records[i].data, i, records[i]);
16369             this.fireEvent("preparedata", this, data, i, records[i]);
16370             
16371             var d = Roo.apply({}, data);
16372             
16373             if(this.tickable){
16374                 Roo.apply(d, {'roo-id' : Roo.id()});
16375                 
16376                 var _this = this;
16377             
16378                 Roo.each(this.parent.item, function(item){
16379                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16380                         return;
16381                     }
16382                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16383                 });
16384             }
16385             
16386             html[html.length] = Roo.util.Format.trim(
16387                 this.dataName ?
16388                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16389                     t.apply(d)
16390             );
16391         }
16392         
16393         
16394         
16395         el.update(html.join(""));
16396         this.nodes = el.dom.childNodes;
16397         this.updateIndexes(0);
16398     },
16399     
16400
16401     /**
16402      * Function to override to reformat the data that is sent to
16403      * the template for each node.
16404      * DEPRICATED - use the preparedata event handler.
16405      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16406      * a JSON object for an UpdateManager bound view).
16407      */
16408     prepareData : function(data, index, record)
16409     {
16410         this.fireEvent("preparedata", this, data, index, record);
16411         return data;
16412     },
16413
16414     onUpdate : function(ds, record){
16415         // Roo.log('on update');   
16416         this.clearSelections();
16417         var index = this.store.indexOf(record);
16418         var n = this.nodes[index];
16419         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16420         n.parentNode.removeChild(n);
16421         this.updateIndexes(index, index);
16422     },
16423
16424     
16425     
16426 // --------- FIXME     
16427     onAdd : function(ds, records, index)
16428     {
16429         //Roo.log(['on Add', ds, records, index] );        
16430         this.clearSelections();
16431         if(this.nodes.length == 0){
16432             this.refresh();
16433             return;
16434         }
16435         var n = this.nodes[index];
16436         for(var i = 0, len = records.length; i < len; i++){
16437             var d = this.prepareData(records[i].data, i, records[i]);
16438             if(n){
16439                 this.tpl.insertBefore(n, d);
16440             }else{
16441                 
16442                 this.tpl.append(this.el, d);
16443             }
16444         }
16445         this.updateIndexes(index);
16446     },
16447
16448     onRemove : function(ds, record, index){
16449        // Roo.log('onRemove');
16450         this.clearSelections();
16451         var el = this.dataName  ?
16452             this.el.child('.roo-tpl-' + this.dataName) :
16453             this.el; 
16454         
16455         el.dom.removeChild(this.nodes[index]);
16456         this.updateIndexes(index);
16457     },
16458
16459     /**
16460      * Refresh an individual node.
16461      * @param {Number} index
16462      */
16463     refreshNode : function(index){
16464         this.onUpdate(this.store, this.store.getAt(index));
16465     },
16466
16467     updateIndexes : function(startIndex, endIndex){
16468         var ns = this.nodes;
16469         startIndex = startIndex || 0;
16470         endIndex = endIndex || ns.length - 1;
16471         for(var i = startIndex; i <= endIndex; i++){
16472             ns[i].nodeIndex = i;
16473         }
16474     },
16475
16476     /**
16477      * Changes the data store this view uses and refresh the view.
16478      * @param {Store} store
16479      */
16480     setStore : function(store, initial){
16481         if(!initial && this.store){
16482             this.store.un("datachanged", this.refresh);
16483             this.store.un("add", this.onAdd);
16484             this.store.un("remove", this.onRemove);
16485             this.store.un("update", this.onUpdate);
16486             this.store.un("clear", this.refresh);
16487             this.store.un("beforeload", this.onBeforeLoad);
16488             this.store.un("load", this.onLoad);
16489             this.store.un("loadexception", this.onLoad);
16490         }
16491         if(store){
16492           
16493             store.on("datachanged", this.refresh, this);
16494             store.on("add", this.onAdd, this);
16495             store.on("remove", this.onRemove, this);
16496             store.on("update", this.onUpdate, this);
16497             store.on("clear", this.refresh, this);
16498             store.on("beforeload", this.onBeforeLoad, this);
16499             store.on("load", this.onLoad, this);
16500             store.on("loadexception", this.onLoad, this);
16501         }
16502         
16503         if(store){
16504             this.refresh();
16505         }
16506     },
16507     /**
16508      * onbeforeLoad - masks the loading area.
16509      *
16510      */
16511     onBeforeLoad : function(store,opts)
16512     {
16513          //Roo.log('onBeforeLoad');   
16514         if (!opts.add) {
16515             this.el.update("");
16516         }
16517         this.el.mask(this.mask ? this.mask : "Loading" ); 
16518     },
16519     onLoad : function ()
16520     {
16521         this.el.unmask();
16522     },
16523     
16524
16525     /**
16526      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16527      * @param {HTMLElement} node
16528      * @return {HTMLElement} The template node
16529      */
16530     findItemFromChild : function(node){
16531         var el = this.dataName  ?
16532             this.el.child('.roo-tpl-' + this.dataName,true) :
16533             this.el.dom; 
16534         
16535         if(!node || node.parentNode == el){
16536                     return node;
16537             }
16538             var p = node.parentNode;
16539             while(p && p != el){
16540             if(p.parentNode == el){
16541                 return p;
16542             }
16543             p = p.parentNode;
16544         }
16545             return null;
16546     },
16547
16548     /** @ignore */
16549     onClick : function(e){
16550         var item = this.findItemFromChild(e.getTarget());
16551         if(item){
16552             var index = this.indexOf(item);
16553             if(this.onItemClick(item, index, e) !== false){
16554                 this.fireEvent("click", this, index, item, e);
16555             }
16556         }else{
16557             this.clearSelections();
16558         }
16559     },
16560
16561     /** @ignore */
16562     onContextMenu : function(e){
16563         var item = this.findItemFromChild(e.getTarget());
16564         if(item){
16565             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16566         }
16567     },
16568
16569     /** @ignore */
16570     onDblClick : function(e){
16571         var item = this.findItemFromChild(e.getTarget());
16572         if(item){
16573             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16574         }
16575     },
16576
16577     onItemClick : function(item, index, e)
16578     {
16579         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16580             return false;
16581         }
16582         if (this.toggleSelect) {
16583             var m = this.isSelected(item) ? 'unselect' : 'select';
16584             //Roo.log(m);
16585             var _t = this;
16586             _t[m](item, true, false);
16587             return true;
16588         }
16589         if(this.multiSelect || this.singleSelect){
16590             if(this.multiSelect && e.shiftKey && this.lastSelection){
16591                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16592             }else{
16593                 this.select(item, this.multiSelect && e.ctrlKey);
16594                 this.lastSelection = item;
16595             }
16596             
16597             if(!this.tickable){
16598                 e.preventDefault();
16599             }
16600             
16601         }
16602         return true;
16603     },
16604
16605     /**
16606      * Get the number of selected nodes.
16607      * @return {Number}
16608      */
16609     getSelectionCount : function(){
16610         return this.selections.length;
16611     },
16612
16613     /**
16614      * Get the currently selected nodes.
16615      * @return {Array} An array of HTMLElements
16616      */
16617     getSelectedNodes : function(){
16618         return this.selections;
16619     },
16620
16621     /**
16622      * Get the indexes of the selected nodes.
16623      * @return {Array}
16624      */
16625     getSelectedIndexes : function(){
16626         var indexes = [], s = this.selections;
16627         for(var i = 0, len = s.length; i < len; i++){
16628             indexes.push(s[i].nodeIndex);
16629         }
16630         return indexes;
16631     },
16632
16633     /**
16634      * Clear all selections
16635      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16636      */
16637     clearSelections : function(suppressEvent){
16638         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16639             this.cmp.elements = this.selections;
16640             this.cmp.removeClass(this.selectedClass);
16641             this.selections = [];
16642             if(!suppressEvent){
16643                 this.fireEvent("selectionchange", this, this.selections);
16644             }
16645         }
16646     },
16647
16648     /**
16649      * Returns true if the passed node is selected
16650      * @param {HTMLElement/Number} node The node or node index
16651      * @return {Boolean}
16652      */
16653     isSelected : function(node){
16654         var s = this.selections;
16655         if(s.length < 1){
16656             return false;
16657         }
16658         node = this.getNode(node);
16659         return s.indexOf(node) !== -1;
16660     },
16661
16662     /**
16663      * Selects nodes.
16664      * @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
16665      * @param {Boolean} keepExisting (optional) true to keep existing selections
16666      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16667      */
16668     select : function(nodeInfo, keepExisting, suppressEvent){
16669         if(nodeInfo instanceof Array){
16670             if(!keepExisting){
16671                 this.clearSelections(true);
16672             }
16673             for(var i = 0, len = nodeInfo.length; i < len; i++){
16674                 this.select(nodeInfo[i], true, true);
16675             }
16676             return;
16677         } 
16678         var node = this.getNode(nodeInfo);
16679         if(!node || this.isSelected(node)){
16680             return; // already selected.
16681         }
16682         if(!keepExisting){
16683             this.clearSelections(true);
16684         }
16685         
16686         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16687             Roo.fly(node).addClass(this.selectedClass);
16688             this.selections.push(node);
16689             if(!suppressEvent){
16690                 this.fireEvent("selectionchange", this, this.selections);
16691             }
16692         }
16693         
16694         
16695     },
16696       /**
16697      * Unselects nodes.
16698      * @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
16699      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16700      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16701      */
16702     unselect : function(nodeInfo, keepExisting, suppressEvent)
16703     {
16704         if(nodeInfo instanceof Array){
16705             Roo.each(this.selections, function(s) {
16706                 this.unselect(s, nodeInfo);
16707             }, this);
16708             return;
16709         }
16710         var node = this.getNode(nodeInfo);
16711         if(!node || !this.isSelected(node)){
16712             //Roo.log("not selected");
16713             return; // not selected.
16714         }
16715         // fireevent???
16716         var ns = [];
16717         Roo.each(this.selections, function(s) {
16718             if (s == node ) {
16719                 Roo.fly(node).removeClass(this.selectedClass);
16720
16721                 return;
16722             }
16723             ns.push(s);
16724         },this);
16725         
16726         this.selections= ns;
16727         this.fireEvent("selectionchange", this, this.selections);
16728     },
16729
16730     /**
16731      * Gets a template node.
16732      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16733      * @return {HTMLElement} The node or null if it wasn't found
16734      */
16735     getNode : function(nodeInfo){
16736         if(typeof nodeInfo == "string"){
16737             return document.getElementById(nodeInfo);
16738         }else if(typeof nodeInfo == "number"){
16739             return this.nodes[nodeInfo];
16740         }
16741         return nodeInfo;
16742     },
16743
16744     /**
16745      * Gets a range template nodes.
16746      * @param {Number} startIndex
16747      * @param {Number} endIndex
16748      * @return {Array} An array of nodes
16749      */
16750     getNodes : function(start, end){
16751         var ns = this.nodes;
16752         start = start || 0;
16753         end = typeof end == "undefined" ? ns.length - 1 : end;
16754         var nodes = [];
16755         if(start <= end){
16756             for(var i = start; i <= end; i++){
16757                 nodes.push(ns[i]);
16758             }
16759         } else{
16760             for(var i = start; i >= end; i--){
16761                 nodes.push(ns[i]);
16762             }
16763         }
16764         return nodes;
16765     },
16766
16767     /**
16768      * Finds the index of the passed node
16769      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16770      * @return {Number} The index of the node or -1
16771      */
16772     indexOf : function(node){
16773         node = this.getNode(node);
16774         if(typeof node.nodeIndex == "number"){
16775             return node.nodeIndex;
16776         }
16777         var ns = this.nodes;
16778         for(var i = 0, len = ns.length; i < len; i++){
16779             if(ns[i] == node){
16780                 return i;
16781             }
16782         }
16783         return -1;
16784     }
16785 });
16786 /*
16787  * - LGPL
16788  *
16789  * based on jquery fullcalendar
16790  * 
16791  */
16792
16793 Roo.bootstrap = Roo.bootstrap || {};
16794 /**
16795  * @class Roo.bootstrap.Calendar
16796  * @extends Roo.bootstrap.Component
16797  * Bootstrap Calendar class
16798  * @cfg {Boolean} loadMask (true|false) default false
16799  * @cfg {Object} header generate the user specific header of the calendar, default false
16800
16801  * @constructor
16802  * Create a new Container
16803  * @param {Object} config The config object
16804  */
16805
16806
16807
16808 Roo.bootstrap.Calendar = function(config){
16809     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16810      this.addEvents({
16811         /**
16812              * @event select
16813              * Fires when a date is selected
16814              * @param {DatePicker} this
16815              * @param {Date} date The selected date
16816              */
16817         'select': true,
16818         /**
16819              * @event monthchange
16820              * Fires when the displayed month changes 
16821              * @param {DatePicker} this
16822              * @param {Date} date The selected month
16823              */
16824         'monthchange': true,
16825         /**
16826              * @event evententer
16827              * Fires when mouse over an event
16828              * @param {Calendar} this
16829              * @param {event} Event
16830              */
16831         'evententer': true,
16832         /**
16833              * @event eventleave
16834              * Fires when the mouse leaves an
16835              * @param {Calendar} this
16836              * @param {event}
16837              */
16838         'eventleave': true,
16839         /**
16840              * @event eventclick
16841              * Fires when the mouse click an
16842              * @param {Calendar} this
16843              * @param {event}
16844              */
16845         'eventclick': true
16846         
16847     });
16848
16849 };
16850
16851 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16852     
16853      /**
16854      * @cfg {Number} startDay
16855      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16856      */
16857     startDay : 0,
16858     
16859     loadMask : false,
16860     
16861     header : false,
16862       
16863     getAutoCreate : function(){
16864         
16865         
16866         var fc_button = function(name, corner, style, content ) {
16867             return Roo.apply({},{
16868                 tag : 'span',
16869                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16870                          (corner.length ?
16871                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16872                             ''
16873                         ),
16874                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16875                 unselectable: 'on'
16876             });
16877         };
16878         
16879         var header = {};
16880         
16881         if(!this.header){
16882             header = {
16883                 tag : 'table',
16884                 cls : 'fc-header',
16885                 style : 'width:100%',
16886                 cn : [
16887                     {
16888                         tag: 'tr',
16889                         cn : [
16890                             {
16891                                 tag : 'td',
16892                                 cls : 'fc-header-left',
16893                                 cn : [
16894                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16895                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16896                                     { tag: 'span', cls: 'fc-header-space' },
16897                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16898
16899
16900                                 ]
16901                             },
16902
16903                             {
16904                                 tag : 'td',
16905                                 cls : 'fc-header-center',
16906                                 cn : [
16907                                     {
16908                                         tag: 'span',
16909                                         cls: 'fc-header-title',
16910                                         cn : {
16911                                             tag: 'H2',
16912                                             html : 'month / year'
16913                                         }
16914                                     }
16915
16916                                 ]
16917                             },
16918                             {
16919                                 tag : 'td',
16920                                 cls : 'fc-header-right',
16921                                 cn : [
16922                               /*      fc_button('month', 'left', '', 'month' ),
16923                                     fc_button('week', '', '', 'week' ),
16924                                     fc_button('day', 'right', '', 'day' )
16925                                 */    
16926
16927                                 ]
16928                             }
16929
16930                         ]
16931                     }
16932                 ]
16933             };
16934         }
16935         
16936         header = this.header;
16937         
16938        
16939         var cal_heads = function() {
16940             var ret = [];
16941             // fixme - handle this.
16942             
16943             for (var i =0; i < Date.dayNames.length; i++) {
16944                 var d = Date.dayNames[i];
16945                 ret.push({
16946                     tag: 'th',
16947                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16948                     html : d.substring(0,3)
16949                 });
16950                 
16951             }
16952             ret[0].cls += ' fc-first';
16953             ret[6].cls += ' fc-last';
16954             return ret;
16955         };
16956         var cal_cell = function(n) {
16957             return  {
16958                 tag: 'td',
16959                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16960                 cn : [
16961                     {
16962                         cn : [
16963                             {
16964                                 cls: 'fc-day-number',
16965                                 html: 'D'
16966                             },
16967                             {
16968                                 cls: 'fc-day-content',
16969                              
16970                                 cn : [
16971                                      {
16972                                         style: 'position: relative;' // height: 17px;
16973                                     }
16974                                 ]
16975                             }
16976                             
16977                             
16978                         ]
16979                     }
16980                 ]
16981                 
16982             }
16983         };
16984         var cal_rows = function() {
16985             
16986             var ret = [];
16987             for (var r = 0; r < 6; r++) {
16988                 var row= {
16989                     tag : 'tr',
16990                     cls : 'fc-week',
16991                     cn : []
16992                 };
16993                 
16994                 for (var i =0; i < Date.dayNames.length; i++) {
16995                     var d = Date.dayNames[i];
16996                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16997
16998                 }
16999                 row.cn[0].cls+=' fc-first';
17000                 row.cn[0].cn[0].style = 'min-height:90px';
17001                 row.cn[6].cls+=' fc-last';
17002                 ret.push(row);
17003                 
17004             }
17005             ret[0].cls += ' fc-first';
17006             ret[4].cls += ' fc-prev-last';
17007             ret[5].cls += ' fc-last';
17008             return ret;
17009             
17010         };
17011         
17012         var cal_table = {
17013             tag: 'table',
17014             cls: 'fc-border-separate',
17015             style : 'width:100%',
17016             cellspacing  : 0,
17017             cn : [
17018                 { 
17019                     tag: 'thead',
17020                     cn : [
17021                         { 
17022                             tag: 'tr',
17023                             cls : 'fc-first fc-last',
17024                             cn : cal_heads()
17025                         }
17026                     ]
17027                 },
17028                 { 
17029                     tag: 'tbody',
17030                     cn : cal_rows()
17031                 }
17032                   
17033             ]
17034         };
17035          
17036          var cfg = {
17037             cls : 'fc fc-ltr',
17038             cn : [
17039                 header,
17040                 {
17041                     cls : 'fc-content',
17042                     style : "position: relative;",
17043                     cn : [
17044                         {
17045                             cls : 'fc-view fc-view-month fc-grid',
17046                             style : 'position: relative',
17047                             unselectable : 'on',
17048                             cn : [
17049                                 {
17050                                     cls : 'fc-event-container',
17051                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17052                                 },
17053                                 cal_table
17054                             ]
17055                         }
17056                     ]
17057     
17058                 }
17059            ] 
17060             
17061         };
17062         
17063          
17064         
17065         return cfg;
17066     },
17067     
17068     
17069     initEvents : function()
17070     {
17071         if(!this.store){
17072             throw "can not find store for calendar";
17073         }
17074         
17075         var mark = {
17076             tag: "div",
17077             cls:"x-dlg-mask",
17078             style: "text-align:center",
17079             cn: [
17080                 {
17081                     tag: "div",
17082                     style: "background-color:white;width:50%;margin:250 auto",
17083                     cn: [
17084                         {
17085                             tag: "img",
17086                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17087                         },
17088                         {
17089                             tag: "span",
17090                             html: "Loading"
17091                         }
17092                         
17093                     ]
17094                 }
17095             ]
17096         };
17097         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17098         
17099         var size = this.el.select('.fc-content', true).first().getSize();
17100         this.maskEl.setSize(size.width, size.height);
17101         this.maskEl.enableDisplayMode("block");
17102         if(!this.loadMask){
17103             this.maskEl.hide();
17104         }
17105         
17106         this.store = Roo.factory(this.store, Roo.data);
17107         this.store.on('load', this.onLoad, this);
17108         this.store.on('beforeload', this.onBeforeLoad, this);
17109         
17110         this.resize();
17111         
17112         this.cells = this.el.select('.fc-day',true);
17113         //Roo.log(this.cells);
17114         this.textNodes = this.el.query('.fc-day-number');
17115         this.cells.addClassOnOver('fc-state-hover');
17116         
17117         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17118         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17119         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17120         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17121         
17122         this.on('monthchange', this.onMonthChange, this);
17123         
17124         this.update(new Date().clearTime());
17125     },
17126     
17127     resize : function() {
17128         var sz  = this.el.getSize();
17129         
17130         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17131         this.el.select('.fc-day-content div',true).setHeight(34);
17132     },
17133     
17134     
17135     // private
17136     showPrevMonth : function(e){
17137         this.update(this.activeDate.add("mo", -1));
17138     },
17139     showToday : function(e){
17140         this.update(new Date().clearTime());
17141     },
17142     // private
17143     showNextMonth : function(e){
17144         this.update(this.activeDate.add("mo", 1));
17145     },
17146
17147     // private
17148     showPrevYear : function(){
17149         this.update(this.activeDate.add("y", -1));
17150     },
17151
17152     // private
17153     showNextYear : function(){
17154         this.update(this.activeDate.add("y", 1));
17155     },
17156
17157     
17158    // private
17159     update : function(date)
17160     {
17161         var vd = this.activeDate;
17162         this.activeDate = date;
17163 //        if(vd && this.el){
17164 //            var t = date.getTime();
17165 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17166 //                Roo.log('using add remove');
17167 //                
17168 //                this.fireEvent('monthchange', this, date);
17169 //                
17170 //                this.cells.removeClass("fc-state-highlight");
17171 //                this.cells.each(function(c){
17172 //                   if(c.dateValue == t){
17173 //                       c.addClass("fc-state-highlight");
17174 //                       setTimeout(function(){
17175 //                            try{c.dom.firstChild.focus();}catch(e){}
17176 //                       }, 50);
17177 //                       return false;
17178 //                   }
17179 //                   return true;
17180 //                });
17181 //                return;
17182 //            }
17183 //        }
17184         
17185         var days = date.getDaysInMonth();
17186         
17187         var firstOfMonth = date.getFirstDateOfMonth();
17188         var startingPos = firstOfMonth.getDay()-this.startDay;
17189         
17190         if(startingPos < this.startDay){
17191             startingPos += 7;
17192         }
17193         
17194         var pm = date.add(Date.MONTH, -1);
17195         var prevStart = pm.getDaysInMonth()-startingPos;
17196 //        
17197         this.cells = this.el.select('.fc-day',true);
17198         this.textNodes = this.el.query('.fc-day-number');
17199         this.cells.addClassOnOver('fc-state-hover');
17200         
17201         var cells = this.cells.elements;
17202         var textEls = this.textNodes;
17203         
17204         Roo.each(cells, function(cell){
17205             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17206         });
17207         
17208         days += startingPos;
17209
17210         // convert everything to numbers so it's fast
17211         var day = 86400000;
17212         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17213         //Roo.log(d);
17214         //Roo.log(pm);
17215         //Roo.log(prevStart);
17216         
17217         var today = new Date().clearTime().getTime();
17218         var sel = date.clearTime().getTime();
17219         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17220         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17221         var ddMatch = this.disabledDatesRE;
17222         var ddText = this.disabledDatesText;
17223         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17224         var ddaysText = this.disabledDaysText;
17225         var format = this.format;
17226         
17227         var setCellClass = function(cal, cell){
17228             cell.row = 0;
17229             cell.events = [];
17230             cell.more = [];
17231             //Roo.log('set Cell Class');
17232             cell.title = "";
17233             var t = d.getTime();
17234             
17235             //Roo.log(d);
17236             
17237             cell.dateValue = t;
17238             if(t == today){
17239                 cell.className += " fc-today";
17240                 cell.className += " fc-state-highlight";
17241                 cell.title = cal.todayText;
17242             }
17243             if(t == sel){
17244                 // disable highlight in other month..
17245                 //cell.className += " fc-state-highlight";
17246                 
17247             }
17248             // disabling
17249             if(t < min) {
17250                 cell.className = " fc-state-disabled";
17251                 cell.title = cal.minText;
17252                 return;
17253             }
17254             if(t > max) {
17255                 cell.className = " fc-state-disabled";
17256                 cell.title = cal.maxText;
17257                 return;
17258             }
17259             if(ddays){
17260                 if(ddays.indexOf(d.getDay()) != -1){
17261                     cell.title = ddaysText;
17262                     cell.className = " fc-state-disabled";
17263                 }
17264             }
17265             if(ddMatch && format){
17266                 var fvalue = d.dateFormat(format);
17267                 if(ddMatch.test(fvalue)){
17268                     cell.title = ddText.replace("%0", fvalue);
17269                     cell.className = " fc-state-disabled";
17270                 }
17271             }
17272             
17273             if (!cell.initialClassName) {
17274                 cell.initialClassName = cell.dom.className;
17275             }
17276             
17277             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17278         };
17279
17280         var i = 0;
17281         
17282         for(; i < startingPos; i++) {
17283             textEls[i].innerHTML = (++prevStart);
17284             d.setDate(d.getDate()+1);
17285             
17286             cells[i].className = "fc-past fc-other-month";
17287             setCellClass(this, cells[i]);
17288         }
17289         
17290         var intDay = 0;
17291         
17292         for(; i < days; i++){
17293             intDay = i - startingPos + 1;
17294             textEls[i].innerHTML = (intDay);
17295             d.setDate(d.getDate()+1);
17296             
17297             cells[i].className = ''; // "x-date-active";
17298             setCellClass(this, cells[i]);
17299         }
17300         var extraDays = 0;
17301         
17302         for(; i < 42; i++) {
17303             textEls[i].innerHTML = (++extraDays);
17304             d.setDate(d.getDate()+1);
17305             
17306             cells[i].className = "fc-future fc-other-month";
17307             setCellClass(this, cells[i]);
17308         }
17309         
17310         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17311         
17312         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17313         
17314         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17315         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17316         
17317         if(totalRows != 6){
17318             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17319             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17320         }
17321         
17322         this.fireEvent('monthchange', this, date);
17323         
17324         
17325         /*
17326         if(!this.internalRender){
17327             var main = this.el.dom.firstChild;
17328             var w = main.offsetWidth;
17329             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17330             Roo.fly(main).setWidth(w);
17331             this.internalRender = true;
17332             // opera does not respect the auto grow header center column
17333             // then, after it gets a width opera refuses to recalculate
17334             // without a second pass
17335             if(Roo.isOpera && !this.secondPass){
17336                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17337                 this.secondPass = true;
17338                 this.update.defer(10, this, [date]);
17339             }
17340         }
17341         */
17342         
17343     },
17344     
17345     findCell : function(dt) {
17346         dt = dt.clearTime().getTime();
17347         var ret = false;
17348         this.cells.each(function(c){
17349             //Roo.log("check " +c.dateValue + '?=' + dt);
17350             if(c.dateValue == dt){
17351                 ret = c;
17352                 return false;
17353             }
17354             return true;
17355         });
17356         
17357         return ret;
17358     },
17359     
17360     findCells : function(ev) {
17361         var s = ev.start.clone().clearTime().getTime();
17362        // Roo.log(s);
17363         var e= ev.end.clone().clearTime().getTime();
17364        // Roo.log(e);
17365         var ret = [];
17366         this.cells.each(function(c){
17367              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17368             
17369             if(c.dateValue > e){
17370                 return ;
17371             }
17372             if(c.dateValue < s){
17373                 return ;
17374             }
17375             ret.push(c);
17376         });
17377         
17378         return ret;    
17379     },
17380     
17381 //    findBestRow: function(cells)
17382 //    {
17383 //        var ret = 0;
17384 //        
17385 //        for (var i =0 ; i < cells.length;i++) {
17386 //            ret  = Math.max(cells[i].rows || 0,ret);
17387 //        }
17388 //        return ret;
17389 //        
17390 //    },
17391     
17392     
17393     addItem : function(ev)
17394     {
17395         // look for vertical location slot in
17396         var cells = this.findCells(ev);
17397         
17398 //        ev.row = this.findBestRow(cells);
17399         
17400         // work out the location.
17401         
17402         var crow = false;
17403         var rows = [];
17404         for(var i =0; i < cells.length; i++) {
17405             
17406             cells[i].row = cells[0].row;
17407             
17408             if(i == 0){
17409                 cells[i].row = cells[i].row + 1;
17410             }
17411             
17412             if (!crow) {
17413                 crow = {
17414                     start : cells[i],
17415                     end :  cells[i]
17416                 };
17417                 continue;
17418             }
17419             if (crow.start.getY() == cells[i].getY()) {
17420                 // on same row.
17421                 crow.end = cells[i];
17422                 continue;
17423             }
17424             // different row.
17425             rows.push(crow);
17426             crow = {
17427                 start: cells[i],
17428                 end : cells[i]
17429             };
17430             
17431         }
17432         
17433         rows.push(crow);
17434         ev.els = [];
17435         ev.rows = rows;
17436         ev.cells = cells;
17437         
17438         cells[0].events.push(ev);
17439         
17440         this.calevents.push(ev);
17441     },
17442     
17443     clearEvents: function() {
17444         
17445         if(!this.calevents){
17446             return;
17447         }
17448         
17449         Roo.each(this.cells.elements, function(c){
17450             c.row = 0;
17451             c.events = [];
17452             c.more = [];
17453         });
17454         
17455         Roo.each(this.calevents, function(e) {
17456             Roo.each(e.els, function(el) {
17457                 el.un('mouseenter' ,this.onEventEnter, this);
17458                 el.un('mouseleave' ,this.onEventLeave, this);
17459                 el.remove();
17460             },this);
17461         },this);
17462         
17463         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17464             e.remove();
17465         });
17466         
17467     },
17468     
17469     renderEvents: function()
17470     {   
17471         var _this = this;
17472         
17473         this.cells.each(function(c) {
17474             
17475             if(c.row < 5){
17476                 return;
17477             }
17478             
17479             var ev = c.events;
17480             
17481             var r = 4;
17482             if(c.row != c.events.length){
17483                 r = 4 - (4 - (c.row - c.events.length));
17484             }
17485             
17486             c.events = ev.slice(0, r);
17487             c.more = ev.slice(r);
17488             
17489             if(c.more.length && c.more.length == 1){
17490                 c.events.push(c.more.pop());
17491             }
17492             
17493             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17494             
17495         });
17496             
17497         this.cells.each(function(c) {
17498             
17499             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17500             
17501             
17502             for (var e = 0; e < c.events.length; e++){
17503                 var ev = c.events[e];
17504                 var rows = ev.rows;
17505                 
17506                 for(var i = 0; i < rows.length; i++) {
17507                 
17508                     // how many rows should it span..
17509
17510                     var  cfg = {
17511                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17512                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17513
17514                         unselectable : "on",
17515                         cn : [
17516                             {
17517                                 cls: 'fc-event-inner',
17518                                 cn : [
17519     //                                {
17520     //                                  tag:'span',
17521     //                                  cls: 'fc-event-time',
17522     //                                  html : cells.length > 1 ? '' : ev.time
17523     //                                },
17524                                     {
17525                                       tag:'span',
17526                                       cls: 'fc-event-title',
17527                                       html : String.format('{0}', ev.title)
17528                                     }
17529
17530
17531                                 ]
17532                             },
17533                             {
17534                                 cls: 'ui-resizable-handle ui-resizable-e',
17535                                 html : '&nbsp;&nbsp;&nbsp'
17536                             }
17537
17538                         ]
17539                     };
17540
17541                     if (i == 0) {
17542                         cfg.cls += ' fc-event-start';
17543                     }
17544                     if ((i+1) == rows.length) {
17545                         cfg.cls += ' fc-event-end';
17546                     }
17547
17548                     var ctr = _this.el.select('.fc-event-container',true).first();
17549                     var cg = ctr.createChild(cfg);
17550
17551                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17552                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17553
17554                     var r = (c.more.length) ? 1 : 0;
17555                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17556                     cg.setWidth(ebox.right - sbox.x -2);
17557
17558                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17559                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17560                     cg.on('click', _this.onEventClick, _this, ev);
17561
17562                     ev.els.push(cg);
17563                     
17564                 }
17565                 
17566             }
17567             
17568             
17569             if(c.more.length){
17570                 var  cfg = {
17571                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17572                     style : 'position: absolute',
17573                     unselectable : "on",
17574                     cn : [
17575                         {
17576                             cls: 'fc-event-inner',
17577                             cn : [
17578                                 {
17579                                   tag:'span',
17580                                   cls: 'fc-event-title',
17581                                   html : 'More'
17582                                 }
17583
17584
17585                             ]
17586                         },
17587                         {
17588                             cls: 'ui-resizable-handle ui-resizable-e',
17589                             html : '&nbsp;&nbsp;&nbsp'
17590                         }
17591
17592                     ]
17593                 };
17594
17595                 var ctr = _this.el.select('.fc-event-container',true).first();
17596                 var cg = ctr.createChild(cfg);
17597
17598                 var sbox = c.select('.fc-day-content',true).first().getBox();
17599                 var ebox = c.select('.fc-day-content',true).first().getBox();
17600                 //Roo.log(cg);
17601                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17602                 cg.setWidth(ebox.right - sbox.x -2);
17603
17604                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17605                 
17606             }
17607             
17608         });
17609         
17610         
17611         
17612     },
17613     
17614     onEventEnter: function (e, el,event,d) {
17615         this.fireEvent('evententer', this, el, event);
17616     },
17617     
17618     onEventLeave: function (e, el,event,d) {
17619         this.fireEvent('eventleave', this, el, event);
17620     },
17621     
17622     onEventClick: function (e, el,event,d) {
17623         this.fireEvent('eventclick', this, el, event);
17624     },
17625     
17626     onMonthChange: function () {
17627         this.store.load();
17628     },
17629     
17630     onMoreEventClick: function(e, el, more)
17631     {
17632         var _this = this;
17633         
17634         this.calpopover.placement = 'right';
17635         this.calpopover.setTitle('More');
17636         
17637         this.calpopover.setContent('');
17638         
17639         var ctr = this.calpopover.el.select('.popover-content', true).first();
17640         
17641         Roo.each(more, function(m){
17642             var cfg = {
17643                 cls : 'fc-event-hori fc-event-draggable',
17644                 html : m.title
17645             };
17646             var cg = ctr.createChild(cfg);
17647             
17648             cg.on('click', _this.onEventClick, _this, m);
17649         });
17650         
17651         this.calpopover.show(el);
17652         
17653         
17654     },
17655     
17656     onLoad: function () 
17657     {   
17658         this.calevents = [];
17659         var cal = this;
17660         
17661         if(this.store.getCount() > 0){
17662             this.store.data.each(function(d){
17663                cal.addItem({
17664                     id : d.data.id,
17665                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17666                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17667                     time : d.data.start_time,
17668                     title : d.data.title,
17669                     description : d.data.description,
17670                     venue : d.data.venue
17671                 });
17672             });
17673         }
17674         
17675         this.renderEvents();
17676         
17677         if(this.calevents.length && this.loadMask){
17678             this.maskEl.hide();
17679         }
17680     },
17681     
17682     onBeforeLoad: function()
17683     {
17684         this.clearEvents();
17685         if(this.loadMask){
17686             this.maskEl.show();
17687         }
17688     }
17689 });
17690
17691  
17692  /*
17693  * - LGPL
17694  *
17695  * element
17696  * 
17697  */
17698
17699 /**
17700  * @class Roo.bootstrap.Popover
17701  * @extends Roo.bootstrap.Component
17702  * Bootstrap Popover class
17703  * @cfg {String} html contents of the popover   (or false to use children..)
17704  * @cfg {String} title of popover (or false to hide)
17705  * @cfg {String} placement how it is placed
17706  * @cfg {String} trigger click || hover (or false to trigger manually)
17707  * @cfg {String} over what (parent or false to trigger manually.)
17708  * @cfg {Number} delay - delay before showing
17709  
17710  * @constructor
17711  * Create a new Popover
17712  * @param {Object} config The config object
17713  */
17714
17715 Roo.bootstrap.Popover = function(config){
17716     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17717     
17718     this.addEvents({
17719         // raw events
17720          /**
17721          * @event show
17722          * After the popover show
17723          * 
17724          * @param {Roo.bootstrap.Popover} this
17725          */
17726         "show" : true,
17727         /**
17728          * @event hide
17729          * After the popover hide
17730          * 
17731          * @param {Roo.bootstrap.Popover} this
17732          */
17733         "hide" : true
17734     });
17735 };
17736
17737 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17738     
17739     title: 'Fill in a title',
17740     html: false,
17741     
17742     placement : 'right',
17743     trigger : 'hover', // hover
17744     
17745     delay : 0,
17746     
17747     over: 'parent',
17748     
17749     can_build_overlaid : false,
17750     
17751     getChildContainer : function()
17752     {
17753         return this.el.select('.popover-content',true).first();
17754     },
17755     
17756     getAutoCreate : function(){
17757          
17758         var cfg = {
17759            cls : 'popover roo-dynamic',
17760            style: 'display:block',
17761            cn : [
17762                 {
17763                     cls : 'arrow'
17764                 },
17765                 {
17766                     cls : 'popover-inner',
17767                     cn : [
17768                         {
17769                             tag: 'h3',
17770                             cls: 'popover-title popover-header',
17771                             html : this.title
17772                         },
17773                         {
17774                             cls : 'popover-content popover-body',
17775                             html : this.html
17776                         }
17777                     ]
17778                     
17779                 }
17780            ]
17781         };
17782         
17783         return cfg;
17784     },
17785     setTitle: function(str)
17786     {
17787         this.title = str;
17788         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17789     },
17790     setContent: function(str)
17791     {
17792         this.html = str;
17793         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17794     },
17795     // as it get's added to the bottom of the page.
17796     onRender : function(ct, position)
17797     {
17798         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17799         if(!this.el){
17800             var cfg = Roo.apply({},  this.getAutoCreate());
17801             cfg.id = Roo.id();
17802             
17803             if (this.cls) {
17804                 cfg.cls += ' ' + this.cls;
17805             }
17806             if (this.style) {
17807                 cfg.style = this.style;
17808             }
17809             //Roo.log("adding to ");
17810             this.el = Roo.get(document.body).createChild(cfg, position);
17811 //            Roo.log(this.el);
17812         }
17813         this.initEvents();
17814     },
17815     
17816     initEvents : function()
17817     {
17818         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17819         this.el.enableDisplayMode('block');
17820         this.el.hide();
17821         if (this.over === false) {
17822             return; 
17823         }
17824         if (this.triggers === false) {
17825             return;
17826         }
17827         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17828         var triggers = this.trigger ? this.trigger.split(' ') : [];
17829         Roo.each(triggers, function(trigger) {
17830         
17831             if (trigger == 'click') {
17832                 on_el.on('click', this.toggle, this);
17833             } else if (trigger != 'manual') {
17834                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17835                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17836       
17837                 on_el.on(eventIn  ,this.enter, this);
17838                 on_el.on(eventOut, this.leave, this);
17839             }
17840         }, this);
17841         
17842     },
17843     
17844     
17845     // private
17846     timeout : null,
17847     hoverState : null,
17848     
17849     toggle : function () {
17850         this.hoverState == 'in' ? this.leave() : this.enter();
17851     },
17852     
17853     enter : function () {
17854         
17855         clearTimeout(this.timeout);
17856     
17857         this.hoverState = 'in';
17858     
17859         if (!this.delay || !this.delay.show) {
17860             this.show();
17861             return;
17862         }
17863         var _t = this;
17864         this.timeout = setTimeout(function () {
17865             if (_t.hoverState == 'in') {
17866                 _t.show();
17867             }
17868         }, this.delay.show)
17869     },
17870     
17871     leave : function() {
17872         clearTimeout(this.timeout);
17873     
17874         this.hoverState = 'out';
17875     
17876         if (!this.delay || !this.delay.hide) {
17877             this.hide();
17878             return;
17879         }
17880         var _t = this;
17881         this.timeout = setTimeout(function () {
17882             if (_t.hoverState == 'out') {
17883                 _t.hide();
17884             }
17885         }, this.delay.hide)
17886     },
17887     
17888     show : function (on_el)
17889     {
17890         if (!on_el) {
17891             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17892         }
17893         
17894         // set content.
17895         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17896         if (this.html !== false) {
17897             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17898         }
17899         this.el.removeClass([
17900             'fade','top','bottom', 'left', 'right','in',
17901             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17902         ]);
17903         if (!this.title.length) {
17904             this.el.select('.popover-title',true).hide();
17905         }
17906         
17907         var placement = typeof this.placement == 'function' ?
17908             this.placement.call(this, this.el, on_el) :
17909             this.placement;
17910             
17911         var autoToken = /\s?auto?\s?/i;
17912         var autoPlace = autoToken.test(placement);
17913         if (autoPlace) {
17914             placement = placement.replace(autoToken, '') || 'top';
17915         }
17916         
17917         //this.el.detach()
17918         //this.el.setXY([0,0]);
17919         this.el.show();
17920         this.el.dom.style.display='block';
17921         this.el.addClass(placement);
17922         
17923         //this.el.appendTo(on_el);
17924         
17925         var p = this.getPosition();
17926         var box = this.el.getBox();
17927         
17928         if (autoPlace) {
17929             // fixme..
17930         }
17931         var align = Roo.bootstrap.Popover.alignment[placement];
17932         
17933 //        Roo.log(align);
17934         this.el.alignTo(on_el, align[0],align[1]);
17935         //var arrow = this.el.select('.arrow',true).first();
17936         //arrow.set(align[2], 
17937         
17938         this.el.addClass('in');
17939         
17940         
17941         if (this.el.hasClass('fade')) {
17942             // fade it?
17943         }
17944         
17945         this.hoverState = 'in';
17946         
17947         this.fireEvent('show', this);
17948         
17949     },
17950     hide : function()
17951     {
17952         this.el.setXY([0,0]);
17953         this.el.removeClass('in');
17954         this.el.hide();
17955         this.hoverState = null;
17956         
17957         this.fireEvent('hide', this);
17958     }
17959     
17960 });
17961
17962 Roo.bootstrap.Popover.alignment = {
17963     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17964     'right' : ['l-r', [10,0], 'left bs-popover-left'],
17965     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17966     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17967 };
17968
17969  /*
17970  * - LGPL
17971  *
17972  * Progress
17973  * 
17974  */
17975
17976 /**
17977  * @class Roo.bootstrap.Progress
17978  * @extends Roo.bootstrap.Component
17979  * Bootstrap Progress class
17980  * @cfg {Boolean} striped striped of the progress bar
17981  * @cfg {Boolean} active animated of the progress bar
17982  * 
17983  * 
17984  * @constructor
17985  * Create a new Progress
17986  * @param {Object} config The config object
17987  */
17988
17989 Roo.bootstrap.Progress = function(config){
17990     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17991 };
17992
17993 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17994     
17995     striped : false,
17996     active: false,
17997     
17998     getAutoCreate : function(){
17999         var cfg = {
18000             tag: 'div',
18001             cls: 'progress'
18002         };
18003         
18004         
18005         if(this.striped){
18006             cfg.cls += ' progress-striped';
18007         }
18008       
18009         if(this.active){
18010             cfg.cls += ' active';
18011         }
18012         
18013         
18014         return cfg;
18015     }
18016    
18017 });
18018
18019  
18020
18021  /*
18022  * - LGPL
18023  *
18024  * ProgressBar
18025  * 
18026  */
18027
18028 /**
18029  * @class Roo.bootstrap.ProgressBar
18030  * @extends Roo.bootstrap.Component
18031  * Bootstrap ProgressBar class
18032  * @cfg {Number} aria_valuenow aria-value now
18033  * @cfg {Number} aria_valuemin aria-value min
18034  * @cfg {Number} aria_valuemax aria-value max
18035  * @cfg {String} label label for the progress bar
18036  * @cfg {String} panel (success | info | warning | danger )
18037  * @cfg {String} role role of the progress bar
18038  * @cfg {String} sr_only text
18039  * 
18040  * 
18041  * @constructor
18042  * Create a new ProgressBar
18043  * @param {Object} config The config object
18044  */
18045
18046 Roo.bootstrap.ProgressBar = function(config){
18047     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18048 };
18049
18050 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18051     
18052     aria_valuenow : 0,
18053     aria_valuemin : 0,
18054     aria_valuemax : 100,
18055     label : false,
18056     panel : false,
18057     role : false,
18058     sr_only: false,
18059     
18060     getAutoCreate : function()
18061     {
18062         
18063         var cfg = {
18064             tag: 'div',
18065             cls: 'progress-bar',
18066             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18067         };
18068         
18069         if(this.sr_only){
18070             cfg.cn = {
18071                 tag: 'span',
18072                 cls: 'sr-only',
18073                 html: this.sr_only
18074             }
18075         }
18076         
18077         if(this.role){
18078             cfg.role = this.role;
18079         }
18080         
18081         if(this.aria_valuenow){
18082             cfg['aria-valuenow'] = this.aria_valuenow;
18083         }
18084         
18085         if(this.aria_valuemin){
18086             cfg['aria-valuemin'] = this.aria_valuemin;
18087         }
18088         
18089         if(this.aria_valuemax){
18090             cfg['aria-valuemax'] = this.aria_valuemax;
18091         }
18092         
18093         if(this.label && !this.sr_only){
18094             cfg.html = this.label;
18095         }
18096         
18097         if(this.panel){
18098             cfg.cls += ' progress-bar-' + this.panel;
18099         }
18100         
18101         return cfg;
18102     },
18103     
18104     update : function(aria_valuenow)
18105     {
18106         this.aria_valuenow = aria_valuenow;
18107         
18108         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18109     }
18110    
18111 });
18112
18113  
18114
18115  /*
18116  * - LGPL
18117  *
18118  * column
18119  * 
18120  */
18121
18122 /**
18123  * @class Roo.bootstrap.TabGroup
18124  * @extends Roo.bootstrap.Column
18125  * Bootstrap Column class
18126  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18127  * @cfg {Boolean} carousel true to make the group behave like a carousel
18128  * @cfg {Boolean} bullets show bullets for the panels
18129  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18130  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18131  * @cfg {Boolean} showarrow (true|false) show arrow default true
18132  * 
18133  * @constructor
18134  * Create a new TabGroup
18135  * @param {Object} config The config object
18136  */
18137
18138 Roo.bootstrap.TabGroup = function(config){
18139     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18140     if (!this.navId) {
18141         this.navId = Roo.id();
18142     }
18143     this.tabs = [];
18144     Roo.bootstrap.TabGroup.register(this);
18145     
18146 };
18147
18148 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18149     
18150     carousel : false,
18151     transition : false,
18152     bullets : 0,
18153     timer : 0,
18154     autoslide : false,
18155     slideFn : false,
18156     slideOnTouch : false,
18157     showarrow : true,
18158     
18159     getAutoCreate : function()
18160     {
18161         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18162         
18163         cfg.cls += ' tab-content';
18164         
18165         if (this.carousel) {
18166             cfg.cls += ' carousel slide';
18167             
18168             cfg.cn = [{
18169                cls : 'carousel-inner',
18170                cn : []
18171             }];
18172         
18173             if(this.bullets  && !Roo.isTouch){
18174                 
18175                 var bullets = {
18176                     cls : 'carousel-bullets',
18177                     cn : []
18178                 };
18179                
18180                 if(this.bullets_cls){
18181                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18182                 }
18183                 
18184                 bullets.cn.push({
18185                     cls : 'clear'
18186                 });
18187                 
18188                 cfg.cn[0].cn.push(bullets);
18189             }
18190             
18191             if(this.showarrow){
18192                 cfg.cn[0].cn.push({
18193                     tag : 'div',
18194                     class : 'carousel-arrow',
18195                     cn : [
18196                         {
18197                             tag : 'div',
18198                             class : 'carousel-prev',
18199                             cn : [
18200                                 {
18201                                     tag : 'i',
18202                                     class : 'fa fa-chevron-left'
18203                                 }
18204                             ]
18205                         },
18206                         {
18207                             tag : 'div',
18208                             class : 'carousel-next',
18209                             cn : [
18210                                 {
18211                                     tag : 'i',
18212                                     class : 'fa fa-chevron-right'
18213                                 }
18214                             ]
18215                         }
18216                     ]
18217                 });
18218             }
18219             
18220         }
18221         
18222         return cfg;
18223     },
18224     
18225     initEvents:  function()
18226     {
18227 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18228 //            this.el.on("touchstart", this.onTouchStart, this);
18229 //        }
18230         
18231         if(this.autoslide){
18232             var _this = this;
18233             
18234             this.slideFn = window.setInterval(function() {
18235                 _this.showPanelNext();
18236             }, this.timer);
18237         }
18238         
18239         if(this.showarrow){
18240             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18241             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18242         }
18243         
18244         
18245     },
18246     
18247 //    onTouchStart : function(e, el, o)
18248 //    {
18249 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18250 //            return;
18251 //        }
18252 //        
18253 //        this.showPanelNext();
18254 //    },
18255     
18256     
18257     getChildContainer : function()
18258     {
18259         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18260     },
18261     
18262     /**
18263     * register a Navigation item
18264     * @param {Roo.bootstrap.NavItem} the navitem to add
18265     */
18266     register : function(item)
18267     {
18268         this.tabs.push( item);
18269         item.navId = this.navId; // not really needed..
18270         this.addBullet();
18271     
18272     },
18273     
18274     getActivePanel : function()
18275     {
18276         var r = false;
18277         Roo.each(this.tabs, function(t) {
18278             if (t.active) {
18279                 r = t;
18280                 return false;
18281             }
18282             return null;
18283         });
18284         return r;
18285         
18286     },
18287     getPanelByName : function(n)
18288     {
18289         var r = false;
18290         Roo.each(this.tabs, function(t) {
18291             if (t.tabId == n) {
18292                 r = t;
18293                 return false;
18294             }
18295             return null;
18296         });
18297         return r;
18298     },
18299     indexOfPanel : function(p)
18300     {
18301         var r = false;
18302         Roo.each(this.tabs, function(t,i) {
18303             if (t.tabId == p.tabId) {
18304                 r = i;
18305                 return false;
18306             }
18307             return null;
18308         });
18309         return r;
18310     },
18311     /**
18312      * show a specific panel
18313      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18314      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18315      */
18316     showPanel : function (pan)
18317     {
18318         if(this.transition || typeof(pan) == 'undefined'){
18319             Roo.log("waiting for the transitionend");
18320             return;
18321         }
18322         
18323         if (typeof(pan) == 'number') {
18324             pan = this.tabs[pan];
18325         }
18326         
18327         if (typeof(pan) == 'string') {
18328             pan = this.getPanelByName(pan);
18329         }
18330         
18331         var cur = this.getActivePanel();
18332         
18333         if(!pan || !cur){
18334             Roo.log('pan or acitve pan is undefined');
18335             return false;
18336         }
18337         
18338         if (pan.tabId == this.getActivePanel().tabId) {
18339             return true;
18340         }
18341         
18342         if (false === cur.fireEvent('beforedeactivate')) {
18343             return false;
18344         }
18345         
18346         if(this.bullets > 0 && !Roo.isTouch){
18347             this.setActiveBullet(this.indexOfPanel(pan));
18348         }
18349         
18350         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18351             
18352             this.transition = true;
18353             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18354             var lr = dir == 'next' ? 'left' : 'right';
18355             pan.el.addClass(dir); // or prev
18356             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18357             cur.el.addClass(lr); // or right
18358             pan.el.addClass(lr);
18359             
18360             var _this = this;
18361             cur.el.on('transitionend', function() {
18362                 Roo.log("trans end?");
18363                 
18364                 pan.el.removeClass([lr,dir]);
18365                 pan.setActive(true);
18366                 
18367                 cur.el.removeClass([lr]);
18368                 cur.setActive(false);
18369                 
18370                 _this.transition = false;
18371                 
18372             }, this, { single:  true } );
18373             
18374             return true;
18375         }
18376         
18377         cur.setActive(false);
18378         pan.setActive(true);
18379         
18380         return true;
18381         
18382     },
18383     showPanelNext : function()
18384     {
18385         var i = this.indexOfPanel(this.getActivePanel());
18386         
18387         if (i >= this.tabs.length - 1 && !this.autoslide) {
18388             return;
18389         }
18390         
18391         if (i >= this.tabs.length - 1 && this.autoslide) {
18392             i = -1;
18393         }
18394         
18395         this.showPanel(this.tabs[i+1]);
18396     },
18397     
18398     showPanelPrev : function()
18399     {
18400         var i = this.indexOfPanel(this.getActivePanel());
18401         
18402         if (i  < 1 && !this.autoslide) {
18403             return;
18404         }
18405         
18406         if (i < 1 && this.autoslide) {
18407             i = this.tabs.length;
18408         }
18409         
18410         this.showPanel(this.tabs[i-1]);
18411     },
18412     
18413     
18414     addBullet: function()
18415     {
18416         if(!this.bullets || Roo.isTouch){
18417             return;
18418         }
18419         var ctr = this.el.select('.carousel-bullets',true).first();
18420         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18421         var bullet = ctr.createChild({
18422             cls : 'bullet bullet-' + i
18423         },ctr.dom.lastChild);
18424         
18425         
18426         var _this = this;
18427         
18428         bullet.on('click', (function(e, el, o, ii, t){
18429
18430             e.preventDefault();
18431
18432             this.showPanel(ii);
18433
18434             if(this.autoslide && this.slideFn){
18435                 clearInterval(this.slideFn);
18436                 this.slideFn = window.setInterval(function() {
18437                     _this.showPanelNext();
18438                 }, this.timer);
18439             }
18440
18441         }).createDelegate(this, [i, bullet], true));
18442                 
18443         
18444     },
18445      
18446     setActiveBullet : function(i)
18447     {
18448         if(Roo.isTouch){
18449             return;
18450         }
18451         
18452         Roo.each(this.el.select('.bullet', true).elements, function(el){
18453             el.removeClass('selected');
18454         });
18455
18456         var bullet = this.el.select('.bullet-' + i, true).first();
18457         
18458         if(!bullet){
18459             return;
18460         }
18461         
18462         bullet.addClass('selected');
18463     }
18464     
18465     
18466   
18467 });
18468
18469  
18470
18471  
18472  
18473 Roo.apply(Roo.bootstrap.TabGroup, {
18474     
18475     groups: {},
18476      /**
18477     * register a Navigation Group
18478     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18479     */
18480     register : function(navgrp)
18481     {
18482         this.groups[navgrp.navId] = navgrp;
18483         
18484     },
18485     /**
18486     * fetch a Navigation Group based on the navigation ID
18487     * if one does not exist , it will get created.
18488     * @param {string} the navgroup to add
18489     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18490     */
18491     get: function(navId) {
18492         if (typeof(this.groups[navId]) == 'undefined') {
18493             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18494         }
18495         return this.groups[navId] ;
18496     }
18497     
18498     
18499     
18500 });
18501
18502  /*
18503  * - LGPL
18504  *
18505  * TabPanel
18506  * 
18507  */
18508
18509 /**
18510  * @class Roo.bootstrap.TabPanel
18511  * @extends Roo.bootstrap.Component
18512  * Bootstrap TabPanel class
18513  * @cfg {Boolean} active panel active
18514  * @cfg {String} html panel content
18515  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18516  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18517  * @cfg {String} href click to link..
18518  * 
18519  * 
18520  * @constructor
18521  * Create a new TabPanel
18522  * @param {Object} config The config object
18523  */
18524
18525 Roo.bootstrap.TabPanel = function(config){
18526     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18527     this.addEvents({
18528         /**
18529              * @event changed
18530              * Fires when the active status changes
18531              * @param {Roo.bootstrap.TabPanel} this
18532              * @param {Boolean} state the new state
18533             
18534          */
18535         'changed': true,
18536         /**
18537              * @event beforedeactivate
18538              * Fires before a tab is de-activated - can be used to do validation on a form.
18539              * @param {Roo.bootstrap.TabPanel} this
18540              * @return {Boolean} false if there is an error
18541             
18542          */
18543         'beforedeactivate': true
18544      });
18545     
18546     this.tabId = this.tabId || Roo.id();
18547   
18548 };
18549
18550 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18551     
18552     active: false,
18553     html: false,
18554     tabId: false,
18555     navId : false,
18556     href : '',
18557     
18558     getAutoCreate : function(){
18559         var cfg = {
18560             tag: 'div',
18561             // item is needed for carousel - not sure if it has any effect otherwise
18562             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18563             html: this.html || ''
18564         };
18565         
18566         if(this.active){
18567             cfg.cls += ' active';
18568         }
18569         
18570         if(this.tabId){
18571             cfg.tabId = this.tabId;
18572         }
18573         
18574         
18575         return cfg;
18576     },
18577     
18578     initEvents:  function()
18579     {
18580         var p = this.parent();
18581         
18582         this.navId = this.navId || p.navId;
18583         
18584         if (typeof(this.navId) != 'undefined') {
18585             // not really needed.. but just in case.. parent should be a NavGroup.
18586             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18587             
18588             tg.register(this);
18589             
18590             var i = tg.tabs.length - 1;
18591             
18592             if(this.active && tg.bullets > 0 && i < tg.bullets){
18593                 tg.setActiveBullet(i);
18594             }
18595         }
18596         
18597         this.el.on('click', this.onClick, this);
18598         
18599         if(Roo.isTouch){
18600             this.el.on("touchstart", this.onTouchStart, this);
18601             this.el.on("touchmove", this.onTouchMove, this);
18602             this.el.on("touchend", this.onTouchEnd, this);
18603         }
18604         
18605     },
18606     
18607     onRender : function(ct, position)
18608     {
18609         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18610     },
18611     
18612     setActive : function(state)
18613     {
18614         Roo.log("panel - set active " + this.tabId + "=" + state);
18615         
18616         this.active = state;
18617         if (!state) {
18618             this.el.removeClass('active');
18619             
18620         } else  if (!this.el.hasClass('active')) {
18621             this.el.addClass('active');
18622         }
18623         
18624         this.fireEvent('changed', this, state);
18625     },
18626     
18627     onClick : function(e)
18628     {
18629         e.preventDefault();
18630         
18631         if(!this.href.length){
18632             return;
18633         }
18634         
18635         window.location.href = this.href;
18636     },
18637     
18638     startX : 0,
18639     startY : 0,
18640     endX : 0,
18641     endY : 0,
18642     swiping : false,
18643     
18644     onTouchStart : function(e)
18645     {
18646         this.swiping = false;
18647         
18648         this.startX = e.browserEvent.touches[0].clientX;
18649         this.startY = e.browserEvent.touches[0].clientY;
18650     },
18651     
18652     onTouchMove : function(e)
18653     {
18654         this.swiping = true;
18655         
18656         this.endX = e.browserEvent.touches[0].clientX;
18657         this.endY = e.browserEvent.touches[0].clientY;
18658     },
18659     
18660     onTouchEnd : function(e)
18661     {
18662         if(!this.swiping){
18663             this.onClick(e);
18664             return;
18665         }
18666         
18667         var tabGroup = this.parent();
18668         
18669         if(this.endX > this.startX){ // swiping right
18670             tabGroup.showPanelPrev();
18671             return;
18672         }
18673         
18674         if(this.startX > this.endX){ // swiping left
18675             tabGroup.showPanelNext();
18676             return;
18677         }
18678     }
18679     
18680     
18681 });
18682  
18683
18684  
18685
18686  /*
18687  * - LGPL
18688  *
18689  * DateField
18690  * 
18691  */
18692
18693 /**
18694  * @class Roo.bootstrap.DateField
18695  * @extends Roo.bootstrap.Input
18696  * Bootstrap DateField class
18697  * @cfg {Number} weekStart default 0
18698  * @cfg {String} viewMode default empty, (months|years)
18699  * @cfg {String} minViewMode default empty, (months|years)
18700  * @cfg {Number} startDate default -Infinity
18701  * @cfg {Number} endDate default Infinity
18702  * @cfg {Boolean} todayHighlight default false
18703  * @cfg {Boolean} todayBtn default false
18704  * @cfg {Boolean} calendarWeeks default false
18705  * @cfg {Object} daysOfWeekDisabled default empty
18706  * @cfg {Boolean} singleMode default false (true | false)
18707  * 
18708  * @cfg {Boolean} keyboardNavigation default true
18709  * @cfg {String} language default en
18710  * 
18711  * @constructor
18712  * Create a new DateField
18713  * @param {Object} config The config object
18714  */
18715
18716 Roo.bootstrap.DateField = function(config){
18717     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18718      this.addEvents({
18719             /**
18720              * @event show
18721              * Fires when this field show.
18722              * @param {Roo.bootstrap.DateField} this
18723              * @param {Mixed} date The date value
18724              */
18725             show : true,
18726             /**
18727              * @event show
18728              * Fires when this field hide.
18729              * @param {Roo.bootstrap.DateField} this
18730              * @param {Mixed} date The date value
18731              */
18732             hide : true,
18733             /**
18734              * @event select
18735              * Fires when select a date.
18736              * @param {Roo.bootstrap.DateField} this
18737              * @param {Mixed} date The date value
18738              */
18739             select : true,
18740             /**
18741              * @event beforeselect
18742              * Fires when before select a date.
18743              * @param {Roo.bootstrap.DateField} this
18744              * @param {Mixed} date The date value
18745              */
18746             beforeselect : true
18747         });
18748 };
18749
18750 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18751     
18752     /**
18753      * @cfg {String} format
18754      * The default date format string which can be overriden for localization support.  The format must be
18755      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18756      */
18757     format : "m/d/y",
18758     /**
18759      * @cfg {String} altFormats
18760      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18761      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18762      */
18763     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18764     
18765     weekStart : 0,
18766     
18767     viewMode : '',
18768     
18769     minViewMode : '',
18770     
18771     todayHighlight : false,
18772     
18773     todayBtn: false,
18774     
18775     language: 'en',
18776     
18777     keyboardNavigation: true,
18778     
18779     calendarWeeks: false,
18780     
18781     startDate: -Infinity,
18782     
18783     endDate: Infinity,
18784     
18785     daysOfWeekDisabled: [],
18786     
18787     _events: [],
18788     
18789     singleMode : false,
18790     
18791     UTCDate: function()
18792     {
18793         return new Date(Date.UTC.apply(Date, arguments));
18794     },
18795     
18796     UTCToday: function()
18797     {
18798         var today = new Date();
18799         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18800     },
18801     
18802     getDate: function() {
18803             var d = this.getUTCDate();
18804             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18805     },
18806     
18807     getUTCDate: function() {
18808             return this.date;
18809     },
18810     
18811     setDate: function(d) {
18812             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18813     },
18814     
18815     setUTCDate: function(d) {
18816             this.date = d;
18817             this.setValue(this.formatDate(this.date));
18818     },
18819         
18820     onRender: function(ct, position)
18821     {
18822         
18823         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18824         
18825         this.language = this.language || 'en';
18826         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18827         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18828         
18829         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18830         this.format = this.format || 'm/d/y';
18831         this.isInline = false;
18832         this.isInput = true;
18833         this.component = this.el.select('.add-on', true).first() || false;
18834         this.component = (this.component && this.component.length === 0) ? false : this.component;
18835         this.hasInput = this.component && this.inputEl().length;
18836         
18837         if (typeof(this.minViewMode === 'string')) {
18838             switch (this.minViewMode) {
18839                 case 'months':
18840                     this.minViewMode = 1;
18841                     break;
18842                 case 'years':
18843                     this.minViewMode = 2;
18844                     break;
18845                 default:
18846                     this.minViewMode = 0;
18847                     break;
18848             }
18849         }
18850         
18851         if (typeof(this.viewMode === 'string')) {
18852             switch (this.viewMode) {
18853                 case 'months':
18854                     this.viewMode = 1;
18855                     break;
18856                 case 'years':
18857                     this.viewMode = 2;
18858                     break;
18859                 default:
18860                     this.viewMode = 0;
18861                     break;
18862             }
18863         }
18864                 
18865         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18866         
18867 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18868         
18869         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18870         
18871         this.picker().on('mousedown', this.onMousedown, this);
18872         this.picker().on('click', this.onClick, this);
18873         
18874         this.picker().addClass('datepicker-dropdown');
18875         
18876         this.startViewMode = this.viewMode;
18877         
18878         if(this.singleMode){
18879             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18880                 v.setVisibilityMode(Roo.Element.DISPLAY);
18881                 v.hide();
18882             });
18883             
18884             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18885                 v.setStyle('width', '189px');
18886             });
18887         }
18888         
18889         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18890             if(!this.calendarWeeks){
18891                 v.remove();
18892                 return;
18893             }
18894             
18895             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18896             v.attr('colspan', function(i, val){
18897                 return parseInt(val) + 1;
18898             });
18899         });
18900                         
18901         
18902         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18903         
18904         this.setStartDate(this.startDate);
18905         this.setEndDate(this.endDate);
18906         
18907         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18908         
18909         this.fillDow();
18910         this.fillMonths();
18911         this.update();
18912         this.showMode();
18913         
18914         if(this.isInline) {
18915             this.showPopup();
18916         }
18917     },
18918     
18919     picker : function()
18920     {
18921         return this.pickerEl;
18922 //        return this.el.select('.datepicker', true).first();
18923     },
18924     
18925     fillDow: function()
18926     {
18927         var dowCnt = this.weekStart;
18928         
18929         var dow = {
18930             tag: 'tr',
18931             cn: [
18932                 
18933             ]
18934         };
18935         
18936         if(this.calendarWeeks){
18937             dow.cn.push({
18938                 tag: 'th',
18939                 cls: 'cw',
18940                 html: '&nbsp;'
18941             })
18942         }
18943         
18944         while (dowCnt < this.weekStart + 7) {
18945             dow.cn.push({
18946                 tag: 'th',
18947                 cls: 'dow',
18948                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18949             });
18950         }
18951         
18952         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18953     },
18954     
18955     fillMonths: function()
18956     {    
18957         var i = 0;
18958         var months = this.picker().select('>.datepicker-months td', true).first();
18959         
18960         months.dom.innerHTML = '';
18961         
18962         while (i < 12) {
18963             var month = {
18964                 tag: 'span',
18965                 cls: 'month',
18966                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18967             };
18968             
18969             months.createChild(month);
18970         }
18971         
18972     },
18973     
18974     update: function()
18975     {
18976         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;
18977         
18978         if (this.date < this.startDate) {
18979             this.viewDate = new Date(this.startDate);
18980         } else if (this.date > this.endDate) {
18981             this.viewDate = new Date(this.endDate);
18982         } else {
18983             this.viewDate = new Date(this.date);
18984         }
18985         
18986         this.fill();
18987     },
18988     
18989     fill: function() 
18990     {
18991         var d = new Date(this.viewDate),
18992                 year = d.getUTCFullYear(),
18993                 month = d.getUTCMonth(),
18994                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18995                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18996                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18997                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18998                 currentDate = this.date && this.date.valueOf(),
18999                 today = this.UTCToday();
19000         
19001         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19002         
19003 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19004         
19005 //        this.picker.select('>tfoot th.today').
19006 //                                              .text(dates[this.language].today)
19007 //                                              .toggle(this.todayBtn !== false);
19008     
19009         this.updateNavArrows();
19010         this.fillMonths();
19011                                                 
19012         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19013         
19014         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19015          
19016         prevMonth.setUTCDate(day);
19017         
19018         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19019         
19020         var nextMonth = new Date(prevMonth);
19021         
19022         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19023         
19024         nextMonth = nextMonth.valueOf();
19025         
19026         var fillMonths = false;
19027         
19028         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19029         
19030         while(prevMonth.valueOf() <= nextMonth) {
19031             var clsName = '';
19032             
19033             if (prevMonth.getUTCDay() === this.weekStart) {
19034                 if(fillMonths){
19035                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19036                 }
19037                     
19038                 fillMonths = {
19039                     tag: 'tr',
19040                     cn: []
19041                 };
19042                 
19043                 if(this.calendarWeeks){
19044                     // ISO 8601: First week contains first thursday.
19045                     // ISO also states week starts on Monday, but we can be more abstract here.
19046                     var
19047                     // Start of current week: based on weekstart/current date
19048                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19049                     // Thursday of this week
19050                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19051                     // First Thursday of year, year from thursday
19052                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19053                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19054                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19055                     
19056                     fillMonths.cn.push({
19057                         tag: 'td',
19058                         cls: 'cw',
19059                         html: calWeek
19060                     });
19061                 }
19062             }
19063             
19064             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19065                 clsName += ' old';
19066             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19067                 clsName += ' new';
19068             }
19069             if (this.todayHighlight &&
19070                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19071                 prevMonth.getUTCMonth() == today.getMonth() &&
19072                 prevMonth.getUTCDate() == today.getDate()) {
19073                 clsName += ' today';
19074             }
19075             
19076             if (currentDate && prevMonth.valueOf() === currentDate) {
19077                 clsName += ' active';
19078             }
19079             
19080             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19081                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19082                     clsName += ' disabled';
19083             }
19084             
19085             fillMonths.cn.push({
19086                 tag: 'td',
19087                 cls: 'day ' + clsName,
19088                 html: prevMonth.getDate()
19089             });
19090             
19091             prevMonth.setDate(prevMonth.getDate()+1);
19092         }
19093           
19094         var currentYear = this.date && this.date.getUTCFullYear();
19095         var currentMonth = this.date && this.date.getUTCMonth();
19096         
19097         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19098         
19099         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19100             v.removeClass('active');
19101             
19102             if(currentYear === year && k === currentMonth){
19103                 v.addClass('active');
19104             }
19105             
19106             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19107                 v.addClass('disabled');
19108             }
19109             
19110         });
19111         
19112         
19113         year = parseInt(year/10, 10) * 10;
19114         
19115         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19116         
19117         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19118         
19119         year -= 1;
19120         for (var i = -1; i < 11; i++) {
19121             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19122                 tag: 'span',
19123                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19124                 html: year
19125             });
19126             
19127             year += 1;
19128         }
19129     },
19130     
19131     showMode: function(dir) 
19132     {
19133         if (dir) {
19134             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19135         }
19136         
19137         Roo.each(this.picker().select('>div',true).elements, function(v){
19138             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19139             v.hide();
19140         });
19141         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19142     },
19143     
19144     place: function()
19145     {
19146         if(this.isInline) {
19147             return;
19148         }
19149         
19150         this.picker().removeClass(['bottom', 'top']);
19151         
19152         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19153             /*
19154              * place to the top of element!
19155              *
19156              */
19157             
19158             this.picker().addClass('top');
19159             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19160             
19161             return;
19162         }
19163         
19164         this.picker().addClass('bottom');
19165         
19166         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19167     },
19168     
19169     parseDate : function(value)
19170     {
19171         if(!value || value instanceof Date){
19172             return value;
19173         }
19174         var v = Date.parseDate(value, this.format);
19175         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19176             v = Date.parseDate(value, 'Y-m-d');
19177         }
19178         if(!v && this.altFormats){
19179             if(!this.altFormatsArray){
19180                 this.altFormatsArray = this.altFormats.split("|");
19181             }
19182             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19183                 v = Date.parseDate(value, this.altFormatsArray[i]);
19184             }
19185         }
19186         return v;
19187     },
19188     
19189     formatDate : function(date, fmt)
19190     {   
19191         return (!date || !(date instanceof Date)) ?
19192         date : date.dateFormat(fmt || this.format);
19193     },
19194     
19195     onFocus : function()
19196     {
19197         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19198         this.showPopup();
19199     },
19200     
19201     onBlur : function()
19202     {
19203         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19204         
19205         var d = this.inputEl().getValue();
19206         
19207         this.setValue(d);
19208                 
19209         this.hidePopup();
19210     },
19211     
19212     showPopup : function()
19213     {
19214         this.picker().show();
19215         this.update();
19216         this.place();
19217         
19218         this.fireEvent('showpopup', this, this.date);
19219     },
19220     
19221     hidePopup : function()
19222     {
19223         if(this.isInline) {
19224             return;
19225         }
19226         this.picker().hide();
19227         this.viewMode = this.startViewMode;
19228         this.showMode();
19229         
19230         this.fireEvent('hidepopup', this, this.date);
19231         
19232     },
19233     
19234     onMousedown: function(e)
19235     {
19236         e.stopPropagation();
19237         e.preventDefault();
19238     },
19239     
19240     keyup: function(e)
19241     {
19242         Roo.bootstrap.DateField.superclass.keyup.call(this);
19243         this.update();
19244     },
19245
19246     setValue: function(v)
19247     {
19248         if(this.fireEvent('beforeselect', this, v) !== false){
19249             var d = new Date(this.parseDate(v) ).clearTime();
19250         
19251             if(isNaN(d.getTime())){
19252                 this.date = this.viewDate = '';
19253                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19254                 return;
19255             }
19256
19257             v = this.formatDate(d);
19258
19259             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19260
19261             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19262
19263             this.update();
19264
19265             this.fireEvent('select', this, this.date);
19266         }
19267     },
19268     
19269     getValue: function()
19270     {
19271         return this.formatDate(this.date);
19272     },
19273     
19274     fireKey: function(e)
19275     {
19276         if (!this.picker().isVisible()){
19277             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19278                 this.showPopup();
19279             }
19280             return;
19281         }
19282         
19283         var dateChanged = false,
19284         dir, day, month,
19285         newDate, newViewDate;
19286         
19287         switch(e.keyCode){
19288             case 27: // escape
19289                 this.hidePopup();
19290                 e.preventDefault();
19291                 break;
19292             case 37: // left
19293             case 39: // right
19294                 if (!this.keyboardNavigation) {
19295                     break;
19296                 }
19297                 dir = e.keyCode == 37 ? -1 : 1;
19298                 
19299                 if (e.ctrlKey){
19300                     newDate = this.moveYear(this.date, dir);
19301                     newViewDate = this.moveYear(this.viewDate, dir);
19302                 } else if (e.shiftKey){
19303                     newDate = this.moveMonth(this.date, dir);
19304                     newViewDate = this.moveMonth(this.viewDate, dir);
19305                 } else {
19306                     newDate = new Date(this.date);
19307                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19308                     newViewDate = new Date(this.viewDate);
19309                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19310                 }
19311                 if (this.dateWithinRange(newDate)){
19312                     this.date = newDate;
19313                     this.viewDate = newViewDate;
19314                     this.setValue(this.formatDate(this.date));
19315 //                    this.update();
19316                     e.preventDefault();
19317                     dateChanged = true;
19318                 }
19319                 break;
19320             case 38: // up
19321             case 40: // down
19322                 if (!this.keyboardNavigation) {
19323                     break;
19324                 }
19325                 dir = e.keyCode == 38 ? -1 : 1;
19326                 if (e.ctrlKey){
19327                     newDate = this.moveYear(this.date, dir);
19328                     newViewDate = this.moveYear(this.viewDate, dir);
19329                 } else if (e.shiftKey){
19330                     newDate = this.moveMonth(this.date, dir);
19331                     newViewDate = this.moveMonth(this.viewDate, dir);
19332                 } else {
19333                     newDate = new Date(this.date);
19334                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19335                     newViewDate = new Date(this.viewDate);
19336                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19337                 }
19338                 if (this.dateWithinRange(newDate)){
19339                     this.date = newDate;
19340                     this.viewDate = newViewDate;
19341                     this.setValue(this.formatDate(this.date));
19342 //                    this.update();
19343                     e.preventDefault();
19344                     dateChanged = true;
19345                 }
19346                 break;
19347             case 13: // enter
19348                 this.setValue(this.formatDate(this.date));
19349                 this.hidePopup();
19350                 e.preventDefault();
19351                 break;
19352             case 9: // tab
19353                 this.setValue(this.formatDate(this.date));
19354                 this.hidePopup();
19355                 break;
19356             case 16: // shift
19357             case 17: // ctrl
19358             case 18: // alt
19359                 break;
19360             default :
19361                 this.hidePopup();
19362                 
19363         }
19364     },
19365     
19366     
19367     onClick: function(e) 
19368     {
19369         e.stopPropagation();
19370         e.preventDefault();
19371         
19372         var target = e.getTarget();
19373         
19374         if(target.nodeName.toLowerCase() === 'i'){
19375             target = Roo.get(target).dom.parentNode;
19376         }
19377         
19378         var nodeName = target.nodeName;
19379         var className = target.className;
19380         var html = target.innerHTML;
19381         //Roo.log(nodeName);
19382         
19383         switch(nodeName.toLowerCase()) {
19384             case 'th':
19385                 switch(className) {
19386                     case 'switch':
19387                         this.showMode(1);
19388                         break;
19389                     case 'prev':
19390                     case 'next':
19391                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19392                         switch(this.viewMode){
19393                                 case 0:
19394                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19395                                         break;
19396                                 case 1:
19397                                 case 2:
19398                                         this.viewDate = this.moveYear(this.viewDate, dir);
19399                                         break;
19400                         }
19401                         this.fill();
19402                         break;
19403                     case 'today':
19404                         var date = new Date();
19405                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19406 //                        this.fill()
19407                         this.setValue(this.formatDate(this.date));
19408                         
19409                         this.hidePopup();
19410                         break;
19411                 }
19412                 break;
19413             case 'span':
19414                 if (className.indexOf('disabled') < 0) {
19415                     this.viewDate.setUTCDate(1);
19416                     if (className.indexOf('month') > -1) {
19417                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19418                     } else {
19419                         var year = parseInt(html, 10) || 0;
19420                         this.viewDate.setUTCFullYear(year);
19421                         
19422                     }
19423                     
19424                     if(this.singleMode){
19425                         this.setValue(this.formatDate(this.viewDate));
19426                         this.hidePopup();
19427                         return;
19428                     }
19429                     
19430                     this.showMode(-1);
19431                     this.fill();
19432                 }
19433                 break;
19434                 
19435             case 'td':
19436                 //Roo.log(className);
19437                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19438                     var day = parseInt(html, 10) || 1;
19439                     var year = this.viewDate.getUTCFullYear(),
19440                         month = this.viewDate.getUTCMonth();
19441
19442                     if (className.indexOf('old') > -1) {
19443                         if(month === 0 ){
19444                             month = 11;
19445                             year -= 1;
19446                         }else{
19447                             month -= 1;
19448                         }
19449                     } else if (className.indexOf('new') > -1) {
19450                         if (month == 11) {
19451                             month = 0;
19452                             year += 1;
19453                         } else {
19454                             month += 1;
19455                         }
19456                     }
19457                     //Roo.log([year,month,day]);
19458                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19459                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19460 //                    this.fill();
19461                     //Roo.log(this.formatDate(this.date));
19462                     this.setValue(this.formatDate(this.date));
19463                     this.hidePopup();
19464                 }
19465                 break;
19466         }
19467     },
19468     
19469     setStartDate: function(startDate)
19470     {
19471         this.startDate = startDate || -Infinity;
19472         if (this.startDate !== -Infinity) {
19473             this.startDate = this.parseDate(this.startDate);
19474         }
19475         this.update();
19476         this.updateNavArrows();
19477     },
19478
19479     setEndDate: function(endDate)
19480     {
19481         this.endDate = endDate || Infinity;
19482         if (this.endDate !== Infinity) {
19483             this.endDate = this.parseDate(this.endDate);
19484         }
19485         this.update();
19486         this.updateNavArrows();
19487     },
19488     
19489     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19490     {
19491         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19492         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19493             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19494         }
19495         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19496             return parseInt(d, 10);
19497         });
19498         this.update();
19499         this.updateNavArrows();
19500     },
19501     
19502     updateNavArrows: function() 
19503     {
19504         if(this.singleMode){
19505             return;
19506         }
19507         
19508         var d = new Date(this.viewDate),
19509         year = d.getUTCFullYear(),
19510         month = d.getUTCMonth();
19511         
19512         Roo.each(this.picker().select('.prev', true).elements, function(v){
19513             v.show();
19514             switch (this.viewMode) {
19515                 case 0:
19516
19517                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19518                         v.hide();
19519                     }
19520                     break;
19521                 case 1:
19522                 case 2:
19523                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19524                         v.hide();
19525                     }
19526                     break;
19527             }
19528         });
19529         
19530         Roo.each(this.picker().select('.next', true).elements, function(v){
19531             v.show();
19532             switch (this.viewMode) {
19533                 case 0:
19534
19535                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19536                         v.hide();
19537                     }
19538                     break;
19539                 case 1:
19540                 case 2:
19541                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19542                         v.hide();
19543                     }
19544                     break;
19545             }
19546         })
19547     },
19548     
19549     moveMonth: function(date, dir)
19550     {
19551         if (!dir) {
19552             return date;
19553         }
19554         var new_date = new Date(date.valueOf()),
19555         day = new_date.getUTCDate(),
19556         month = new_date.getUTCMonth(),
19557         mag = Math.abs(dir),
19558         new_month, test;
19559         dir = dir > 0 ? 1 : -1;
19560         if (mag == 1){
19561             test = dir == -1
19562             // If going back one month, make sure month is not current month
19563             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19564             ? function(){
19565                 return new_date.getUTCMonth() == month;
19566             }
19567             // If going forward one month, make sure month is as expected
19568             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19569             : function(){
19570                 return new_date.getUTCMonth() != new_month;
19571             };
19572             new_month = month + dir;
19573             new_date.setUTCMonth(new_month);
19574             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19575             if (new_month < 0 || new_month > 11) {
19576                 new_month = (new_month + 12) % 12;
19577             }
19578         } else {
19579             // For magnitudes >1, move one month at a time...
19580             for (var i=0; i<mag; i++) {
19581                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19582                 new_date = this.moveMonth(new_date, dir);
19583             }
19584             // ...then reset the day, keeping it in the new month
19585             new_month = new_date.getUTCMonth();
19586             new_date.setUTCDate(day);
19587             test = function(){
19588                 return new_month != new_date.getUTCMonth();
19589             };
19590         }
19591         // Common date-resetting loop -- if date is beyond end of month, make it
19592         // end of month
19593         while (test()){
19594             new_date.setUTCDate(--day);
19595             new_date.setUTCMonth(new_month);
19596         }
19597         return new_date;
19598     },
19599
19600     moveYear: function(date, dir)
19601     {
19602         return this.moveMonth(date, dir*12);
19603     },
19604
19605     dateWithinRange: function(date)
19606     {
19607         return date >= this.startDate && date <= this.endDate;
19608     },
19609
19610     
19611     remove: function() 
19612     {
19613         this.picker().remove();
19614     },
19615     
19616     validateValue : function(value)
19617     {
19618         if(this.getVisibilityEl().hasClass('hidden')){
19619             return true;
19620         }
19621         
19622         if(value.length < 1)  {
19623             if(this.allowBlank){
19624                 return true;
19625             }
19626             return false;
19627         }
19628         
19629         if(value.length < this.minLength){
19630             return false;
19631         }
19632         if(value.length > this.maxLength){
19633             return false;
19634         }
19635         if(this.vtype){
19636             var vt = Roo.form.VTypes;
19637             if(!vt[this.vtype](value, this)){
19638                 return false;
19639             }
19640         }
19641         if(typeof this.validator == "function"){
19642             var msg = this.validator(value);
19643             if(msg !== true){
19644                 return false;
19645             }
19646         }
19647         
19648         if(this.regex && !this.regex.test(value)){
19649             return false;
19650         }
19651         
19652         if(typeof(this.parseDate(value)) == 'undefined'){
19653             return false;
19654         }
19655         
19656         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19657             return false;
19658         }      
19659         
19660         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19661             return false;
19662         } 
19663         
19664         
19665         return true;
19666     },
19667     
19668     reset : function()
19669     {
19670         this.date = this.viewDate = '';
19671         
19672         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19673     }
19674    
19675 });
19676
19677 Roo.apply(Roo.bootstrap.DateField,  {
19678     
19679     head : {
19680         tag: 'thead',
19681         cn: [
19682         {
19683             tag: 'tr',
19684             cn: [
19685             {
19686                 tag: 'th',
19687                 cls: 'prev',
19688                 html: '<i class="fa fa-arrow-left"/>'
19689             },
19690             {
19691                 tag: 'th',
19692                 cls: 'switch',
19693                 colspan: '5'
19694             },
19695             {
19696                 tag: 'th',
19697                 cls: 'next',
19698                 html: '<i class="fa fa-arrow-right"/>'
19699             }
19700
19701             ]
19702         }
19703         ]
19704     },
19705     
19706     content : {
19707         tag: 'tbody',
19708         cn: [
19709         {
19710             tag: 'tr',
19711             cn: [
19712             {
19713                 tag: 'td',
19714                 colspan: '7'
19715             }
19716             ]
19717         }
19718         ]
19719     },
19720     
19721     footer : {
19722         tag: 'tfoot',
19723         cn: [
19724         {
19725             tag: 'tr',
19726             cn: [
19727             {
19728                 tag: 'th',
19729                 colspan: '7',
19730                 cls: 'today'
19731             }
19732                     
19733             ]
19734         }
19735         ]
19736     },
19737     
19738     dates:{
19739         en: {
19740             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19741             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19742             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19743             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19744             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19745             today: "Today"
19746         }
19747     },
19748     
19749     modes: [
19750     {
19751         clsName: 'days',
19752         navFnc: 'Month',
19753         navStep: 1
19754     },
19755     {
19756         clsName: 'months',
19757         navFnc: 'FullYear',
19758         navStep: 1
19759     },
19760     {
19761         clsName: 'years',
19762         navFnc: 'FullYear',
19763         navStep: 10
19764     }]
19765 });
19766
19767 Roo.apply(Roo.bootstrap.DateField,  {
19768   
19769     template : {
19770         tag: 'div',
19771         cls: 'datepicker dropdown-menu roo-dynamic',
19772         cn: [
19773         {
19774             tag: 'div',
19775             cls: 'datepicker-days',
19776             cn: [
19777             {
19778                 tag: 'table',
19779                 cls: 'table-condensed',
19780                 cn:[
19781                 Roo.bootstrap.DateField.head,
19782                 {
19783                     tag: 'tbody'
19784                 },
19785                 Roo.bootstrap.DateField.footer
19786                 ]
19787             }
19788             ]
19789         },
19790         {
19791             tag: 'div',
19792             cls: 'datepicker-months',
19793             cn: [
19794             {
19795                 tag: 'table',
19796                 cls: 'table-condensed',
19797                 cn:[
19798                 Roo.bootstrap.DateField.head,
19799                 Roo.bootstrap.DateField.content,
19800                 Roo.bootstrap.DateField.footer
19801                 ]
19802             }
19803             ]
19804         },
19805         {
19806             tag: 'div',
19807             cls: 'datepicker-years',
19808             cn: [
19809             {
19810                 tag: 'table',
19811                 cls: 'table-condensed',
19812                 cn:[
19813                 Roo.bootstrap.DateField.head,
19814                 Roo.bootstrap.DateField.content,
19815                 Roo.bootstrap.DateField.footer
19816                 ]
19817             }
19818             ]
19819         }
19820         ]
19821     }
19822 });
19823
19824  
19825
19826  /*
19827  * - LGPL
19828  *
19829  * TimeField
19830  * 
19831  */
19832
19833 /**
19834  * @class Roo.bootstrap.TimeField
19835  * @extends Roo.bootstrap.Input
19836  * Bootstrap DateField class
19837  * 
19838  * 
19839  * @constructor
19840  * Create a new TimeField
19841  * @param {Object} config The config object
19842  */
19843
19844 Roo.bootstrap.TimeField = function(config){
19845     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19846     this.addEvents({
19847             /**
19848              * @event show
19849              * Fires when this field show.
19850              * @param {Roo.bootstrap.DateField} thisthis
19851              * @param {Mixed} date The date value
19852              */
19853             show : true,
19854             /**
19855              * @event show
19856              * Fires when this field hide.
19857              * @param {Roo.bootstrap.DateField} this
19858              * @param {Mixed} date The date value
19859              */
19860             hide : true,
19861             /**
19862              * @event select
19863              * Fires when select a date.
19864              * @param {Roo.bootstrap.DateField} this
19865              * @param {Mixed} date The date value
19866              */
19867             select : true
19868         });
19869 };
19870
19871 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19872     
19873     /**
19874      * @cfg {String} format
19875      * The default time format string which can be overriden for localization support.  The format must be
19876      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19877      */
19878     format : "H:i",
19879        
19880     onRender: function(ct, position)
19881     {
19882         
19883         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19884                 
19885         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19886         
19887         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19888         
19889         this.pop = this.picker().select('>.datepicker-time',true).first();
19890         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19891         
19892         this.picker().on('mousedown', this.onMousedown, this);
19893         this.picker().on('click', this.onClick, this);
19894         
19895         this.picker().addClass('datepicker-dropdown');
19896     
19897         this.fillTime();
19898         this.update();
19899             
19900         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19901         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19902         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19903         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19904         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19905         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19906
19907     },
19908     
19909     fireKey: function(e){
19910         if (!this.picker().isVisible()){
19911             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19912                 this.show();
19913             }
19914             return;
19915         }
19916
19917         e.preventDefault();
19918         
19919         switch(e.keyCode){
19920             case 27: // escape
19921                 this.hide();
19922                 break;
19923             case 37: // left
19924             case 39: // right
19925                 this.onTogglePeriod();
19926                 break;
19927             case 38: // up
19928                 this.onIncrementMinutes();
19929                 break;
19930             case 40: // down
19931                 this.onDecrementMinutes();
19932                 break;
19933             case 13: // enter
19934             case 9: // tab
19935                 this.setTime();
19936                 break;
19937         }
19938     },
19939     
19940     onClick: function(e) {
19941         e.stopPropagation();
19942         e.preventDefault();
19943     },
19944     
19945     picker : function()
19946     {
19947         return this.el.select('.datepicker', true).first();
19948     },
19949     
19950     fillTime: function()
19951     {    
19952         var time = this.pop.select('tbody', true).first();
19953         
19954         time.dom.innerHTML = '';
19955         
19956         time.createChild({
19957             tag: 'tr',
19958             cn: [
19959                 {
19960                     tag: 'td',
19961                     cn: [
19962                         {
19963                             tag: 'a',
19964                             href: '#',
19965                             cls: 'btn',
19966                             cn: [
19967                                 {
19968                                     tag: 'span',
19969                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19970                                 }
19971                             ]
19972                         } 
19973                     ]
19974                 },
19975                 {
19976                     tag: 'td',
19977                     cls: 'separator'
19978                 },
19979                 {
19980                     tag: 'td',
19981                     cn: [
19982                         {
19983                             tag: 'a',
19984                             href: '#',
19985                             cls: 'btn',
19986                             cn: [
19987                                 {
19988                                     tag: 'span',
19989                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19990                                 }
19991                             ]
19992                         }
19993                     ]
19994                 },
19995                 {
19996                     tag: 'td',
19997                     cls: 'separator'
19998                 }
19999             ]
20000         });
20001         
20002         time.createChild({
20003             tag: 'tr',
20004             cn: [
20005                 {
20006                     tag: 'td',
20007                     cn: [
20008                         {
20009                             tag: 'span',
20010                             cls: 'timepicker-hour',
20011                             html: '00'
20012                         }  
20013                     ]
20014                 },
20015                 {
20016                     tag: 'td',
20017                     cls: 'separator',
20018                     html: ':'
20019                 },
20020                 {
20021                     tag: 'td',
20022                     cn: [
20023                         {
20024                             tag: 'span',
20025                             cls: 'timepicker-minute',
20026                             html: '00'
20027                         }  
20028                     ]
20029                 },
20030                 {
20031                     tag: 'td',
20032                     cls: 'separator'
20033                 },
20034                 {
20035                     tag: 'td',
20036                     cn: [
20037                         {
20038                             tag: 'button',
20039                             type: 'button',
20040                             cls: 'btn btn-primary period',
20041                             html: 'AM'
20042                             
20043                         }
20044                     ]
20045                 }
20046             ]
20047         });
20048         
20049         time.createChild({
20050             tag: 'tr',
20051             cn: [
20052                 {
20053                     tag: 'td',
20054                     cn: [
20055                         {
20056                             tag: 'a',
20057                             href: '#',
20058                             cls: 'btn',
20059                             cn: [
20060                                 {
20061                                     tag: 'span',
20062                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20063                                 }
20064                             ]
20065                         }
20066                     ]
20067                 },
20068                 {
20069                     tag: 'td',
20070                     cls: 'separator'
20071                 },
20072                 {
20073                     tag: 'td',
20074                     cn: [
20075                         {
20076                             tag: 'a',
20077                             href: '#',
20078                             cls: 'btn',
20079                             cn: [
20080                                 {
20081                                     tag: 'span',
20082                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20083                                 }
20084                             ]
20085                         }
20086                     ]
20087                 },
20088                 {
20089                     tag: 'td',
20090                     cls: 'separator'
20091                 }
20092             ]
20093         });
20094         
20095     },
20096     
20097     update: function()
20098     {
20099         
20100         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20101         
20102         this.fill();
20103     },
20104     
20105     fill: function() 
20106     {
20107         var hours = this.time.getHours();
20108         var minutes = this.time.getMinutes();
20109         var period = 'AM';
20110         
20111         if(hours > 11){
20112             period = 'PM';
20113         }
20114         
20115         if(hours == 0){
20116             hours = 12;
20117         }
20118         
20119         
20120         if(hours > 12){
20121             hours = hours - 12;
20122         }
20123         
20124         if(hours < 10){
20125             hours = '0' + hours;
20126         }
20127         
20128         if(minutes < 10){
20129             minutes = '0' + minutes;
20130         }
20131         
20132         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20133         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20134         this.pop.select('button', true).first().dom.innerHTML = period;
20135         
20136     },
20137     
20138     place: function()
20139     {   
20140         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20141         
20142         var cls = ['bottom'];
20143         
20144         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20145             cls.pop();
20146             cls.push('top');
20147         }
20148         
20149         cls.push('right');
20150         
20151         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20152             cls.pop();
20153             cls.push('left');
20154         }
20155         
20156         this.picker().addClass(cls.join('-'));
20157         
20158         var _this = this;
20159         
20160         Roo.each(cls, function(c){
20161             if(c == 'bottom'){
20162                 _this.picker().setTop(_this.inputEl().getHeight());
20163                 return;
20164             }
20165             if(c == 'top'){
20166                 _this.picker().setTop(0 - _this.picker().getHeight());
20167                 return;
20168             }
20169             
20170             if(c == 'left'){
20171                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20172                 return;
20173             }
20174             if(c == 'right'){
20175                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20176                 return;
20177             }
20178         });
20179         
20180     },
20181   
20182     onFocus : function()
20183     {
20184         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20185         this.show();
20186     },
20187     
20188     onBlur : function()
20189     {
20190         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20191         this.hide();
20192     },
20193     
20194     show : function()
20195     {
20196         this.picker().show();
20197         this.pop.show();
20198         this.update();
20199         this.place();
20200         
20201         this.fireEvent('show', this, this.date);
20202     },
20203     
20204     hide : function()
20205     {
20206         this.picker().hide();
20207         this.pop.hide();
20208         
20209         this.fireEvent('hide', this, this.date);
20210     },
20211     
20212     setTime : function()
20213     {
20214         this.hide();
20215         this.setValue(this.time.format(this.format));
20216         
20217         this.fireEvent('select', this, this.date);
20218         
20219         
20220     },
20221     
20222     onMousedown: function(e){
20223         e.stopPropagation();
20224         e.preventDefault();
20225     },
20226     
20227     onIncrementHours: function()
20228     {
20229         Roo.log('onIncrementHours');
20230         this.time = this.time.add(Date.HOUR, 1);
20231         this.update();
20232         
20233     },
20234     
20235     onDecrementHours: function()
20236     {
20237         Roo.log('onDecrementHours');
20238         this.time = this.time.add(Date.HOUR, -1);
20239         this.update();
20240     },
20241     
20242     onIncrementMinutes: function()
20243     {
20244         Roo.log('onIncrementMinutes');
20245         this.time = this.time.add(Date.MINUTE, 1);
20246         this.update();
20247     },
20248     
20249     onDecrementMinutes: function()
20250     {
20251         Roo.log('onDecrementMinutes');
20252         this.time = this.time.add(Date.MINUTE, -1);
20253         this.update();
20254     },
20255     
20256     onTogglePeriod: function()
20257     {
20258         Roo.log('onTogglePeriod');
20259         this.time = this.time.add(Date.HOUR, 12);
20260         this.update();
20261     }
20262     
20263    
20264 });
20265
20266 Roo.apply(Roo.bootstrap.TimeField,  {
20267     
20268     content : {
20269         tag: 'tbody',
20270         cn: [
20271             {
20272                 tag: 'tr',
20273                 cn: [
20274                 {
20275                     tag: 'td',
20276                     colspan: '7'
20277                 }
20278                 ]
20279             }
20280         ]
20281     },
20282     
20283     footer : {
20284         tag: 'tfoot',
20285         cn: [
20286             {
20287                 tag: 'tr',
20288                 cn: [
20289                 {
20290                     tag: 'th',
20291                     colspan: '7',
20292                     cls: '',
20293                     cn: [
20294                         {
20295                             tag: 'button',
20296                             cls: 'btn btn-info ok',
20297                             html: 'OK'
20298                         }
20299                     ]
20300                 }
20301
20302                 ]
20303             }
20304         ]
20305     }
20306 });
20307
20308 Roo.apply(Roo.bootstrap.TimeField,  {
20309   
20310     template : {
20311         tag: 'div',
20312         cls: 'datepicker dropdown-menu',
20313         cn: [
20314             {
20315                 tag: 'div',
20316                 cls: 'datepicker-time',
20317                 cn: [
20318                 {
20319                     tag: 'table',
20320                     cls: 'table-condensed',
20321                     cn:[
20322                     Roo.bootstrap.TimeField.content,
20323                     Roo.bootstrap.TimeField.footer
20324                     ]
20325                 }
20326                 ]
20327             }
20328         ]
20329     }
20330 });
20331
20332  
20333
20334  /*
20335  * - LGPL
20336  *
20337  * MonthField
20338  * 
20339  */
20340
20341 /**
20342  * @class Roo.bootstrap.MonthField
20343  * @extends Roo.bootstrap.Input
20344  * Bootstrap MonthField class
20345  * 
20346  * @cfg {String} language default en
20347  * 
20348  * @constructor
20349  * Create a new MonthField
20350  * @param {Object} config The config object
20351  */
20352
20353 Roo.bootstrap.MonthField = function(config){
20354     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20355     
20356     this.addEvents({
20357         /**
20358          * @event show
20359          * Fires when this field show.
20360          * @param {Roo.bootstrap.MonthField} this
20361          * @param {Mixed} date The date value
20362          */
20363         show : true,
20364         /**
20365          * @event show
20366          * Fires when this field hide.
20367          * @param {Roo.bootstrap.MonthField} this
20368          * @param {Mixed} date The date value
20369          */
20370         hide : true,
20371         /**
20372          * @event select
20373          * Fires when select a date.
20374          * @param {Roo.bootstrap.MonthField} this
20375          * @param {String} oldvalue The old value
20376          * @param {String} newvalue The new value
20377          */
20378         select : true
20379     });
20380 };
20381
20382 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20383     
20384     onRender: function(ct, position)
20385     {
20386         
20387         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20388         
20389         this.language = this.language || 'en';
20390         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20391         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20392         
20393         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20394         this.isInline = false;
20395         this.isInput = true;
20396         this.component = this.el.select('.add-on', true).first() || false;
20397         this.component = (this.component && this.component.length === 0) ? false : this.component;
20398         this.hasInput = this.component && this.inputEL().length;
20399         
20400         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20401         
20402         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20403         
20404         this.picker().on('mousedown', this.onMousedown, this);
20405         this.picker().on('click', this.onClick, this);
20406         
20407         this.picker().addClass('datepicker-dropdown');
20408         
20409         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20410             v.setStyle('width', '189px');
20411         });
20412         
20413         this.fillMonths();
20414         
20415         this.update();
20416         
20417         if(this.isInline) {
20418             this.show();
20419         }
20420         
20421     },
20422     
20423     setValue: function(v, suppressEvent)
20424     {   
20425         var o = this.getValue();
20426         
20427         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20428         
20429         this.update();
20430
20431         if(suppressEvent !== true){
20432             this.fireEvent('select', this, o, v);
20433         }
20434         
20435     },
20436     
20437     getValue: function()
20438     {
20439         return this.value;
20440     },
20441     
20442     onClick: function(e) 
20443     {
20444         e.stopPropagation();
20445         e.preventDefault();
20446         
20447         var target = e.getTarget();
20448         
20449         if(target.nodeName.toLowerCase() === 'i'){
20450             target = Roo.get(target).dom.parentNode;
20451         }
20452         
20453         var nodeName = target.nodeName;
20454         var className = target.className;
20455         var html = target.innerHTML;
20456         
20457         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20458             return;
20459         }
20460         
20461         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20462         
20463         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20464         
20465         this.hide();
20466                         
20467     },
20468     
20469     picker : function()
20470     {
20471         return this.pickerEl;
20472     },
20473     
20474     fillMonths: function()
20475     {    
20476         var i = 0;
20477         var months = this.picker().select('>.datepicker-months td', true).first();
20478         
20479         months.dom.innerHTML = '';
20480         
20481         while (i < 12) {
20482             var month = {
20483                 tag: 'span',
20484                 cls: 'month',
20485                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20486             };
20487             
20488             months.createChild(month);
20489         }
20490         
20491     },
20492     
20493     update: function()
20494     {
20495         var _this = this;
20496         
20497         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20498             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20499         }
20500         
20501         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20502             e.removeClass('active');
20503             
20504             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20505                 e.addClass('active');
20506             }
20507         })
20508     },
20509     
20510     place: function()
20511     {
20512         if(this.isInline) {
20513             return;
20514         }
20515         
20516         this.picker().removeClass(['bottom', 'top']);
20517         
20518         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20519             /*
20520              * place to the top of element!
20521              *
20522              */
20523             
20524             this.picker().addClass('top');
20525             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20526             
20527             return;
20528         }
20529         
20530         this.picker().addClass('bottom');
20531         
20532         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20533     },
20534     
20535     onFocus : function()
20536     {
20537         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20538         this.show();
20539     },
20540     
20541     onBlur : function()
20542     {
20543         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20544         
20545         var d = this.inputEl().getValue();
20546         
20547         this.setValue(d);
20548                 
20549         this.hide();
20550     },
20551     
20552     show : function()
20553     {
20554         this.picker().show();
20555         this.picker().select('>.datepicker-months', true).first().show();
20556         this.update();
20557         this.place();
20558         
20559         this.fireEvent('show', this, this.date);
20560     },
20561     
20562     hide : function()
20563     {
20564         if(this.isInline) {
20565             return;
20566         }
20567         this.picker().hide();
20568         this.fireEvent('hide', this, this.date);
20569         
20570     },
20571     
20572     onMousedown: function(e)
20573     {
20574         e.stopPropagation();
20575         e.preventDefault();
20576     },
20577     
20578     keyup: function(e)
20579     {
20580         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20581         this.update();
20582     },
20583
20584     fireKey: function(e)
20585     {
20586         if (!this.picker().isVisible()){
20587             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20588                 this.show();
20589             }
20590             return;
20591         }
20592         
20593         var dir;
20594         
20595         switch(e.keyCode){
20596             case 27: // escape
20597                 this.hide();
20598                 e.preventDefault();
20599                 break;
20600             case 37: // left
20601             case 39: // right
20602                 dir = e.keyCode == 37 ? -1 : 1;
20603                 
20604                 this.vIndex = this.vIndex + dir;
20605                 
20606                 if(this.vIndex < 0){
20607                     this.vIndex = 0;
20608                 }
20609                 
20610                 if(this.vIndex > 11){
20611                     this.vIndex = 11;
20612                 }
20613                 
20614                 if(isNaN(this.vIndex)){
20615                     this.vIndex = 0;
20616                 }
20617                 
20618                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20619                 
20620                 break;
20621             case 38: // up
20622             case 40: // down
20623                 
20624                 dir = e.keyCode == 38 ? -1 : 1;
20625                 
20626                 this.vIndex = this.vIndex + dir * 4;
20627                 
20628                 if(this.vIndex < 0){
20629                     this.vIndex = 0;
20630                 }
20631                 
20632                 if(this.vIndex > 11){
20633                     this.vIndex = 11;
20634                 }
20635                 
20636                 if(isNaN(this.vIndex)){
20637                     this.vIndex = 0;
20638                 }
20639                 
20640                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20641                 break;
20642                 
20643             case 13: // enter
20644                 
20645                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20646                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20647                 }
20648                 
20649                 this.hide();
20650                 e.preventDefault();
20651                 break;
20652             case 9: // tab
20653                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20654                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20655                 }
20656                 this.hide();
20657                 break;
20658             case 16: // shift
20659             case 17: // ctrl
20660             case 18: // alt
20661                 break;
20662             default :
20663                 this.hide();
20664                 
20665         }
20666     },
20667     
20668     remove: function() 
20669     {
20670         this.picker().remove();
20671     }
20672    
20673 });
20674
20675 Roo.apply(Roo.bootstrap.MonthField,  {
20676     
20677     content : {
20678         tag: 'tbody',
20679         cn: [
20680         {
20681             tag: 'tr',
20682             cn: [
20683             {
20684                 tag: 'td',
20685                 colspan: '7'
20686             }
20687             ]
20688         }
20689         ]
20690     },
20691     
20692     dates:{
20693         en: {
20694             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20695             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20696         }
20697     }
20698 });
20699
20700 Roo.apply(Roo.bootstrap.MonthField,  {
20701   
20702     template : {
20703         tag: 'div',
20704         cls: 'datepicker dropdown-menu roo-dynamic',
20705         cn: [
20706             {
20707                 tag: 'div',
20708                 cls: 'datepicker-months',
20709                 cn: [
20710                 {
20711                     tag: 'table',
20712                     cls: 'table-condensed',
20713                     cn:[
20714                         Roo.bootstrap.DateField.content
20715                     ]
20716                 }
20717                 ]
20718             }
20719         ]
20720     }
20721 });
20722
20723  
20724
20725  
20726  /*
20727  * - LGPL
20728  *
20729  * CheckBox
20730  * 
20731  */
20732
20733 /**
20734  * @class Roo.bootstrap.CheckBox
20735  * @extends Roo.bootstrap.Input
20736  * Bootstrap CheckBox class
20737  * 
20738  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20739  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20740  * @cfg {String} boxLabel The text that appears beside the checkbox
20741  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20742  * @cfg {Boolean} checked initnal the element
20743  * @cfg {Boolean} inline inline the element (default false)
20744  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20745  * @cfg {String} tooltip label tooltip
20746  * 
20747  * @constructor
20748  * Create a new CheckBox
20749  * @param {Object} config The config object
20750  */
20751
20752 Roo.bootstrap.CheckBox = function(config){
20753     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20754    
20755     this.addEvents({
20756         /**
20757         * @event check
20758         * Fires when the element is checked or unchecked.
20759         * @param {Roo.bootstrap.CheckBox} this This input
20760         * @param {Boolean} checked The new checked value
20761         */
20762        check : true,
20763        /**
20764         * @event click
20765         * Fires when the element is click.
20766         * @param {Roo.bootstrap.CheckBox} this This input
20767         */
20768        click : true
20769     });
20770     
20771 };
20772
20773 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20774   
20775     inputType: 'checkbox',
20776     inputValue: 1,
20777     valueOff: 0,
20778     boxLabel: false,
20779     checked: false,
20780     weight : false,
20781     inline: false,
20782     tooltip : '',
20783     
20784     getAutoCreate : function()
20785     {
20786         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20787         
20788         var id = Roo.id();
20789         
20790         var cfg = {};
20791         
20792         cfg.cls = 'form-group ' + this.inputType; //input-group
20793         
20794         if(this.inline){
20795             cfg.cls += ' ' + this.inputType + '-inline';
20796         }
20797         
20798         var input =  {
20799             tag: 'input',
20800             id : id,
20801             type : this.inputType,
20802             value : this.inputValue,
20803             cls : 'roo-' + this.inputType, //'form-box',
20804             placeholder : this.placeholder || ''
20805             
20806         };
20807         
20808         if(this.inputType != 'radio'){
20809             var hidden =  {
20810                 tag: 'input',
20811                 type : 'hidden',
20812                 cls : 'roo-hidden-value',
20813                 value : this.checked ? this.inputValue : this.valueOff
20814             };
20815         }
20816         
20817             
20818         if (this.weight) { // Validity check?
20819             cfg.cls += " " + this.inputType + "-" + this.weight;
20820         }
20821         
20822         if (this.disabled) {
20823             input.disabled=true;
20824         }
20825         
20826         if(this.checked){
20827             input.checked = this.checked;
20828         }
20829         
20830         if (this.name) {
20831             
20832             input.name = this.name;
20833             
20834             if(this.inputType != 'radio'){
20835                 hidden.name = this.name;
20836                 input.name = '_hidden_' + this.name;
20837             }
20838         }
20839         
20840         if (this.size) {
20841             input.cls += ' input-' + this.size;
20842         }
20843         
20844         var settings=this;
20845         
20846         ['xs','sm','md','lg'].map(function(size){
20847             if (settings[size]) {
20848                 cfg.cls += ' col-' + size + '-' + settings[size];
20849             }
20850         });
20851         
20852         var inputblock = input;
20853          
20854         if (this.before || this.after) {
20855             
20856             inputblock = {
20857                 cls : 'input-group',
20858                 cn :  [] 
20859             };
20860             
20861             if (this.before) {
20862                 inputblock.cn.push({
20863                     tag :'span',
20864                     cls : 'input-group-addon',
20865                     html : this.before
20866                 });
20867             }
20868             
20869             inputblock.cn.push(input);
20870             
20871             if(this.inputType != 'radio'){
20872                 inputblock.cn.push(hidden);
20873             }
20874             
20875             if (this.after) {
20876                 inputblock.cn.push({
20877                     tag :'span',
20878                     cls : 'input-group-addon',
20879                     html : this.after
20880                 });
20881             }
20882             
20883         }
20884         
20885         if (align ==='left' && this.fieldLabel.length) {
20886 //                Roo.log("left and has label");
20887             cfg.cn = [
20888                 {
20889                     tag: 'label',
20890                     'for' :  id,
20891                     cls : 'control-label',
20892                     html : this.fieldLabel
20893                 },
20894                 {
20895                     cls : "", 
20896                     cn: [
20897                         inputblock
20898                     ]
20899                 }
20900             ];
20901             
20902             if(this.labelWidth > 12){
20903                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20904             }
20905             
20906             if(this.labelWidth < 13 && this.labelmd == 0){
20907                 this.labelmd = this.labelWidth;
20908             }
20909             
20910             if(this.labellg > 0){
20911                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20912                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20913             }
20914             
20915             if(this.labelmd > 0){
20916                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20917                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20918             }
20919             
20920             if(this.labelsm > 0){
20921                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20922                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20923             }
20924             
20925             if(this.labelxs > 0){
20926                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20927                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20928             }
20929             
20930         } else if ( this.fieldLabel.length) {
20931 //                Roo.log(" label");
20932                 cfg.cn = [
20933                    
20934                     {
20935                         tag: this.boxLabel ? 'span' : 'label',
20936                         'for': id,
20937                         cls: 'control-label box-input-label',
20938                         //cls : 'input-group-addon',
20939                         html : this.fieldLabel
20940                     },
20941                     
20942                     inputblock
20943                     
20944                 ];
20945
20946         } else {
20947             
20948 //                Roo.log(" no label && no align");
20949                 cfg.cn = [  inputblock ] ;
20950                 
20951                 
20952         }
20953         
20954         if(this.boxLabel){
20955              var boxLabelCfg = {
20956                 tag: 'label',
20957                 //'for': id, // box label is handled by onclick - so no for...
20958                 cls: 'box-label',
20959                 html: this.boxLabel
20960             };
20961             
20962             if(this.tooltip){
20963                 boxLabelCfg.tooltip = this.tooltip;
20964             }
20965              
20966             cfg.cn.push(boxLabelCfg);
20967         }
20968         
20969         if(this.inputType != 'radio'){
20970             cfg.cn.push(hidden);
20971         }
20972         
20973         return cfg;
20974         
20975     },
20976     
20977     /**
20978      * return the real input element.
20979      */
20980     inputEl: function ()
20981     {
20982         return this.el.select('input.roo-' + this.inputType,true).first();
20983     },
20984     hiddenEl: function ()
20985     {
20986         return this.el.select('input.roo-hidden-value',true).first();
20987     },
20988     
20989     labelEl: function()
20990     {
20991         return this.el.select('label.control-label',true).first();
20992     },
20993     /* depricated... */
20994     
20995     label: function()
20996     {
20997         return this.labelEl();
20998     },
20999     
21000     boxLabelEl: function()
21001     {
21002         return this.el.select('label.box-label',true).first();
21003     },
21004     
21005     initEvents : function()
21006     {
21007 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21008         
21009         this.inputEl().on('click', this.onClick,  this);
21010         
21011         if (this.boxLabel) { 
21012             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
21013         }
21014         
21015         this.startValue = this.getValue();
21016         
21017         if(this.groupId){
21018             Roo.bootstrap.CheckBox.register(this);
21019         }
21020     },
21021     
21022     onClick : function(e)
21023     {   
21024         if(this.fireEvent('click', this, e) !== false){
21025             this.setChecked(!this.checked);
21026         }
21027         
21028     },
21029     
21030     setChecked : function(state,suppressEvent)
21031     {
21032         this.startValue = this.getValue();
21033
21034         if(this.inputType == 'radio'){
21035             
21036             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21037                 e.dom.checked = false;
21038             });
21039             
21040             this.inputEl().dom.checked = true;
21041             
21042             this.inputEl().dom.value = this.inputValue;
21043             
21044             if(suppressEvent !== true){
21045                 this.fireEvent('check', this, true);
21046             }
21047             
21048             this.validate();
21049             
21050             return;
21051         }
21052         
21053         this.checked = state;
21054         
21055         this.inputEl().dom.checked = state;
21056         
21057         
21058         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21059         
21060         if(suppressEvent !== true){
21061             this.fireEvent('check', this, state);
21062         }
21063         
21064         this.validate();
21065     },
21066     
21067     getValue : function()
21068     {
21069         if(this.inputType == 'radio'){
21070             return this.getGroupValue();
21071         }
21072         
21073         return this.hiddenEl().dom.value;
21074         
21075     },
21076     
21077     getGroupValue : function()
21078     {
21079         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21080             return '';
21081         }
21082         
21083         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21084     },
21085     
21086     setValue : function(v,suppressEvent)
21087     {
21088         if(this.inputType == 'radio'){
21089             this.setGroupValue(v, suppressEvent);
21090             return;
21091         }
21092         
21093         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21094         
21095         this.validate();
21096     },
21097     
21098     setGroupValue : function(v, suppressEvent)
21099     {
21100         this.startValue = this.getValue();
21101         
21102         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21103             e.dom.checked = false;
21104             
21105             if(e.dom.value == v){
21106                 e.dom.checked = true;
21107             }
21108         });
21109         
21110         if(suppressEvent !== true){
21111             this.fireEvent('check', this, true);
21112         }
21113
21114         this.validate();
21115         
21116         return;
21117     },
21118     
21119     validate : function()
21120     {
21121         if(this.getVisibilityEl().hasClass('hidden')){
21122             return true;
21123         }
21124         
21125         if(
21126                 this.disabled || 
21127                 (this.inputType == 'radio' && this.validateRadio()) ||
21128                 (this.inputType == 'checkbox' && this.validateCheckbox())
21129         ){
21130             this.markValid();
21131             return true;
21132         }
21133         
21134         this.markInvalid();
21135         return false;
21136     },
21137     
21138     validateRadio : function()
21139     {
21140         if(this.getVisibilityEl().hasClass('hidden')){
21141             return true;
21142         }
21143         
21144         if(this.allowBlank){
21145             return true;
21146         }
21147         
21148         var valid = false;
21149         
21150         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21151             if(!e.dom.checked){
21152                 return;
21153             }
21154             
21155             valid = true;
21156             
21157             return false;
21158         });
21159         
21160         return valid;
21161     },
21162     
21163     validateCheckbox : function()
21164     {
21165         if(!this.groupId){
21166             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21167             //return (this.getValue() == this.inputValue) ? true : false;
21168         }
21169         
21170         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21171         
21172         if(!group){
21173             return false;
21174         }
21175         
21176         var r = false;
21177         
21178         for(var i in group){
21179             if(group[i].el.isVisible(true)){
21180                 r = false;
21181                 break;
21182             }
21183             
21184             r = true;
21185         }
21186         
21187         for(var i in group){
21188             if(r){
21189                 break;
21190             }
21191             
21192             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21193         }
21194         
21195         return r;
21196     },
21197     
21198     /**
21199      * Mark this field as valid
21200      */
21201     markValid : function()
21202     {
21203         var _this = this;
21204         
21205         this.fireEvent('valid', this);
21206         
21207         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21208         
21209         if(this.groupId){
21210             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21211         }
21212         
21213         if(label){
21214             label.markValid();
21215         }
21216
21217         if(this.inputType == 'radio'){
21218             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21219                 var fg = e.findParent('.form-group', false, true);
21220                 if (Roo.bootstrap.version == 3) {
21221                     fg.removeClass([_this.invalidClass, _this.validClass]);
21222                     fg.addClass(_this.validClass);
21223                 } else {
21224                     fg.removeClass(['is-valid', 'is-invalid']);
21225                     fg.addClass('is-valid');
21226                 }
21227             });
21228             
21229             return;
21230         }
21231
21232         if(!this.groupId){
21233             var fg = this.el.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             return;
21242         }
21243         
21244         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21245         
21246         if(!group){
21247             return;
21248         }
21249         
21250         for(var i in group){
21251             var fg = group[i].el.findParent('.form-group', false, true);
21252             if (Roo.bootstrap.version == 3) {
21253                 fg.removeClass([this.invalidClass, this.validClass]);
21254                 fg.addClass(this.validClass);
21255             } else {
21256                 fg.removeClass(['is-valid', 'is-invalid']);
21257                 fg.addClass('is-valid');
21258             }
21259         }
21260     },
21261     
21262      /**
21263      * Mark this field as invalid
21264      * @param {String} msg The validation message
21265      */
21266     markInvalid : function(msg)
21267     {
21268         if(this.allowBlank){
21269             return;
21270         }
21271         
21272         var _this = this;
21273         
21274         this.fireEvent('invalid', this, msg);
21275         
21276         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21277         
21278         if(this.groupId){
21279             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21280         }
21281         
21282         if(label){
21283             label.markInvalid();
21284         }
21285             
21286         if(this.inputType == 'radio'){
21287             
21288             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21289                 var fg = e.findParent('.form-group', false, true);
21290                 if (Roo.bootstrap.version == 3) {
21291                     fg.removeClass([_this.invalidClass, _this.validClass]);
21292                     fg.addClass(_this.invalidClass);
21293                 } else {
21294                     fg.removeClass(['is-invalid', 'is-valid']);
21295                     fg.addClass('is-invalid');
21296                 }
21297             });
21298             
21299             return;
21300         }
21301         
21302         if(!this.groupId){
21303             var fg = this.el.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             return;
21312         }
21313         
21314         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21315         
21316         if(!group){
21317             return;
21318         }
21319         
21320         for(var i in group){
21321             var fg = group[i].el.findParent('.form-group', false, true);
21322             if (Roo.bootstrap.version == 3) {
21323                 fg.removeClass([_this.invalidClass, _this.validClass]);
21324                 fg.addClass(_this.invalidClass);
21325             } else {
21326                 fg.removeClass(['is-invalid', 'is-valid']);
21327                 fg.addClass('is-invalid');
21328             }
21329         }
21330         
21331     },
21332     
21333     clearInvalid : function()
21334     {
21335         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21336         
21337         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21338         
21339         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21340         
21341         if (label && label.iconEl) {
21342             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21343             label.iconEl.removeClass(['is-invalid', 'is-valid']);
21344         }
21345     },
21346     
21347     disable : function()
21348     {
21349         if(this.inputType != 'radio'){
21350             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21351             return;
21352         }
21353         
21354         var _this = this;
21355         
21356         if(this.rendered){
21357             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21358                 _this.getActionEl().addClass(this.disabledClass);
21359                 e.dom.disabled = true;
21360             });
21361         }
21362         
21363         this.disabled = true;
21364         this.fireEvent("disable", this);
21365         return this;
21366     },
21367
21368     enable : function()
21369     {
21370         if(this.inputType != 'radio'){
21371             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21372             return;
21373         }
21374         
21375         var _this = this;
21376         
21377         if(this.rendered){
21378             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21379                 _this.getActionEl().removeClass(this.disabledClass);
21380                 e.dom.disabled = false;
21381             });
21382         }
21383         
21384         this.disabled = false;
21385         this.fireEvent("enable", this);
21386         return this;
21387     },
21388     
21389     setBoxLabel : function(v)
21390     {
21391         this.boxLabel = v;
21392         
21393         if(this.rendered){
21394             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21395         }
21396     }
21397
21398 });
21399
21400 Roo.apply(Roo.bootstrap.CheckBox, {
21401     
21402     groups: {},
21403     
21404      /**
21405     * register a CheckBox Group
21406     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21407     */
21408     register : function(checkbox)
21409     {
21410         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21411             this.groups[checkbox.groupId] = {};
21412         }
21413         
21414         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21415             return;
21416         }
21417         
21418         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21419         
21420     },
21421     /**
21422     * fetch a CheckBox Group based on the group ID
21423     * @param {string} the group ID
21424     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21425     */
21426     get: function(groupId) {
21427         if (typeof(this.groups[groupId]) == 'undefined') {
21428             return false;
21429         }
21430         
21431         return this.groups[groupId] ;
21432     }
21433     
21434     
21435 });
21436 /*
21437  * - LGPL
21438  *
21439  * RadioItem
21440  * 
21441  */
21442
21443 /**
21444  * @class Roo.bootstrap.Radio
21445  * @extends Roo.bootstrap.Component
21446  * Bootstrap Radio class
21447  * @cfg {String} boxLabel - the label associated
21448  * @cfg {String} value - the value of radio
21449  * 
21450  * @constructor
21451  * Create a new Radio
21452  * @param {Object} config The config object
21453  */
21454 Roo.bootstrap.Radio = function(config){
21455     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21456     
21457 };
21458
21459 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21460     
21461     boxLabel : '',
21462     
21463     value : '',
21464     
21465     getAutoCreate : function()
21466     {
21467         var cfg = {
21468             tag : 'div',
21469             cls : 'form-group radio',
21470             cn : [
21471                 {
21472                     tag : 'label',
21473                     cls : 'box-label',
21474                     html : this.boxLabel
21475                 }
21476             ]
21477         };
21478         
21479         return cfg;
21480     },
21481     
21482     initEvents : function() 
21483     {
21484         this.parent().register(this);
21485         
21486         this.el.on('click', this.onClick, this);
21487         
21488     },
21489     
21490     onClick : function(e)
21491     {
21492         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21493             this.setChecked(true);
21494         }
21495     },
21496     
21497     setChecked : function(state, suppressEvent)
21498     {
21499         this.parent().setValue(this.value, suppressEvent);
21500         
21501     },
21502     
21503     setBoxLabel : function(v)
21504     {
21505         this.boxLabel = v;
21506         
21507         if(this.rendered){
21508             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21509         }
21510     }
21511     
21512 });
21513  
21514
21515  /*
21516  * - LGPL
21517  *
21518  * Input
21519  * 
21520  */
21521
21522 /**
21523  * @class Roo.bootstrap.SecurePass
21524  * @extends Roo.bootstrap.Input
21525  * Bootstrap SecurePass class
21526  *
21527  * 
21528  * @constructor
21529  * Create a new SecurePass
21530  * @param {Object} config The config object
21531  */
21532  
21533 Roo.bootstrap.SecurePass = function (config) {
21534     // these go here, so the translation tool can replace them..
21535     this.errors = {
21536         PwdEmpty: "Please type a password, and then retype it to confirm.",
21537         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21538         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21539         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21540         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21541         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21542         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21543         TooWeak: "Your password is Too Weak."
21544     },
21545     this.meterLabel = "Password strength:";
21546     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21547     this.meterClass = [
21548         "roo-password-meter-tooweak", 
21549         "roo-password-meter-weak", 
21550         "roo-password-meter-medium", 
21551         "roo-password-meter-strong", 
21552         "roo-password-meter-grey"
21553     ];
21554     
21555     this.errors = {};
21556     
21557     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21558 }
21559
21560 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21561     /**
21562      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21563      * {
21564      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21565      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21566      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21567      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21568      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21569      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21570      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21571      * })
21572      */
21573     // private
21574     
21575     meterWidth: 300,
21576     errorMsg :'',    
21577     errors: false,
21578     imageRoot: '/',
21579     /**
21580      * @cfg {String/Object} Label for the strength meter (defaults to
21581      * 'Password strength:')
21582      */
21583     // private
21584     meterLabel: '',
21585     /**
21586      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21587      * ['Weak', 'Medium', 'Strong'])
21588      */
21589     // private    
21590     pwdStrengths: false,    
21591     // private
21592     strength: 0,
21593     // private
21594     _lastPwd: null,
21595     // private
21596     kCapitalLetter: 0,
21597     kSmallLetter: 1,
21598     kDigit: 2,
21599     kPunctuation: 3,
21600     
21601     insecure: false,
21602     // private
21603     initEvents: function ()
21604     {
21605         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21606
21607         if (this.el.is('input[type=password]') && Roo.isSafari) {
21608             this.el.on('keydown', this.SafariOnKeyDown, this);
21609         }
21610
21611         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21612     },
21613     // private
21614     onRender: function (ct, position)
21615     {
21616         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21617         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21618         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21619
21620         this.trigger.createChild({
21621                    cn: [
21622                     {
21623                     //id: 'PwdMeter',
21624                     tag: 'div',
21625                     cls: 'roo-password-meter-grey col-xs-12',
21626                     style: {
21627                         //width: 0,
21628                         //width: this.meterWidth + 'px'                                                
21629                         }
21630                     },
21631                     {                            
21632                          cls: 'roo-password-meter-text'                          
21633                     }
21634                 ]            
21635         });
21636
21637          
21638         if (this.hideTrigger) {
21639             this.trigger.setDisplayed(false);
21640         }
21641         this.setSize(this.width || '', this.height || '');
21642     },
21643     // private
21644     onDestroy: function ()
21645     {
21646         if (this.trigger) {
21647             this.trigger.removeAllListeners();
21648             this.trigger.remove();
21649         }
21650         if (this.wrap) {
21651             this.wrap.remove();
21652         }
21653         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21654     },
21655     // private
21656     checkStrength: function ()
21657     {
21658         var pwd = this.inputEl().getValue();
21659         if (pwd == this._lastPwd) {
21660             return;
21661         }
21662
21663         var strength;
21664         if (this.ClientSideStrongPassword(pwd)) {
21665             strength = 3;
21666         } else if (this.ClientSideMediumPassword(pwd)) {
21667             strength = 2;
21668         } else if (this.ClientSideWeakPassword(pwd)) {
21669             strength = 1;
21670         } else {
21671             strength = 0;
21672         }
21673         
21674         Roo.log('strength1: ' + strength);
21675         
21676         //var pm = this.trigger.child('div/div/div').dom;
21677         var pm = this.trigger.child('div/div');
21678         pm.removeClass(this.meterClass);
21679         pm.addClass(this.meterClass[strength]);
21680                 
21681         
21682         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21683                 
21684         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21685         
21686         this._lastPwd = pwd;
21687     },
21688     reset: function ()
21689     {
21690         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21691         
21692         this._lastPwd = '';
21693         
21694         var pm = this.trigger.child('div/div');
21695         pm.removeClass(this.meterClass);
21696         pm.addClass('roo-password-meter-grey');        
21697         
21698         
21699         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21700         
21701         pt.innerHTML = '';
21702         this.inputEl().dom.type='password';
21703     },
21704     // private
21705     validateValue: function (value)
21706     {
21707         
21708         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21709             return false;
21710         }
21711         if (value.length == 0) {
21712             if (this.allowBlank) {
21713                 this.clearInvalid();
21714                 return true;
21715             }
21716
21717             this.markInvalid(this.errors.PwdEmpty);
21718             this.errorMsg = this.errors.PwdEmpty;
21719             return false;
21720         }
21721         
21722         if(this.insecure){
21723             return true;
21724         }
21725         
21726         if ('[\x21-\x7e]*'.match(value)) {
21727             this.markInvalid(this.errors.PwdBadChar);
21728             this.errorMsg = this.errors.PwdBadChar;
21729             return false;
21730         }
21731         if (value.length < 6) {
21732             this.markInvalid(this.errors.PwdShort);
21733             this.errorMsg = this.errors.PwdShort;
21734             return false;
21735         }
21736         if (value.length > 16) {
21737             this.markInvalid(this.errors.PwdLong);
21738             this.errorMsg = this.errors.PwdLong;
21739             return false;
21740         }
21741         var strength;
21742         if (this.ClientSideStrongPassword(value)) {
21743             strength = 3;
21744         } else if (this.ClientSideMediumPassword(value)) {
21745             strength = 2;
21746         } else if (this.ClientSideWeakPassword(value)) {
21747             strength = 1;
21748         } else {
21749             strength = 0;
21750         }
21751
21752         
21753         if (strength < 2) {
21754             //this.markInvalid(this.errors.TooWeak);
21755             this.errorMsg = this.errors.TooWeak;
21756             //return false;
21757         }
21758         
21759         
21760         console.log('strength2: ' + strength);
21761         
21762         //var pm = this.trigger.child('div/div/div').dom;
21763         
21764         var pm = this.trigger.child('div/div');
21765         pm.removeClass(this.meterClass);
21766         pm.addClass(this.meterClass[strength]);
21767                 
21768         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21769                 
21770         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21771         
21772         this.errorMsg = ''; 
21773         return true;
21774     },
21775     // private
21776     CharacterSetChecks: function (type)
21777     {
21778         this.type = type;
21779         this.fResult = false;
21780     },
21781     // private
21782     isctype: function (character, type)
21783     {
21784         switch (type) {  
21785             case this.kCapitalLetter:
21786                 if (character >= 'A' && character <= 'Z') {
21787                     return true;
21788                 }
21789                 break;
21790             
21791             case this.kSmallLetter:
21792                 if (character >= 'a' && character <= 'z') {
21793                     return true;
21794                 }
21795                 break;
21796             
21797             case this.kDigit:
21798                 if (character >= '0' && character <= '9') {
21799                     return true;
21800                 }
21801                 break;
21802             
21803             case this.kPunctuation:
21804                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21805                     return true;
21806                 }
21807                 break;
21808             
21809             default:
21810                 return false;
21811         }
21812
21813     },
21814     // private
21815     IsLongEnough: function (pwd, size)
21816     {
21817         return !(pwd == null || isNaN(size) || pwd.length < size);
21818     },
21819     // private
21820     SpansEnoughCharacterSets: function (word, nb)
21821     {
21822         if (!this.IsLongEnough(word, nb))
21823         {
21824             return false;
21825         }
21826
21827         var characterSetChecks = new Array(
21828             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21829             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21830         );
21831         
21832         for (var index = 0; index < word.length; ++index) {
21833             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21834                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21835                     characterSetChecks[nCharSet].fResult = true;
21836                     break;
21837                 }
21838             }
21839         }
21840
21841         var nCharSets = 0;
21842         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21843             if (characterSetChecks[nCharSet].fResult) {
21844                 ++nCharSets;
21845             }
21846         }
21847
21848         if (nCharSets < nb) {
21849             return false;
21850         }
21851         return true;
21852     },
21853     // private
21854     ClientSideStrongPassword: function (pwd)
21855     {
21856         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21857     },
21858     // private
21859     ClientSideMediumPassword: function (pwd)
21860     {
21861         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21862     },
21863     // private
21864     ClientSideWeakPassword: function (pwd)
21865     {
21866         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21867     }
21868           
21869 })//<script type="text/javascript">
21870
21871 /*
21872  * Based  Ext JS Library 1.1.1
21873  * Copyright(c) 2006-2007, Ext JS, LLC.
21874  * LGPL
21875  *
21876  */
21877  
21878 /**
21879  * @class Roo.HtmlEditorCore
21880  * @extends Roo.Component
21881  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21882  *
21883  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21884  */
21885
21886 Roo.HtmlEditorCore = function(config){
21887     
21888     
21889     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21890     
21891     
21892     this.addEvents({
21893         /**
21894          * @event initialize
21895          * Fires when the editor is fully initialized (including the iframe)
21896          * @param {Roo.HtmlEditorCore} this
21897          */
21898         initialize: true,
21899         /**
21900          * @event activate
21901          * Fires when the editor is first receives the focus. Any insertion must wait
21902          * until after this event.
21903          * @param {Roo.HtmlEditorCore} this
21904          */
21905         activate: true,
21906          /**
21907          * @event beforesync
21908          * Fires before the textarea is updated with content from the editor iframe. Return false
21909          * to cancel the sync.
21910          * @param {Roo.HtmlEditorCore} this
21911          * @param {String} html
21912          */
21913         beforesync: true,
21914          /**
21915          * @event beforepush
21916          * Fires before the iframe editor is updated with content from the textarea. Return false
21917          * to cancel the push.
21918          * @param {Roo.HtmlEditorCore} this
21919          * @param {String} html
21920          */
21921         beforepush: true,
21922          /**
21923          * @event sync
21924          * Fires when the textarea is updated with content from the editor iframe.
21925          * @param {Roo.HtmlEditorCore} this
21926          * @param {String} html
21927          */
21928         sync: true,
21929          /**
21930          * @event push
21931          * Fires when the iframe editor is updated with content from the textarea.
21932          * @param {Roo.HtmlEditorCore} this
21933          * @param {String} html
21934          */
21935         push: true,
21936         
21937         /**
21938          * @event editorevent
21939          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21940          * @param {Roo.HtmlEditorCore} this
21941          */
21942         editorevent: true
21943         
21944     });
21945     
21946     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21947     
21948     // defaults : white / black...
21949     this.applyBlacklists();
21950     
21951     
21952     
21953 };
21954
21955
21956 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21957
21958
21959      /**
21960      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21961      */
21962     
21963     owner : false,
21964     
21965      /**
21966      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21967      *                        Roo.resizable.
21968      */
21969     resizable : false,
21970      /**
21971      * @cfg {Number} height (in pixels)
21972      */   
21973     height: 300,
21974    /**
21975      * @cfg {Number} width (in pixels)
21976      */   
21977     width: 500,
21978     
21979     /**
21980      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21981      * 
21982      */
21983     stylesheets: false,
21984     
21985     // id of frame..
21986     frameId: false,
21987     
21988     // private properties
21989     validationEvent : false,
21990     deferHeight: true,
21991     initialized : false,
21992     activated : false,
21993     sourceEditMode : false,
21994     onFocus : Roo.emptyFn,
21995     iframePad:3,
21996     hideMode:'offsets',
21997     
21998     clearUp: true,
21999     
22000     // blacklist + whitelisted elements..
22001     black: false,
22002     white: false,
22003      
22004     bodyCls : '',
22005
22006     /**
22007      * Protected method that will not generally be called directly. It
22008      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22009      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22010      */
22011     getDocMarkup : function(){
22012         // body styles..
22013         var st = '';
22014         
22015         // inherit styels from page...?? 
22016         if (this.stylesheets === false) {
22017             
22018             Roo.get(document.head).select('style').each(function(node) {
22019                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22020             });
22021             
22022             Roo.get(document.head).select('link').each(function(node) { 
22023                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22024             });
22025             
22026         } else if (!this.stylesheets.length) {
22027                 // simple..
22028                 st = '<style type="text/css">' +
22029                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22030                    '</style>';
22031         } else { 
22032             st = '<style type="text/css">' +
22033                     this.stylesheets +
22034                 '</style>';
22035         }
22036         
22037         st +=  '<style type="text/css">' +
22038             'IMG { cursor: pointer } ' +
22039         '</style>';
22040
22041         var cls = 'roo-htmleditor-body';
22042         
22043         if(this.bodyCls.length){
22044             cls += ' ' + this.bodyCls;
22045         }
22046         
22047         return '<html><head>' + st  +
22048             //<style type="text/css">' +
22049             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22050             //'</style>' +
22051             ' </head><body class="' +  cls + '"></body></html>';
22052     },
22053
22054     // private
22055     onRender : function(ct, position)
22056     {
22057         var _t = this;
22058         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22059         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22060         
22061         
22062         this.el.dom.style.border = '0 none';
22063         this.el.dom.setAttribute('tabIndex', -1);
22064         this.el.addClass('x-hidden hide');
22065         
22066         
22067         
22068         if(Roo.isIE){ // fix IE 1px bogus margin
22069             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22070         }
22071        
22072         
22073         this.frameId = Roo.id();
22074         
22075          
22076         
22077         var iframe = this.owner.wrap.createChild({
22078             tag: 'iframe',
22079             cls: 'form-control', // bootstrap..
22080             id: this.frameId,
22081             name: this.frameId,
22082             frameBorder : 'no',
22083             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22084         }, this.el
22085         );
22086         
22087         
22088         this.iframe = iframe.dom;
22089
22090          this.assignDocWin();
22091         
22092         this.doc.designMode = 'on';
22093        
22094         this.doc.open();
22095         this.doc.write(this.getDocMarkup());
22096         this.doc.close();
22097
22098         
22099         var task = { // must defer to wait for browser to be ready
22100             run : function(){
22101                 //console.log("run task?" + this.doc.readyState);
22102                 this.assignDocWin();
22103                 if(this.doc.body || this.doc.readyState == 'complete'){
22104                     try {
22105                         this.doc.designMode="on";
22106                     } catch (e) {
22107                         return;
22108                     }
22109                     Roo.TaskMgr.stop(task);
22110                     this.initEditor.defer(10, this);
22111                 }
22112             },
22113             interval : 10,
22114             duration: 10000,
22115             scope: this
22116         };
22117         Roo.TaskMgr.start(task);
22118
22119     },
22120
22121     // private
22122     onResize : function(w, h)
22123     {
22124          Roo.log('resize: ' +w + ',' + h );
22125         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22126         if(!this.iframe){
22127             return;
22128         }
22129         if(typeof w == 'number'){
22130             
22131             this.iframe.style.width = w + 'px';
22132         }
22133         if(typeof h == 'number'){
22134             
22135             this.iframe.style.height = h + 'px';
22136             if(this.doc){
22137                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22138             }
22139         }
22140         
22141     },
22142
22143     /**
22144      * Toggles the editor between standard and source edit mode.
22145      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22146      */
22147     toggleSourceEdit : function(sourceEditMode){
22148         
22149         this.sourceEditMode = sourceEditMode === true;
22150         
22151         if(this.sourceEditMode){
22152  
22153             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22154             
22155         }else{
22156             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22157             //this.iframe.className = '';
22158             this.deferFocus();
22159         }
22160         //this.setSize(this.owner.wrap.getSize());
22161         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22162     },
22163
22164     
22165   
22166
22167     /**
22168      * Protected method that will not generally be called directly. If you need/want
22169      * custom HTML cleanup, this is the method you should override.
22170      * @param {String} html The HTML to be cleaned
22171      * return {String} The cleaned HTML
22172      */
22173     cleanHtml : function(html){
22174         html = String(html);
22175         if(html.length > 5){
22176             if(Roo.isSafari){ // strip safari nonsense
22177                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22178             }
22179         }
22180         if(html == '&nbsp;'){
22181             html = '';
22182         }
22183         return html;
22184     },
22185
22186     /**
22187      * HTML Editor -> Textarea
22188      * Protected method that will not generally be called directly. Syncs the contents
22189      * of the editor iframe with the textarea.
22190      */
22191     syncValue : function(){
22192         if(this.initialized){
22193             var bd = (this.doc.body || this.doc.documentElement);
22194             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22195             var html = bd.innerHTML;
22196             if(Roo.isSafari){
22197                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22198                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22199                 if(m && m[1]){
22200                     html = '<div style="'+m[0]+'">' + html + '</div>';
22201                 }
22202             }
22203             html = this.cleanHtml(html);
22204             // fix up the special chars.. normaly like back quotes in word...
22205             // however we do not want to do this with chinese..
22206             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22207                 var cc = b.charCodeAt();
22208                 if (
22209                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22210                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22211                     (cc >= 0xf900 && cc < 0xfb00 )
22212                 ) {
22213                         return b;
22214                 }
22215                 return "&#"+cc+";" 
22216             });
22217             if(this.owner.fireEvent('beforesync', this, html) !== false){
22218                 this.el.dom.value = html;
22219                 this.owner.fireEvent('sync', this, html);
22220             }
22221         }
22222     },
22223
22224     /**
22225      * Protected method that will not generally be called directly. Pushes the value of the textarea
22226      * into the iframe editor.
22227      */
22228     pushValue : function(){
22229         if(this.initialized){
22230             var v = this.el.dom.value.trim();
22231             
22232 //            if(v.length < 1){
22233 //                v = '&#160;';
22234 //            }
22235             
22236             if(this.owner.fireEvent('beforepush', this, v) !== false){
22237                 var d = (this.doc.body || this.doc.documentElement);
22238                 d.innerHTML = v;
22239                 this.cleanUpPaste();
22240                 this.el.dom.value = d.innerHTML;
22241                 this.owner.fireEvent('push', this, v);
22242             }
22243         }
22244     },
22245
22246     // private
22247     deferFocus : function(){
22248         this.focus.defer(10, this);
22249     },
22250
22251     // doc'ed in Field
22252     focus : function(){
22253         if(this.win && !this.sourceEditMode){
22254             this.win.focus();
22255         }else{
22256             this.el.focus();
22257         }
22258     },
22259     
22260     assignDocWin: function()
22261     {
22262         var iframe = this.iframe;
22263         
22264          if(Roo.isIE){
22265             this.doc = iframe.contentWindow.document;
22266             this.win = iframe.contentWindow;
22267         } else {
22268 //            if (!Roo.get(this.frameId)) {
22269 //                return;
22270 //            }
22271 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22272 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22273             
22274             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22275                 return;
22276             }
22277             
22278             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22279             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22280         }
22281     },
22282     
22283     // private
22284     initEditor : function(){
22285         //console.log("INIT EDITOR");
22286         this.assignDocWin();
22287         
22288         
22289         
22290         this.doc.designMode="on";
22291         this.doc.open();
22292         this.doc.write(this.getDocMarkup());
22293         this.doc.close();
22294         
22295         var dbody = (this.doc.body || this.doc.documentElement);
22296         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22297         // this copies styles from the containing element into thsi one..
22298         // not sure why we need all of this..
22299         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22300         
22301         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22302         //ss['background-attachment'] = 'fixed'; // w3c
22303         dbody.bgProperties = 'fixed'; // ie
22304         //Roo.DomHelper.applyStyles(dbody, ss);
22305         Roo.EventManager.on(this.doc, {
22306             //'mousedown': this.onEditorEvent,
22307             'mouseup': this.onEditorEvent,
22308             'dblclick': this.onEditorEvent,
22309             'click': this.onEditorEvent,
22310             'keyup': this.onEditorEvent,
22311             buffer:100,
22312             scope: this
22313         });
22314         if(Roo.isGecko){
22315             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22316         }
22317         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22318             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22319         }
22320         this.initialized = true;
22321
22322         this.owner.fireEvent('initialize', this);
22323         this.pushValue();
22324     },
22325
22326     // private
22327     onDestroy : function(){
22328         
22329         
22330         
22331         if(this.rendered){
22332             
22333             //for (var i =0; i < this.toolbars.length;i++) {
22334             //    // fixme - ask toolbars for heights?
22335             //    this.toolbars[i].onDestroy();
22336            // }
22337             
22338             //this.wrap.dom.innerHTML = '';
22339             //this.wrap.remove();
22340         }
22341     },
22342
22343     // private
22344     onFirstFocus : function(){
22345         
22346         this.assignDocWin();
22347         
22348         
22349         this.activated = true;
22350          
22351     
22352         if(Roo.isGecko){ // prevent silly gecko errors
22353             this.win.focus();
22354             var s = this.win.getSelection();
22355             if(!s.focusNode || s.focusNode.nodeType != 3){
22356                 var r = s.getRangeAt(0);
22357                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22358                 r.collapse(true);
22359                 this.deferFocus();
22360             }
22361             try{
22362                 this.execCmd('useCSS', true);
22363                 this.execCmd('styleWithCSS', false);
22364             }catch(e){}
22365         }
22366         this.owner.fireEvent('activate', this);
22367     },
22368
22369     // private
22370     adjustFont: function(btn){
22371         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22372         //if(Roo.isSafari){ // safari
22373         //    adjust *= 2;
22374        // }
22375         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22376         if(Roo.isSafari){ // safari
22377             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22378             v =  (v < 10) ? 10 : v;
22379             v =  (v > 48) ? 48 : v;
22380             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22381             
22382         }
22383         
22384         
22385         v = Math.max(1, v+adjust);
22386         
22387         this.execCmd('FontSize', v  );
22388     },
22389
22390     onEditorEvent : function(e)
22391     {
22392         this.owner.fireEvent('editorevent', this, e);
22393       //  this.updateToolbar();
22394         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22395     },
22396
22397     insertTag : function(tg)
22398     {
22399         // could be a bit smarter... -> wrap the current selected tRoo..
22400         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22401             
22402             range = this.createRange(this.getSelection());
22403             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22404             wrappingNode.appendChild(range.extractContents());
22405             range.insertNode(wrappingNode);
22406
22407             return;
22408             
22409             
22410             
22411         }
22412         this.execCmd("formatblock",   tg);
22413         
22414     },
22415     
22416     insertText : function(txt)
22417     {
22418         
22419         
22420         var range = this.createRange();
22421         range.deleteContents();
22422                //alert(Sender.getAttribute('label'));
22423                
22424         range.insertNode(this.doc.createTextNode(txt));
22425     } ,
22426     
22427      
22428
22429     /**
22430      * Executes a Midas editor command on the editor document and performs necessary focus and
22431      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22432      * @param {String} cmd The Midas command
22433      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22434      */
22435     relayCmd : function(cmd, value){
22436         this.win.focus();
22437         this.execCmd(cmd, value);
22438         this.owner.fireEvent('editorevent', this);
22439         //this.updateToolbar();
22440         this.owner.deferFocus();
22441     },
22442
22443     /**
22444      * Executes a Midas editor command directly on the editor document.
22445      * For visual commands, you should use {@link #relayCmd} instead.
22446      * <b>This should only be called after the editor is initialized.</b>
22447      * @param {String} cmd The Midas command
22448      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22449      */
22450     execCmd : function(cmd, value){
22451         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22452         this.syncValue();
22453     },
22454  
22455  
22456    
22457     /**
22458      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22459      * to insert tRoo.
22460      * @param {String} text | dom node.. 
22461      */
22462     insertAtCursor : function(text)
22463     {
22464         
22465         if(!this.activated){
22466             return;
22467         }
22468         /*
22469         if(Roo.isIE){
22470             this.win.focus();
22471             var r = this.doc.selection.createRange();
22472             if(r){
22473                 r.collapse(true);
22474                 r.pasteHTML(text);
22475                 this.syncValue();
22476                 this.deferFocus();
22477             
22478             }
22479             return;
22480         }
22481         */
22482         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22483             this.win.focus();
22484             
22485             
22486             // from jquery ui (MIT licenced)
22487             var range, node;
22488             var win = this.win;
22489             
22490             if (win.getSelection && win.getSelection().getRangeAt) {
22491                 range = win.getSelection().getRangeAt(0);
22492                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22493                 range.insertNode(node);
22494             } else if (win.document.selection && win.document.selection.createRange) {
22495                 // no firefox support
22496                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22497                 win.document.selection.createRange().pasteHTML(txt);
22498             } else {
22499                 // no firefox support
22500                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22501                 this.execCmd('InsertHTML', txt);
22502             } 
22503             
22504             this.syncValue();
22505             
22506             this.deferFocus();
22507         }
22508     },
22509  // private
22510     mozKeyPress : function(e){
22511         if(e.ctrlKey){
22512             var c = e.getCharCode(), cmd;
22513           
22514             if(c > 0){
22515                 c = String.fromCharCode(c).toLowerCase();
22516                 switch(c){
22517                     case 'b':
22518                         cmd = 'bold';
22519                         break;
22520                     case 'i':
22521                         cmd = 'italic';
22522                         break;
22523                     
22524                     case 'u':
22525                         cmd = 'underline';
22526                         break;
22527                     
22528                     case 'v':
22529                         this.cleanUpPaste.defer(100, this);
22530                         return;
22531                         
22532                 }
22533                 if(cmd){
22534                     this.win.focus();
22535                     this.execCmd(cmd);
22536                     this.deferFocus();
22537                     e.preventDefault();
22538                 }
22539                 
22540             }
22541         }
22542     },
22543
22544     // private
22545     fixKeys : function(){ // load time branching for fastest keydown performance
22546         if(Roo.isIE){
22547             return function(e){
22548                 var k = e.getKey(), r;
22549                 if(k == e.TAB){
22550                     e.stopEvent();
22551                     r = this.doc.selection.createRange();
22552                     if(r){
22553                         r.collapse(true);
22554                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22555                         this.deferFocus();
22556                     }
22557                     return;
22558                 }
22559                 
22560                 if(k == e.ENTER){
22561                     r = this.doc.selection.createRange();
22562                     if(r){
22563                         var target = r.parentElement();
22564                         if(!target || target.tagName.toLowerCase() != 'li'){
22565                             e.stopEvent();
22566                             r.pasteHTML('<br />');
22567                             r.collapse(false);
22568                             r.select();
22569                         }
22570                     }
22571                 }
22572                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22573                     this.cleanUpPaste.defer(100, this);
22574                     return;
22575                 }
22576                 
22577                 
22578             };
22579         }else if(Roo.isOpera){
22580             return function(e){
22581                 var k = e.getKey();
22582                 if(k == e.TAB){
22583                     e.stopEvent();
22584                     this.win.focus();
22585                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22586                     this.deferFocus();
22587                 }
22588                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22589                     this.cleanUpPaste.defer(100, this);
22590                     return;
22591                 }
22592                 
22593             };
22594         }else if(Roo.isSafari){
22595             return function(e){
22596                 var k = e.getKey();
22597                 
22598                 if(k == e.TAB){
22599                     e.stopEvent();
22600                     this.execCmd('InsertText','\t');
22601                     this.deferFocus();
22602                     return;
22603                 }
22604                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22605                     this.cleanUpPaste.defer(100, this);
22606                     return;
22607                 }
22608                 
22609              };
22610         }
22611     }(),
22612     
22613     getAllAncestors: function()
22614     {
22615         var p = this.getSelectedNode();
22616         var a = [];
22617         if (!p) {
22618             a.push(p); // push blank onto stack..
22619             p = this.getParentElement();
22620         }
22621         
22622         
22623         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22624             a.push(p);
22625             p = p.parentNode;
22626         }
22627         a.push(this.doc.body);
22628         return a;
22629     },
22630     lastSel : false,
22631     lastSelNode : false,
22632     
22633     
22634     getSelection : function() 
22635     {
22636         this.assignDocWin();
22637         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22638     },
22639     
22640     getSelectedNode: function() 
22641     {
22642         // this may only work on Gecko!!!
22643         
22644         // should we cache this!!!!
22645         
22646         
22647         
22648          
22649         var range = this.createRange(this.getSelection()).cloneRange();
22650         
22651         if (Roo.isIE) {
22652             var parent = range.parentElement();
22653             while (true) {
22654                 var testRange = range.duplicate();
22655                 testRange.moveToElementText(parent);
22656                 if (testRange.inRange(range)) {
22657                     break;
22658                 }
22659                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22660                     break;
22661                 }
22662                 parent = parent.parentElement;
22663             }
22664             return parent;
22665         }
22666         
22667         // is ancestor a text element.
22668         var ac =  range.commonAncestorContainer;
22669         if (ac.nodeType == 3) {
22670             ac = ac.parentNode;
22671         }
22672         
22673         var ar = ac.childNodes;
22674          
22675         var nodes = [];
22676         var other_nodes = [];
22677         var has_other_nodes = false;
22678         for (var i=0;i<ar.length;i++) {
22679             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22680                 continue;
22681             }
22682             // fullly contained node.
22683             
22684             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22685                 nodes.push(ar[i]);
22686                 continue;
22687             }
22688             
22689             // probably selected..
22690             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22691                 other_nodes.push(ar[i]);
22692                 continue;
22693             }
22694             // outer..
22695             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22696                 continue;
22697             }
22698             
22699             
22700             has_other_nodes = true;
22701         }
22702         if (!nodes.length && other_nodes.length) {
22703             nodes= other_nodes;
22704         }
22705         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22706             return false;
22707         }
22708         
22709         return nodes[0];
22710     },
22711     createRange: function(sel)
22712     {
22713         // this has strange effects when using with 
22714         // top toolbar - not sure if it's a great idea.
22715         //this.editor.contentWindow.focus();
22716         if (typeof sel != "undefined") {
22717             try {
22718                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22719             } catch(e) {
22720                 return this.doc.createRange();
22721             }
22722         } else {
22723             return this.doc.createRange();
22724         }
22725     },
22726     getParentElement: function()
22727     {
22728         
22729         this.assignDocWin();
22730         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22731         
22732         var range = this.createRange(sel);
22733          
22734         try {
22735             var p = range.commonAncestorContainer;
22736             while (p.nodeType == 3) { // text node
22737                 p = p.parentNode;
22738             }
22739             return p;
22740         } catch (e) {
22741             return null;
22742         }
22743     
22744     },
22745     /***
22746      *
22747      * Range intersection.. the hard stuff...
22748      *  '-1' = before
22749      *  '0' = hits..
22750      *  '1' = after.
22751      *         [ -- selected range --- ]
22752      *   [fail]                        [fail]
22753      *
22754      *    basically..
22755      *      if end is before start or  hits it. fail.
22756      *      if start is after end or hits it fail.
22757      *
22758      *   if either hits (but other is outside. - then it's not 
22759      *   
22760      *    
22761      **/
22762     
22763     
22764     // @see http://www.thismuchiknow.co.uk/?p=64.
22765     rangeIntersectsNode : function(range, node)
22766     {
22767         var nodeRange = node.ownerDocument.createRange();
22768         try {
22769             nodeRange.selectNode(node);
22770         } catch (e) {
22771             nodeRange.selectNodeContents(node);
22772         }
22773     
22774         var rangeStartRange = range.cloneRange();
22775         rangeStartRange.collapse(true);
22776     
22777         var rangeEndRange = range.cloneRange();
22778         rangeEndRange.collapse(false);
22779     
22780         var nodeStartRange = nodeRange.cloneRange();
22781         nodeStartRange.collapse(true);
22782     
22783         var nodeEndRange = nodeRange.cloneRange();
22784         nodeEndRange.collapse(false);
22785     
22786         return rangeStartRange.compareBoundaryPoints(
22787                  Range.START_TO_START, nodeEndRange) == -1 &&
22788                rangeEndRange.compareBoundaryPoints(
22789                  Range.START_TO_START, nodeStartRange) == 1;
22790         
22791          
22792     },
22793     rangeCompareNode : function(range, node)
22794     {
22795         var nodeRange = node.ownerDocument.createRange();
22796         try {
22797             nodeRange.selectNode(node);
22798         } catch (e) {
22799             nodeRange.selectNodeContents(node);
22800         }
22801         
22802         
22803         range.collapse(true);
22804     
22805         nodeRange.collapse(true);
22806      
22807         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22808         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22809          
22810         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22811         
22812         var nodeIsBefore   =  ss == 1;
22813         var nodeIsAfter    = ee == -1;
22814         
22815         if (nodeIsBefore && nodeIsAfter) {
22816             return 0; // outer
22817         }
22818         if (!nodeIsBefore && nodeIsAfter) {
22819             return 1; //right trailed.
22820         }
22821         
22822         if (nodeIsBefore && !nodeIsAfter) {
22823             return 2;  // left trailed.
22824         }
22825         // fully contined.
22826         return 3;
22827     },
22828
22829     // private? - in a new class?
22830     cleanUpPaste :  function()
22831     {
22832         // cleans up the whole document..
22833         Roo.log('cleanuppaste');
22834         
22835         this.cleanUpChildren(this.doc.body);
22836         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22837         if (clean != this.doc.body.innerHTML) {
22838             this.doc.body.innerHTML = clean;
22839         }
22840         
22841     },
22842     
22843     cleanWordChars : function(input) {// change the chars to hex code
22844         var he = Roo.HtmlEditorCore;
22845         
22846         var output = input;
22847         Roo.each(he.swapCodes, function(sw) { 
22848             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22849             
22850             output = output.replace(swapper, sw[1]);
22851         });
22852         
22853         return output;
22854     },
22855     
22856     
22857     cleanUpChildren : function (n)
22858     {
22859         if (!n.childNodes.length) {
22860             return;
22861         }
22862         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22863            this.cleanUpChild(n.childNodes[i]);
22864         }
22865     },
22866     
22867     
22868         
22869     
22870     cleanUpChild : function (node)
22871     {
22872         var ed = this;
22873         //console.log(node);
22874         if (node.nodeName == "#text") {
22875             // clean up silly Windows -- stuff?
22876             return; 
22877         }
22878         if (node.nodeName == "#comment") {
22879             node.parentNode.removeChild(node);
22880             // clean up silly Windows -- stuff?
22881             return; 
22882         }
22883         var lcname = node.tagName.toLowerCase();
22884         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22885         // whitelist of tags..
22886         
22887         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22888             // remove node.
22889             node.parentNode.removeChild(node);
22890             return;
22891             
22892         }
22893         
22894         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22895         
22896         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22897         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22898         
22899         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22900         //    remove_keep_children = true;
22901         //}
22902         
22903         if (remove_keep_children) {
22904             this.cleanUpChildren(node);
22905             // inserts everything just before this node...
22906             while (node.childNodes.length) {
22907                 var cn = node.childNodes[0];
22908                 node.removeChild(cn);
22909                 node.parentNode.insertBefore(cn, node);
22910             }
22911             node.parentNode.removeChild(node);
22912             return;
22913         }
22914         
22915         if (!node.attributes || !node.attributes.length) {
22916             this.cleanUpChildren(node);
22917             return;
22918         }
22919         
22920         function cleanAttr(n,v)
22921         {
22922             
22923             if (v.match(/^\./) || v.match(/^\//)) {
22924                 return;
22925             }
22926             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22927                 return;
22928             }
22929             if (v.match(/^#/)) {
22930                 return;
22931             }
22932 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22933             node.removeAttribute(n);
22934             
22935         }
22936         
22937         var cwhite = this.cwhite;
22938         var cblack = this.cblack;
22939             
22940         function cleanStyle(n,v)
22941         {
22942             if (v.match(/expression/)) { //XSS?? should we even bother..
22943                 node.removeAttribute(n);
22944                 return;
22945             }
22946             
22947             var parts = v.split(/;/);
22948             var clean = [];
22949             
22950             Roo.each(parts, function(p) {
22951                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22952                 if (!p.length) {
22953                     return true;
22954                 }
22955                 var l = p.split(':').shift().replace(/\s+/g,'');
22956                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22957                 
22958                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22959 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22960                     //node.removeAttribute(n);
22961                     return true;
22962                 }
22963                 //Roo.log()
22964                 // only allow 'c whitelisted system attributes'
22965                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22966 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22967                     //node.removeAttribute(n);
22968                     return true;
22969                 }
22970                 
22971                 
22972                  
22973                 
22974                 clean.push(p);
22975                 return true;
22976             });
22977             if (clean.length) { 
22978                 node.setAttribute(n, clean.join(';'));
22979             } else {
22980                 node.removeAttribute(n);
22981             }
22982             
22983         }
22984         
22985         
22986         for (var i = node.attributes.length-1; i > -1 ; i--) {
22987             var a = node.attributes[i];
22988             //console.log(a);
22989             
22990             if (a.name.toLowerCase().substr(0,2)=='on')  {
22991                 node.removeAttribute(a.name);
22992                 continue;
22993             }
22994             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22995                 node.removeAttribute(a.name);
22996                 continue;
22997             }
22998             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22999                 cleanAttr(a.name,a.value); // fixme..
23000                 continue;
23001             }
23002             if (a.name == 'style') {
23003                 cleanStyle(a.name,a.value);
23004                 continue;
23005             }
23006             /// clean up MS crap..
23007             // tecnically this should be a list of valid class'es..
23008             
23009             
23010             if (a.name == 'class') {
23011                 if (a.value.match(/^Mso/)) {
23012                     node.className = '';
23013                 }
23014                 
23015                 if (a.value.match(/^body$/)) {
23016                     node.className = '';
23017                 }
23018                 continue;
23019             }
23020             
23021             // style cleanup!?
23022             // class cleanup?
23023             
23024         }
23025         
23026         
23027         this.cleanUpChildren(node);
23028         
23029         
23030     },
23031     
23032     /**
23033      * Clean up MS wordisms...
23034      */
23035     cleanWord : function(node)
23036     {
23037         
23038         
23039         if (!node) {
23040             this.cleanWord(this.doc.body);
23041             return;
23042         }
23043         if (node.nodeName == "#text") {
23044             // clean up silly Windows -- stuff?
23045             return; 
23046         }
23047         if (node.nodeName == "#comment") {
23048             node.parentNode.removeChild(node);
23049             // clean up silly Windows -- stuff?
23050             return; 
23051         }
23052         
23053         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23054             node.parentNode.removeChild(node);
23055             return;
23056         }
23057         
23058         // remove - but keep children..
23059         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
23060             while (node.childNodes.length) {
23061                 var cn = node.childNodes[0];
23062                 node.removeChild(cn);
23063                 node.parentNode.insertBefore(cn, node);
23064             }
23065             node.parentNode.removeChild(node);
23066             this.iterateChildren(node, this.cleanWord);
23067             return;
23068         }
23069         // clean styles
23070         if (node.className.length) {
23071             
23072             var cn = node.className.split(/\W+/);
23073             var cna = [];
23074             Roo.each(cn, function(cls) {
23075                 if (cls.match(/Mso[a-zA-Z]+/)) {
23076                     return;
23077                 }
23078                 cna.push(cls);
23079             });
23080             node.className = cna.length ? cna.join(' ') : '';
23081             if (!cna.length) {
23082                 node.removeAttribute("class");
23083             }
23084         }
23085         
23086         if (node.hasAttribute("lang")) {
23087             node.removeAttribute("lang");
23088         }
23089         
23090         if (node.hasAttribute("style")) {
23091             
23092             var styles = node.getAttribute("style").split(";");
23093             var nstyle = [];
23094             Roo.each(styles, function(s) {
23095                 if (!s.match(/:/)) {
23096                     return;
23097                 }
23098                 var kv = s.split(":");
23099                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23100                     return;
23101                 }
23102                 // what ever is left... we allow.
23103                 nstyle.push(s);
23104             });
23105             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23106             if (!nstyle.length) {
23107                 node.removeAttribute('style');
23108             }
23109         }
23110         this.iterateChildren(node, this.cleanWord);
23111         
23112         
23113         
23114     },
23115     /**
23116      * iterateChildren of a Node, calling fn each time, using this as the scole..
23117      * @param {DomNode} node node to iterate children of.
23118      * @param {Function} fn method of this class to call on each item.
23119      */
23120     iterateChildren : function(node, fn)
23121     {
23122         if (!node.childNodes.length) {
23123                 return;
23124         }
23125         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23126            fn.call(this, node.childNodes[i])
23127         }
23128     },
23129     
23130     
23131     /**
23132      * cleanTableWidths.
23133      *
23134      * Quite often pasting from word etc.. results in tables with column and widths.
23135      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23136      *
23137      */
23138     cleanTableWidths : function(node)
23139     {
23140          
23141          
23142         if (!node) {
23143             this.cleanTableWidths(this.doc.body);
23144             return;
23145         }
23146         
23147         // ignore list...
23148         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23149             return; 
23150         }
23151         Roo.log(node.tagName);
23152         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23153             this.iterateChildren(node, this.cleanTableWidths);
23154             return;
23155         }
23156         if (node.hasAttribute('width')) {
23157             node.removeAttribute('width');
23158         }
23159         
23160          
23161         if (node.hasAttribute("style")) {
23162             // pretty basic...
23163             
23164             var styles = node.getAttribute("style").split(";");
23165             var nstyle = [];
23166             Roo.each(styles, function(s) {
23167                 if (!s.match(/:/)) {
23168                     return;
23169                 }
23170                 var kv = s.split(":");
23171                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23172                     return;
23173                 }
23174                 // what ever is left... we allow.
23175                 nstyle.push(s);
23176             });
23177             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23178             if (!nstyle.length) {
23179                 node.removeAttribute('style');
23180             }
23181         }
23182         
23183         this.iterateChildren(node, this.cleanTableWidths);
23184         
23185         
23186     },
23187     
23188     
23189     
23190     
23191     domToHTML : function(currentElement, depth, nopadtext) {
23192         
23193         depth = depth || 0;
23194         nopadtext = nopadtext || false;
23195     
23196         if (!currentElement) {
23197             return this.domToHTML(this.doc.body);
23198         }
23199         
23200         //Roo.log(currentElement);
23201         var j;
23202         var allText = false;
23203         var nodeName = currentElement.nodeName;
23204         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23205         
23206         if  (nodeName == '#text') {
23207             
23208             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23209         }
23210         
23211         
23212         var ret = '';
23213         if (nodeName != 'BODY') {
23214              
23215             var i = 0;
23216             // Prints the node tagName, such as <A>, <IMG>, etc
23217             if (tagName) {
23218                 var attr = [];
23219                 for(i = 0; i < currentElement.attributes.length;i++) {
23220                     // quoting?
23221                     var aname = currentElement.attributes.item(i).name;
23222                     if (!currentElement.attributes.item(i).value.length) {
23223                         continue;
23224                     }
23225                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23226                 }
23227                 
23228                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23229             } 
23230             else {
23231                 
23232                 // eack
23233             }
23234         } else {
23235             tagName = false;
23236         }
23237         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23238             return ret;
23239         }
23240         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23241             nopadtext = true;
23242         }
23243         
23244         
23245         // Traverse the tree
23246         i = 0;
23247         var currentElementChild = currentElement.childNodes.item(i);
23248         var allText = true;
23249         var innerHTML  = '';
23250         lastnode = '';
23251         while (currentElementChild) {
23252             // Formatting code (indent the tree so it looks nice on the screen)
23253             var nopad = nopadtext;
23254             if (lastnode == 'SPAN') {
23255                 nopad  = true;
23256             }
23257             // text
23258             if  (currentElementChild.nodeName == '#text') {
23259                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23260                 toadd = nopadtext ? toadd : toadd.trim();
23261                 if (!nopad && toadd.length > 80) {
23262                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23263                 }
23264                 innerHTML  += toadd;
23265                 
23266                 i++;
23267                 currentElementChild = currentElement.childNodes.item(i);
23268                 lastNode = '';
23269                 continue;
23270             }
23271             allText = false;
23272             
23273             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23274                 
23275             // Recursively traverse the tree structure of the child node
23276             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23277             lastnode = currentElementChild.nodeName;
23278             i++;
23279             currentElementChild=currentElement.childNodes.item(i);
23280         }
23281         
23282         ret += innerHTML;
23283         
23284         if (!allText) {
23285                 // The remaining code is mostly for formatting the tree
23286             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23287         }
23288         
23289         
23290         if (tagName) {
23291             ret+= "</"+tagName+">";
23292         }
23293         return ret;
23294         
23295     },
23296         
23297     applyBlacklists : function()
23298     {
23299         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23300         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23301         
23302         this.white = [];
23303         this.black = [];
23304         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23305             if (b.indexOf(tag) > -1) {
23306                 return;
23307             }
23308             this.white.push(tag);
23309             
23310         }, this);
23311         
23312         Roo.each(w, function(tag) {
23313             if (b.indexOf(tag) > -1) {
23314                 return;
23315             }
23316             if (this.white.indexOf(tag) > -1) {
23317                 return;
23318             }
23319             this.white.push(tag);
23320             
23321         }, this);
23322         
23323         
23324         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23325             if (w.indexOf(tag) > -1) {
23326                 return;
23327             }
23328             this.black.push(tag);
23329             
23330         }, this);
23331         
23332         Roo.each(b, function(tag) {
23333             if (w.indexOf(tag) > -1) {
23334                 return;
23335             }
23336             if (this.black.indexOf(tag) > -1) {
23337                 return;
23338             }
23339             this.black.push(tag);
23340             
23341         }, this);
23342         
23343         
23344         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23345         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23346         
23347         this.cwhite = [];
23348         this.cblack = [];
23349         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23350             if (b.indexOf(tag) > -1) {
23351                 return;
23352             }
23353             this.cwhite.push(tag);
23354             
23355         }, this);
23356         
23357         Roo.each(w, function(tag) {
23358             if (b.indexOf(tag) > -1) {
23359                 return;
23360             }
23361             if (this.cwhite.indexOf(tag) > -1) {
23362                 return;
23363             }
23364             this.cwhite.push(tag);
23365             
23366         }, this);
23367         
23368         
23369         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23370             if (w.indexOf(tag) > -1) {
23371                 return;
23372             }
23373             this.cblack.push(tag);
23374             
23375         }, this);
23376         
23377         Roo.each(b, function(tag) {
23378             if (w.indexOf(tag) > -1) {
23379                 return;
23380             }
23381             if (this.cblack.indexOf(tag) > -1) {
23382                 return;
23383             }
23384             this.cblack.push(tag);
23385             
23386         }, this);
23387     },
23388     
23389     setStylesheets : function(stylesheets)
23390     {
23391         if(typeof(stylesheets) == 'string'){
23392             Roo.get(this.iframe.contentDocument.head).createChild({
23393                 tag : 'link',
23394                 rel : 'stylesheet',
23395                 type : 'text/css',
23396                 href : stylesheets
23397             });
23398             
23399             return;
23400         }
23401         var _this = this;
23402      
23403         Roo.each(stylesheets, function(s) {
23404             if(!s.length){
23405                 return;
23406             }
23407             
23408             Roo.get(_this.iframe.contentDocument.head).createChild({
23409                 tag : 'link',
23410                 rel : 'stylesheet',
23411                 type : 'text/css',
23412                 href : s
23413             });
23414         });
23415
23416         
23417     },
23418     
23419     removeStylesheets : function()
23420     {
23421         var _this = this;
23422         
23423         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23424             s.remove();
23425         });
23426     },
23427     
23428     setStyle : function(style)
23429     {
23430         Roo.get(this.iframe.contentDocument.head).createChild({
23431             tag : 'style',
23432             type : 'text/css',
23433             html : style
23434         });
23435
23436         return;
23437     }
23438     
23439     // hide stuff that is not compatible
23440     /**
23441      * @event blur
23442      * @hide
23443      */
23444     /**
23445      * @event change
23446      * @hide
23447      */
23448     /**
23449      * @event focus
23450      * @hide
23451      */
23452     /**
23453      * @event specialkey
23454      * @hide
23455      */
23456     /**
23457      * @cfg {String} fieldClass @hide
23458      */
23459     /**
23460      * @cfg {String} focusClass @hide
23461      */
23462     /**
23463      * @cfg {String} autoCreate @hide
23464      */
23465     /**
23466      * @cfg {String} inputType @hide
23467      */
23468     /**
23469      * @cfg {String} invalidClass @hide
23470      */
23471     /**
23472      * @cfg {String} invalidText @hide
23473      */
23474     /**
23475      * @cfg {String} msgFx @hide
23476      */
23477     /**
23478      * @cfg {String} validateOnBlur @hide
23479      */
23480 });
23481
23482 Roo.HtmlEditorCore.white = [
23483         'area', 'br', 'img', 'input', 'hr', 'wbr',
23484         
23485        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23486        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23487        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23488        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23489        'table',   'ul',         'xmp', 
23490        
23491        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23492       'thead',   'tr', 
23493      
23494       'dir', 'menu', 'ol', 'ul', 'dl',
23495        
23496       'embed',  'object'
23497 ];
23498
23499
23500 Roo.HtmlEditorCore.black = [
23501     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23502         'applet', // 
23503         'base',   'basefont', 'bgsound', 'blink',  'body', 
23504         'frame',  'frameset', 'head',    'html',   'ilayer', 
23505         'iframe', 'layer',  'link',     'meta',    'object',   
23506         'script', 'style' ,'title',  'xml' // clean later..
23507 ];
23508 Roo.HtmlEditorCore.clean = [
23509     'script', 'style', 'title', 'xml'
23510 ];
23511 Roo.HtmlEditorCore.remove = [
23512     'font'
23513 ];
23514 // attributes..
23515
23516 Roo.HtmlEditorCore.ablack = [
23517     'on'
23518 ];
23519     
23520 Roo.HtmlEditorCore.aclean = [ 
23521     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23522 ];
23523
23524 // protocols..
23525 Roo.HtmlEditorCore.pwhite= [
23526         'http',  'https',  'mailto'
23527 ];
23528
23529 // white listed style attributes.
23530 Roo.HtmlEditorCore.cwhite= [
23531       //  'text-align', /// default is to allow most things..
23532       
23533          
23534 //        'font-size'//??
23535 ];
23536
23537 // black listed style attributes.
23538 Roo.HtmlEditorCore.cblack= [
23539       //  'font-size' -- this can be set by the project 
23540 ];
23541
23542
23543 Roo.HtmlEditorCore.swapCodes   =[ 
23544     [    8211, "--" ], 
23545     [    8212, "--" ], 
23546     [    8216,  "'" ],  
23547     [    8217, "'" ],  
23548     [    8220, '"' ],  
23549     [    8221, '"' ],  
23550     [    8226, "*" ],  
23551     [    8230, "..." ]
23552 ]; 
23553
23554     /*
23555  * - LGPL
23556  *
23557  * HtmlEditor
23558  * 
23559  */
23560
23561 /**
23562  * @class Roo.bootstrap.HtmlEditor
23563  * @extends Roo.bootstrap.TextArea
23564  * Bootstrap HtmlEditor class
23565
23566  * @constructor
23567  * Create a new HtmlEditor
23568  * @param {Object} config The config object
23569  */
23570
23571 Roo.bootstrap.HtmlEditor = function(config){
23572     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23573     if (!this.toolbars) {
23574         this.toolbars = [];
23575     }
23576     
23577     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23578     this.addEvents({
23579             /**
23580              * @event initialize
23581              * Fires when the editor is fully initialized (including the iframe)
23582              * @param {HtmlEditor} this
23583              */
23584             initialize: true,
23585             /**
23586              * @event activate
23587              * Fires when the editor is first receives the focus. Any insertion must wait
23588              * until after this event.
23589              * @param {HtmlEditor} this
23590              */
23591             activate: true,
23592              /**
23593              * @event beforesync
23594              * Fires before the textarea is updated with content from the editor iframe. Return false
23595              * to cancel the sync.
23596              * @param {HtmlEditor} this
23597              * @param {String} html
23598              */
23599             beforesync: true,
23600              /**
23601              * @event beforepush
23602              * Fires before the iframe editor is updated with content from the textarea. Return false
23603              * to cancel the push.
23604              * @param {HtmlEditor} this
23605              * @param {String} html
23606              */
23607             beforepush: true,
23608              /**
23609              * @event sync
23610              * Fires when the textarea is updated with content from the editor iframe.
23611              * @param {HtmlEditor} this
23612              * @param {String} html
23613              */
23614             sync: true,
23615              /**
23616              * @event push
23617              * Fires when the iframe editor is updated with content from the textarea.
23618              * @param {HtmlEditor} this
23619              * @param {String} html
23620              */
23621             push: true,
23622              /**
23623              * @event editmodechange
23624              * Fires when the editor switches edit modes
23625              * @param {HtmlEditor} this
23626              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23627              */
23628             editmodechange: true,
23629             /**
23630              * @event editorevent
23631              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23632              * @param {HtmlEditor} this
23633              */
23634             editorevent: true,
23635             /**
23636              * @event firstfocus
23637              * Fires when on first focus - needed by toolbars..
23638              * @param {HtmlEditor} this
23639              */
23640             firstfocus: true,
23641             /**
23642              * @event autosave
23643              * Auto save the htmlEditor value as a file into Events
23644              * @param {HtmlEditor} this
23645              */
23646             autosave: true,
23647             /**
23648              * @event savedpreview
23649              * preview the saved version of htmlEditor
23650              * @param {HtmlEditor} this
23651              */
23652             savedpreview: true
23653         });
23654 };
23655
23656
23657 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23658     
23659     
23660       /**
23661      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23662      */
23663     toolbars : false,
23664     
23665      /**
23666     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23667     */
23668     btns : [],
23669    
23670      /**
23671      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23672      *                        Roo.resizable.
23673      */
23674     resizable : false,
23675      /**
23676      * @cfg {Number} height (in pixels)
23677      */   
23678     height: 300,
23679    /**
23680      * @cfg {Number} width (in pixels)
23681      */   
23682     width: false,
23683     
23684     /**
23685      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23686      * 
23687      */
23688     stylesheets: false,
23689     
23690     // id of frame..
23691     frameId: false,
23692     
23693     // private properties
23694     validationEvent : false,
23695     deferHeight: true,
23696     initialized : false,
23697     activated : false,
23698     
23699     onFocus : Roo.emptyFn,
23700     iframePad:3,
23701     hideMode:'offsets',
23702     
23703     tbContainer : false,
23704     
23705     bodyCls : '',
23706     
23707     toolbarContainer :function() {
23708         return this.wrap.select('.x-html-editor-tb',true).first();
23709     },
23710
23711     /**
23712      * Protected method that will not generally be called directly. It
23713      * is called when the editor creates its toolbar. Override this method if you need to
23714      * add custom toolbar buttons.
23715      * @param {HtmlEditor} editor
23716      */
23717     createToolbar : function(){
23718         Roo.log('renewing');
23719         Roo.log("create toolbars");
23720         
23721         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23722         this.toolbars[0].render(this.toolbarContainer());
23723         
23724         return;
23725         
23726 //        if (!editor.toolbars || !editor.toolbars.length) {
23727 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23728 //        }
23729 //        
23730 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23731 //            editor.toolbars[i] = Roo.factory(
23732 //                    typeof(editor.toolbars[i]) == 'string' ?
23733 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23734 //                Roo.bootstrap.HtmlEditor);
23735 //            editor.toolbars[i].init(editor);
23736 //        }
23737     },
23738
23739      
23740     // private
23741     onRender : function(ct, position)
23742     {
23743        // Roo.log("Call onRender: " + this.xtype);
23744         var _t = this;
23745         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23746       
23747         this.wrap = this.inputEl().wrap({
23748             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23749         });
23750         
23751         this.editorcore.onRender(ct, position);
23752          
23753         if (this.resizable) {
23754             this.resizeEl = new Roo.Resizable(this.wrap, {
23755                 pinned : true,
23756                 wrap: true,
23757                 dynamic : true,
23758                 minHeight : this.height,
23759                 height: this.height,
23760                 handles : this.resizable,
23761                 width: this.width,
23762                 listeners : {
23763                     resize : function(r, w, h) {
23764                         _t.onResize(w,h); // -something
23765                     }
23766                 }
23767             });
23768             
23769         }
23770         this.createToolbar(this);
23771        
23772         
23773         if(!this.width && this.resizable){
23774             this.setSize(this.wrap.getSize());
23775         }
23776         if (this.resizeEl) {
23777             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23778             // should trigger onReize..
23779         }
23780         
23781     },
23782
23783     // private
23784     onResize : function(w, h)
23785     {
23786         Roo.log('resize: ' +w + ',' + h );
23787         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23788         var ew = false;
23789         var eh = false;
23790         
23791         if(this.inputEl() ){
23792             if(typeof w == 'number'){
23793                 var aw = w - this.wrap.getFrameWidth('lr');
23794                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23795                 ew = aw;
23796             }
23797             if(typeof h == 'number'){
23798                  var tbh = -11;  // fixme it needs to tool bar size!
23799                 for (var i =0; i < this.toolbars.length;i++) {
23800                     // fixme - ask toolbars for heights?
23801                     tbh += this.toolbars[i].el.getHeight();
23802                     //if (this.toolbars[i].footer) {
23803                     //    tbh += this.toolbars[i].footer.el.getHeight();
23804                     //}
23805                 }
23806               
23807                 
23808                 
23809                 
23810                 
23811                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23812                 ah -= 5; // knock a few pixes off for look..
23813                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23814                 var eh = ah;
23815             }
23816         }
23817         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23818         this.editorcore.onResize(ew,eh);
23819         
23820     },
23821
23822     /**
23823      * Toggles the editor between standard and source edit mode.
23824      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23825      */
23826     toggleSourceEdit : function(sourceEditMode)
23827     {
23828         this.editorcore.toggleSourceEdit(sourceEditMode);
23829         
23830         if(this.editorcore.sourceEditMode){
23831             Roo.log('editor - showing textarea');
23832             
23833 //            Roo.log('in');
23834 //            Roo.log(this.syncValue());
23835             this.syncValue();
23836             this.inputEl().removeClass(['hide', 'x-hidden']);
23837             this.inputEl().dom.removeAttribute('tabIndex');
23838             this.inputEl().focus();
23839         }else{
23840             Roo.log('editor - hiding textarea');
23841 //            Roo.log('out')
23842 //            Roo.log(this.pushValue()); 
23843             this.pushValue();
23844             
23845             this.inputEl().addClass(['hide', 'x-hidden']);
23846             this.inputEl().dom.setAttribute('tabIndex', -1);
23847             //this.deferFocus();
23848         }
23849          
23850         if(this.resizable){
23851             this.setSize(this.wrap.getSize());
23852         }
23853         
23854         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23855     },
23856  
23857     // private (for BoxComponent)
23858     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23859
23860     // private (for BoxComponent)
23861     getResizeEl : function(){
23862         return this.wrap;
23863     },
23864
23865     // private (for BoxComponent)
23866     getPositionEl : function(){
23867         return this.wrap;
23868     },
23869
23870     // private
23871     initEvents : function(){
23872         this.originalValue = this.getValue();
23873     },
23874
23875 //    /**
23876 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23877 //     * @method
23878 //     */
23879 //    markInvalid : Roo.emptyFn,
23880 //    /**
23881 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23882 //     * @method
23883 //     */
23884 //    clearInvalid : Roo.emptyFn,
23885
23886     setValue : function(v){
23887         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23888         this.editorcore.pushValue();
23889     },
23890
23891      
23892     // private
23893     deferFocus : function(){
23894         this.focus.defer(10, this);
23895     },
23896
23897     // doc'ed in Field
23898     focus : function(){
23899         this.editorcore.focus();
23900         
23901     },
23902       
23903
23904     // private
23905     onDestroy : function(){
23906         
23907         
23908         
23909         if(this.rendered){
23910             
23911             for (var i =0; i < this.toolbars.length;i++) {
23912                 // fixme - ask toolbars for heights?
23913                 this.toolbars[i].onDestroy();
23914             }
23915             
23916             this.wrap.dom.innerHTML = '';
23917             this.wrap.remove();
23918         }
23919     },
23920
23921     // private
23922     onFirstFocus : function(){
23923         //Roo.log("onFirstFocus");
23924         this.editorcore.onFirstFocus();
23925          for (var i =0; i < this.toolbars.length;i++) {
23926             this.toolbars[i].onFirstFocus();
23927         }
23928         
23929     },
23930     
23931     // private
23932     syncValue : function()
23933     {   
23934         this.editorcore.syncValue();
23935     },
23936     
23937     pushValue : function()
23938     {   
23939         this.editorcore.pushValue();
23940     }
23941      
23942     
23943     // hide stuff that is not compatible
23944     /**
23945      * @event blur
23946      * @hide
23947      */
23948     /**
23949      * @event change
23950      * @hide
23951      */
23952     /**
23953      * @event focus
23954      * @hide
23955      */
23956     /**
23957      * @event specialkey
23958      * @hide
23959      */
23960     /**
23961      * @cfg {String} fieldClass @hide
23962      */
23963     /**
23964      * @cfg {String} focusClass @hide
23965      */
23966     /**
23967      * @cfg {String} autoCreate @hide
23968      */
23969     /**
23970      * @cfg {String} inputType @hide
23971      */
23972      
23973     /**
23974      * @cfg {String} invalidText @hide
23975      */
23976     /**
23977      * @cfg {String} msgFx @hide
23978      */
23979     /**
23980      * @cfg {String} validateOnBlur @hide
23981      */
23982 });
23983  
23984     
23985    
23986    
23987    
23988       
23989 Roo.namespace('Roo.bootstrap.htmleditor');
23990 /**
23991  * @class Roo.bootstrap.HtmlEditorToolbar1
23992  * Basic Toolbar
23993  * 
23994  * Usage:
23995  *
23996  new Roo.bootstrap.HtmlEditor({
23997     ....
23998     toolbars : [
23999         new Roo.bootstrap.HtmlEditorToolbar1({
24000             disable : { fonts: 1 , format: 1, ..., ... , ...],
24001             btns : [ .... ]
24002         })
24003     }
24004      
24005  * 
24006  * @cfg {Object} disable List of elements to disable..
24007  * @cfg {Array} btns List of additional buttons.
24008  * 
24009  * 
24010  * NEEDS Extra CSS? 
24011  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24012  */
24013  
24014 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24015 {
24016     
24017     Roo.apply(this, config);
24018     
24019     // default disabled, based on 'good practice'..
24020     this.disable = this.disable || {};
24021     Roo.applyIf(this.disable, {
24022         fontSize : true,
24023         colors : true,
24024         specialElements : true
24025     });
24026     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24027     
24028     this.editor = config.editor;
24029     this.editorcore = config.editor.editorcore;
24030     
24031     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24032     
24033     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24034     // dont call parent... till later.
24035 }
24036 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
24037      
24038     bar : true,
24039     
24040     editor : false,
24041     editorcore : false,
24042     
24043     
24044     formats : [
24045         "p" ,  
24046         "h1","h2","h3","h4","h5","h6", 
24047         "pre", "code", 
24048         "abbr", "acronym", "address", "cite", "samp", "var",
24049         'div','span'
24050     ],
24051     
24052     onRender : function(ct, position)
24053     {
24054        // Roo.log("Call onRender: " + this.xtype);
24055         
24056        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24057        Roo.log(this.el);
24058        this.el.dom.style.marginBottom = '0';
24059        var _this = this;
24060        var editorcore = this.editorcore;
24061        var editor= this.editor;
24062        
24063        var children = [];
24064        var btn = function(id,cmd , toggle, handler, html){
24065        
24066             var  event = toggle ? 'toggle' : 'click';
24067        
24068             var a = {
24069                 size : 'sm',
24070                 xtype: 'Button',
24071                 xns: Roo.bootstrap,
24072                 //glyphicon : id,
24073                 fa: id,
24074                 cmd : id || cmd,
24075                 enableToggle:toggle !== false,
24076                 html : html || '',
24077                 pressed : toggle ? false : null,
24078                 listeners : {}
24079             };
24080             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24081                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24082             };
24083             children.push(a);
24084             return a;
24085        }
24086        
24087     //    var cb_box = function...
24088         
24089         var style = {
24090                 xtype: 'Button',
24091                 size : 'sm',
24092                 xns: Roo.bootstrap,
24093                 fa : 'font',
24094                 //html : 'submit'
24095                 menu : {
24096                     xtype: 'Menu',
24097                     xns: Roo.bootstrap,
24098                     items:  []
24099                 }
24100         };
24101         Roo.each(this.formats, function(f) {
24102             style.menu.items.push({
24103                 xtype :'MenuItem',
24104                 xns: Roo.bootstrap,
24105                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24106                 tagname : f,
24107                 listeners : {
24108                     click : function()
24109                     {
24110                         editorcore.insertTag(this.tagname);
24111                         editor.focus();
24112                     }
24113                 }
24114                 
24115             });
24116         });
24117         children.push(style);   
24118         
24119         btn('bold',false,true);
24120         btn('italic',false,true);
24121         btn('align-left', 'justifyleft',true);
24122         btn('align-center', 'justifycenter',true);
24123         btn('align-right' , 'justifyright',true);
24124         btn('link', false, false, function(btn) {
24125             //Roo.log("create link?");
24126             var url = prompt(this.createLinkText, this.defaultLinkValue);
24127             if(url && url != 'http:/'+'/'){
24128                 this.editorcore.relayCmd('createlink', url);
24129             }
24130         }),
24131         btn('list','insertunorderedlist',true);
24132         btn('pencil', false,true, function(btn){
24133                 Roo.log(this);
24134                 this.toggleSourceEdit(btn.pressed);
24135         });
24136         
24137         if (this.editor.btns.length > 0) {
24138             for (var i = 0; i<this.editor.btns.length; i++) {
24139                 children.push(this.editor.btns[i]);
24140             }
24141         }
24142         
24143         /*
24144         var cog = {
24145                 xtype: 'Button',
24146                 size : 'sm',
24147                 xns: Roo.bootstrap,
24148                 glyphicon : 'cog',
24149                 //html : 'submit'
24150                 menu : {
24151                     xtype: 'Menu',
24152                     xns: Roo.bootstrap,
24153                     items:  []
24154                 }
24155         };
24156         
24157         cog.menu.items.push({
24158             xtype :'MenuItem',
24159             xns: Roo.bootstrap,
24160             html : Clean styles,
24161             tagname : f,
24162             listeners : {
24163                 click : function()
24164                 {
24165                     editorcore.insertTag(this.tagname);
24166                     editor.focus();
24167                 }
24168             }
24169             
24170         });
24171        */
24172         
24173          
24174        this.xtype = 'NavSimplebar';
24175         
24176         for(var i=0;i< children.length;i++) {
24177             
24178             this.buttons.add(this.addxtypeChild(children[i]));
24179             
24180         }
24181         
24182         editor.on('editorevent', this.updateToolbar, this);
24183     },
24184     onBtnClick : function(id)
24185     {
24186        this.editorcore.relayCmd(id);
24187        this.editorcore.focus();
24188     },
24189     
24190     /**
24191      * Protected method that will not generally be called directly. It triggers
24192      * a toolbar update by reading the markup state of the current selection in the editor.
24193      */
24194     updateToolbar: function(){
24195
24196         if(!this.editorcore.activated){
24197             this.editor.onFirstFocus(); // is this neeed?
24198             return;
24199         }
24200
24201         var btns = this.buttons; 
24202         var doc = this.editorcore.doc;
24203         btns.get('bold').setActive(doc.queryCommandState('bold'));
24204         btns.get('italic').setActive(doc.queryCommandState('italic'));
24205         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24206         
24207         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24208         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24209         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24210         
24211         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24212         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24213          /*
24214         
24215         var ans = this.editorcore.getAllAncestors();
24216         if (this.formatCombo) {
24217             
24218             
24219             var store = this.formatCombo.store;
24220             this.formatCombo.setValue("");
24221             for (var i =0; i < ans.length;i++) {
24222                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24223                     // select it..
24224                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24225                     break;
24226                 }
24227             }
24228         }
24229         
24230         
24231         
24232         // hides menus... - so this cant be on a menu...
24233         Roo.bootstrap.MenuMgr.hideAll();
24234         */
24235         Roo.bootstrap.MenuMgr.hideAll();
24236         //this.editorsyncValue();
24237     },
24238     onFirstFocus: function() {
24239         this.buttons.each(function(item){
24240            item.enable();
24241         });
24242     },
24243     toggleSourceEdit : function(sourceEditMode){
24244         
24245           
24246         if(sourceEditMode){
24247             Roo.log("disabling buttons");
24248            this.buttons.each( function(item){
24249                 if(item.cmd != 'pencil'){
24250                     item.disable();
24251                 }
24252             });
24253           
24254         }else{
24255             Roo.log("enabling buttons");
24256             if(this.editorcore.initialized){
24257                 this.buttons.each( function(item){
24258                     item.enable();
24259                 });
24260             }
24261             
24262         }
24263         Roo.log("calling toggole on editor");
24264         // tell the editor that it's been pressed..
24265         this.editor.toggleSourceEdit(sourceEditMode);
24266        
24267     }
24268 });
24269
24270
24271
24272
24273
24274 /**
24275  * @class Roo.bootstrap.Table.AbstractSelectionModel
24276  * @extends Roo.util.Observable
24277  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24278  * implemented by descendant classes.  This class should not be directly instantiated.
24279  * @constructor
24280  */
24281 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24282     this.locked = false;
24283     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24284 };
24285
24286
24287 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24288     /** @ignore Called by the grid automatically. Do not call directly. */
24289     init : function(grid){
24290         this.grid = grid;
24291         this.initEvents();
24292     },
24293
24294     /**
24295      * Locks the selections.
24296      */
24297     lock : function(){
24298         this.locked = true;
24299     },
24300
24301     /**
24302      * Unlocks the selections.
24303      */
24304     unlock : function(){
24305         this.locked = false;
24306     },
24307
24308     /**
24309      * Returns true if the selections are locked.
24310      * @return {Boolean}
24311      */
24312     isLocked : function(){
24313         return this.locked;
24314     }
24315 });
24316 /**
24317  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24318  * @class Roo.bootstrap.Table.RowSelectionModel
24319  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24320  * It supports multiple selections and keyboard selection/navigation. 
24321  * @constructor
24322  * @param {Object} config
24323  */
24324
24325 Roo.bootstrap.Table.RowSelectionModel = function(config){
24326     Roo.apply(this, config);
24327     this.selections = new Roo.util.MixedCollection(false, function(o){
24328         return o.id;
24329     });
24330
24331     this.last = false;
24332     this.lastActive = false;
24333
24334     this.addEvents({
24335         /**
24336              * @event selectionchange
24337              * Fires when the selection changes
24338              * @param {SelectionModel} this
24339              */
24340             "selectionchange" : true,
24341         /**
24342              * @event afterselectionchange
24343              * Fires after the selection changes (eg. by key press or clicking)
24344              * @param {SelectionModel} this
24345              */
24346             "afterselectionchange" : true,
24347         /**
24348              * @event beforerowselect
24349              * Fires when a row is selected being selected, return false to cancel.
24350              * @param {SelectionModel} this
24351              * @param {Number} rowIndex The selected index
24352              * @param {Boolean} keepExisting False if other selections will be cleared
24353              */
24354             "beforerowselect" : true,
24355         /**
24356              * @event rowselect
24357              * Fires when a row is selected.
24358              * @param {SelectionModel} this
24359              * @param {Number} rowIndex The selected index
24360              * @param {Roo.data.Record} r The record
24361              */
24362             "rowselect" : true,
24363         /**
24364              * @event rowdeselect
24365              * Fires when a row is deselected.
24366              * @param {SelectionModel} this
24367              * @param {Number} rowIndex The selected index
24368              */
24369         "rowdeselect" : true
24370     });
24371     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24372     this.locked = false;
24373  };
24374
24375 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24376     /**
24377      * @cfg {Boolean} singleSelect
24378      * True to allow selection of only one row at a time (defaults to false)
24379      */
24380     singleSelect : false,
24381
24382     // private
24383     initEvents : function()
24384     {
24385
24386         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24387         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24388         //}else{ // allow click to work like normal
24389          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24390         //}
24391         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24392         this.grid.on("rowclick", this.handleMouseDown, this);
24393         
24394         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24395             "up" : function(e){
24396                 if(!e.shiftKey){
24397                     this.selectPrevious(e.shiftKey);
24398                 }else if(this.last !== false && this.lastActive !== false){
24399                     var last = this.last;
24400                     this.selectRange(this.last,  this.lastActive-1);
24401                     this.grid.getView().focusRow(this.lastActive);
24402                     if(last !== false){
24403                         this.last = last;
24404                     }
24405                 }else{
24406                     this.selectFirstRow();
24407                 }
24408                 this.fireEvent("afterselectionchange", this);
24409             },
24410             "down" : function(e){
24411                 if(!e.shiftKey){
24412                     this.selectNext(e.shiftKey);
24413                 }else if(this.last !== false && this.lastActive !== false){
24414                     var last = this.last;
24415                     this.selectRange(this.last,  this.lastActive+1);
24416                     this.grid.getView().focusRow(this.lastActive);
24417                     if(last !== false){
24418                         this.last = last;
24419                     }
24420                 }else{
24421                     this.selectFirstRow();
24422                 }
24423                 this.fireEvent("afterselectionchange", this);
24424             },
24425             scope: this
24426         });
24427         this.grid.store.on('load', function(){
24428             this.selections.clear();
24429         },this);
24430         /*
24431         var view = this.grid.view;
24432         view.on("refresh", this.onRefresh, this);
24433         view.on("rowupdated", this.onRowUpdated, this);
24434         view.on("rowremoved", this.onRemove, this);
24435         */
24436     },
24437
24438     // private
24439     onRefresh : function()
24440     {
24441         var ds = this.grid.store, i, v = this.grid.view;
24442         var s = this.selections;
24443         s.each(function(r){
24444             if((i = ds.indexOfId(r.id)) != -1){
24445                 v.onRowSelect(i);
24446             }else{
24447                 s.remove(r);
24448             }
24449         });
24450     },
24451
24452     // private
24453     onRemove : function(v, index, r){
24454         this.selections.remove(r);
24455     },
24456
24457     // private
24458     onRowUpdated : function(v, index, r){
24459         if(this.isSelected(r)){
24460             v.onRowSelect(index);
24461         }
24462     },
24463
24464     /**
24465      * Select records.
24466      * @param {Array} records The records to select
24467      * @param {Boolean} keepExisting (optional) True to keep existing selections
24468      */
24469     selectRecords : function(records, keepExisting)
24470     {
24471         if(!keepExisting){
24472             this.clearSelections();
24473         }
24474             var ds = this.grid.store;
24475         for(var i = 0, len = records.length; i < len; i++){
24476             this.selectRow(ds.indexOf(records[i]), true);
24477         }
24478     },
24479
24480     /**
24481      * Gets the number of selected rows.
24482      * @return {Number}
24483      */
24484     getCount : function(){
24485         return this.selections.length;
24486     },
24487
24488     /**
24489      * Selects the first row in the grid.
24490      */
24491     selectFirstRow : function(){
24492         this.selectRow(0);
24493     },
24494
24495     /**
24496      * Select the last row.
24497      * @param {Boolean} keepExisting (optional) True to keep existing selections
24498      */
24499     selectLastRow : function(keepExisting){
24500         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24501         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24502     },
24503
24504     /**
24505      * Selects the row immediately following the last selected row.
24506      * @param {Boolean} keepExisting (optional) True to keep existing selections
24507      */
24508     selectNext : function(keepExisting)
24509     {
24510             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24511             this.selectRow(this.last+1, keepExisting);
24512             this.grid.getView().focusRow(this.last);
24513         }
24514     },
24515
24516     /**
24517      * Selects the row that precedes the last selected row.
24518      * @param {Boolean} keepExisting (optional) True to keep existing selections
24519      */
24520     selectPrevious : function(keepExisting){
24521         if(this.last){
24522             this.selectRow(this.last-1, keepExisting);
24523             this.grid.getView().focusRow(this.last);
24524         }
24525     },
24526
24527     /**
24528      * Returns the selected records
24529      * @return {Array} Array of selected records
24530      */
24531     getSelections : function(){
24532         return [].concat(this.selections.items);
24533     },
24534
24535     /**
24536      * Returns the first selected record.
24537      * @return {Record}
24538      */
24539     getSelected : function(){
24540         return this.selections.itemAt(0);
24541     },
24542
24543
24544     /**
24545      * Clears all selections.
24546      */
24547     clearSelections : function(fast)
24548     {
24549         if(this.locked) {
24550             return;
24551         }
24552         if(fast !== true){
24553                 var ds = this.grid.store;
24554             var s = this.selections;
24555             s.each(function(r){
24556                 this.deselectRow(ds.indexOfId(r.id));
24557             }, this);
24558             s.clear();
24559         }else{
24560             this.selections.clear();
24561         }
24562         this.last = false;
24563     },
24564
24565
24566     /**
24567      * Selects all rows.
24568      */
24569     selectAll : function(){
24570         if(this.locked) {
24571             return;
24572         }
24573         this.selections.clear();
24574         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24575             this.selectRow(i, true);
24576         }
24577     },
24578
24579     /**
24580      * Returns True if there is a selection.
24581      * @return {Boolean}
24582      */
24583     hasSelection : function(){
24584         return this.selections.length > 0;
24585     },
24586
24587     /**
24588      * Returns True if the specified row is selected.
24589      * @param {Number/Record} record The record or index of the record to check
24590      * @return {Boolean}
24591      */
24592     isSelected : function(index){
24593             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24594         return (r && this.selections.key(r.id) ? true : false);
24595     },
24596
24597     /**
24598      * Returns True if the specified record id is selected.
24599      * @param {String} id The id of record to check
24600      * @return {Boolean}
24601      */
24602     isIdSelected : function(id){
24603         return (this.selections.key(id) ? true : false);
24604     },
24605
24606
24607     // private
24608     handleMouseDBClick : function(e, t){
24609         
24610     },
24611     // private
24612     handleMouseDown : function(e, t)
24613     {
24614             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24615         if(this.isLocked() || rowIndex < 0 ){
24616             return;
24617         };
24618         if(e.shiftKey && this.last !== false){
24619             var last = this.last;
24620             this.selectRange(last, rowIndex, e.ctrlKey);
24621             this.last = last; // reset the last
24622             t.focus();
24623     
24624         }else{
24625             var isSelected = this.isSelected(rowIndex);
24626             //Roo.log("select row:" + rowIndex);
24627             if(isSelected){
24628                 this.deselectRow(rowIndex);
24629             } else {
24630                         this.selectRow(rowIndex, true);
24631             }
24632     
24633             /*
24634                 if(e.button !== 0 && isSelected){
24635                 alert('rowIndex 2: ' + rowIndex);
24636                     view.focusRow(rowIndex);
24637                 }else if(e.ctrlKey && isSelected){
24638                     this.deselectRow(rowIndex);
24639                 }else if(!isSelected){
24640                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24641                     view.focusRow(rowIndex);
24642                 }
24643             */
24644         }
24645         this.fireEvent("afterselectionchange", this);
24646     },
24647     // private
24648     handleDragableRowClick :  function(grid, rowIndex, e) 
24649     {
24650         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24651             this.selectRow(rowIndex, false);
24652             grid.view.focusRow(rowIndex);
24653              this.fireEvent("afterselectionchange", this);
24654         }
24655     },
24656     
24657     /**
24658      * Selects multiple rows.
24659      * @param {Array} rows Array of the indexes of the row to select
24660      * @param {Boolean} keepExisting (optional) True to keep existing selections
24661      */
24662     selectRows : function(rows, keepExisting){
24663         if(!keepExisting){
24664             this.clearSelections();
24665         }
24666         for(var i = 0, len = rows.length; i < len; i++){
24667             this.selectRow(rows[i], true);
24668         }
24669     },
24670
24671     /**
24672      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24673      * @param {Number} startRow The index of the first row in the range
24674      * @param {Number} endRow The index of the last row in the range
24675      * @param {Boolean} keepExisting (optional) True to retain existing selections
24676      */
24677     selectRange : function(startRow, endRow, keepExisting){
24678         if(this.locked) {
24679             return;
24680         }
24681         if(!keepExisting){
24682             this.clearSelections();
24683         }
24684         if(startRow <= endRow){
24685             for(var i = startRow; i <= endRow; i++){
24686                 this.selectRow(i, true);
24687             }
24688         }else{
24689             for(var i = startRow; i >= endRow; i--){
24690                 this.selectRow(i, true);
24691             }
24692         }
24693     },
24694
24695     /**
24696      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24697      * @param {Number} startRow The index of the first row in the range
24698      * @param {Number} endRow The index of the last row in the range
24699      */
24700     deselectRange : function(startRow, endRow, preventViewNotify){
24701         if(this.locked) {
24702             return;
24703         }
24704         for(var i = startRow; i <= endRow; i++){
24705             this.deselectRow(i, preventViewNotify);
24706         }
24707     },
24708
24709     /**
24710      * Selects a row.
24711      * @param {Number} row The index of the row to select
24712      * @param {Boolean} keepExisting (optional) True to keep existing selections
24713      */
24714     selectRow : function(index, keepExisting, preventViewNotify)
24715     {
24716             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24717             return;
24718         }
24719         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24720             if(!keepExisting || this.singleSelect){
24721                 this.clearSelections();
24722             }
24723             
24724             var r = this.grid.store.getAt(index);
24725             //console.log('selectRow - record id :' + r.id);
24726             
24727             this.selections.add(r);
24728             this.last = this.lastActive = index;
24729             if(!preventViewNotify){
24730                 var proxy = new Roo.Element(
24731                                 this.grid.getRowDom(index)
24732                 );
24733                 proxy.addClass('bg-info info');
24734             }
24735             this.fireEvent("rowselect", this, index, r);
24736             this.fireEvent("selectionchange", this);
24737         }
24738     },
24739
24740     /**
24741      * Deselects a row.
24742      * @param {Number} row The index of the row to deselect
24743      */
24744     deselectRow : function(index, preventViewNotify)
24745     {
24746         if(this.locked) {
24747             return;
24748         }
24749         if(this.last == index){
24750             this.last = false;
24751         }
24752         if(this.lastActive == index){
24753             this.lastActive = false;
24754         }
24755         
24756         var r = this.grid.store.getAt(index);
24757         if (!r) {
24758             return;
24759         }
24760         
24761         this.selections.remove(r);
24762         //.console.log('deselectRow - record id :' + r.id);
24763         if(!preventViewNotify){
24764         
24765             var proxy = new Roo.Element(
24766                 this.grid.getRowDom(index)
24767             );
24768             proxy.removeClass('bg-info info');
24769         }
24770         this.fireEvent("rowdeselect", this, index);
24771         this.fireEvent("selectionchange", this);
24772     },
24773
24774     // private
24775     restoreLast : function(){
24776         if(this._last){
24777             this.last = this._last;
24778         }
24779     },
24780
24781     // private
24782     acceptsNav : function(row, col, cm){
24783         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24784     },
24785
24786     // private
24787     onEditorKey : function(field, e){
24788         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24789         if(k == e.TAB){
24790             e.stopEvent();
24791             ed.completeEdit();
24792             if(e.shiftKey){
24793                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24794             }else{
24795                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24796             }
24797         }else if(k == e.ENTER && !e.ctrlKey){
24798             e.stopEvent();
24799             ed.completeEdit();
24800             if(e.shiftKey){
24801                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24802             }else{
24803                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24804             }
24805         }else if(k == e.ESC){
24806             ed.cancelEdit();
24807         }
24808         if(newCell){
24809             g.startEditing(newCell[0], newCell[1]);
24810         }
24811     }
24812 });
24813 /*
24814  * Based on:
24815  * Ext JS Library 1.1.1
24816  * Copyright(c) 2006-2007, Ext JS, LLC.
24817  *
24818  * Originally Released Under LGPL - original licence link has changed is not relivant.
24819  *
24820  * Fork - LGPL
24821  * <script type="text/javascript">
24822  */
24823  
24824 /**
24825  * @class Roo.bootstrap.PagingToolbar
24826  * @extends Roo.bootstrap.NavSimplebar
24827  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24828  * @constructor
24829  * Create a new PagingToolbar
24830  * @param {Object} config The config object
24831  * @param {Roo.data.Store} store
24832  */
24833 Roo.bootstrap.PagingToolbar = function(config)
24834 {
24835     // old args format still supported... - xtype is prefered..
24836         // created from xtype...
24837     
24838     this.ds = config.dataSource;
24839     
24840     if (config.store && !this.ds) {
24841         this.store= Roo.factory(config.store, Roo.data);
24842         this.ds = this.store;
24843         this.ds.xmodule = this.xmodule || false;
24844     }
24845     
24846     this.toolbarItems = [];
24847     if (config.items) {
24848         this.toolbarItems = config.items;
24849     }
24850     
24851     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24852     
24853     this.cursor = 0;
24854     
24855     if (this.ds) { 
24856         this.bind(this.ds);
24857     }
24858     
24859     if (Roo.bootstrap.version == 4) {
24860         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24861     } else {
24862         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24863     }
24864     
24865 };
24866
24867 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24868     /**
24869      * @cfg {Roo.data.Store} dataSource
24870      * The underlying data store providing the paged data
24871      */
24872     /**
24873      * @cfg {String/HTMLElement/Element} container
24874      * container The id or element that will contain the toolbar
24875      */
24876     /**
24877      * @cfg {Boolean} displayInfo
24878      * True to display the displayMsg (defaults to false)
24879      */
24880     /**
24881      * @cfg {Number} pageSize
24882      * The number of records to display per page (defaults to 20)
24883      */
24884     pageSize: 20,
24885     /**
24886      * @cfg {String} displayMsg
24887      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24888      */
24889     displayMsg : 'Displaying {0} - {1} of {2}',
24890     /**
24891      * @cfg {String} emptyMsg
24892      * The message to display when no records are found (defaults to "No data to display")
24893      */
24894     emptyMsg : 'No data to display',
24895     /**
24896      * Customizable piece of the default paging text (defaults to "Page")
24897      * @type String
24898      */
24899     beforePageText : "Page",
24900     /**
24901      * Customizable piece of the default paging text (defaults to "of %0")
24902      * @type String
24903      */
24904     afterPageText : "of {0}",
24905     /**
24906      * Customizable piece of the default paging text (defaults to "First Page")
24907      * @type String
24908      */
24909     firstText : "First Page",
24910     /**
24911      * Customizable piece of the default paging text (defaults to "Previous Page")
24912      * @type String
24913      */
24914     prevText : "Previous Page",
24915     /**
24916      * Customizable piece of the default paging text (defaults to "Next Page")
24917      * @type String
24918      */
24919     nextText : "Next Page",
24920     /**
24921      * Customizable piece of the default paging text (defaults to "Last Page")
24922      * @type String
24923      */
24924     lastText : "Last Page",
24925     /**
24926      * Customizable piece of the default paging text (defaults to "Refresh")
24927      * @type String
24928      */
24929     refreshText : "Refresh",
24930
24931     buttons : false,
24932     // private
24933     onRender : function(ct, position) 
24934     {
24935         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24936         this.navgroup.parentId = this.id;
24937         this.navgroup.onRender(this.el, null);
24938         // add the buttons to the navgroup
24939         
24940         if(this.displayInfo){
24941             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24942             this.displayEl = this.el.select('.x-paging-info', true).first();
24943 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24944 //            this.displayEl = navel.el.select('span',true).first();
24945         }
24946         
24947         var _this = this;
24948         
24949         if(this.buttons){
24950             Roo.each(_this.buttons, function(e){ // this might need to use render????
24951                Roo.factory(e).render(_this.el);
24952             });
24953         }
24954             
24955         Roo.each(_this.toolbarItems, function(e) {
24956             _this.navgroup.addItem(e);
24957         });
24958         
24959         
24960         this.first = this.navgroup.addItem({
24961             tooltip: this.firstText,
24962             cls: "prev btn-outline-secondary",
24963             html : ' <i class="fa fa-step-backward"></i>',
24964             disabled: true,
24965             preventDefault: true,
24966             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24967         });
24968         
24969         this.prev =  this.navgroup.addItem({
24970             tooltip: this.prevText,
24971             cls: "prev btn-outline-secondary",
24972             html : ' <i class="fa fa-backward"></i>',
24973             disabled: true,
24974             preventDefault: true,
24975             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24976         });
24977     //this.addSeparator();
24978         
24979         
24980         var field = this.navgroup.addItem( {
24981             tagtype : 'span',
24982             cls : 'x-paging-position  btn-outline-secondary',
24983              disabled: true,
24984             html : this.beforePageText  +
24985                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24986                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24987          } ); //?? escaped?
24988         
24989         this.field = field.el.select('input', true).first();
24990         this.field.on("keydown", this.onPagingKeydown, this);
24991         this.field.on("focus", function(){this.dom.select();});
24992     
24993     
24994         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24995         //this.field.setHeight(18);
24996         //this.addSeparator();
24997         this.next = this.navgroup.addItem({
24998             tooltip: this.nextText,
24999             cls: "next btn-outline-secondary",
25000             html : ' <i class="fa fa-forward"></i>',
25001             disabled: true,
25002             preventDefault: true,
25003             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
25004         });
25005         this.last = this.navgroup.addItem({
25006             tooltip: this.lastText,
25007             html : ' <i class="fa fa-step-forward"></i>',
25008             cls: "next btn-outline-secondary",
25009             disabled: true,
25010             preventDefault: true,
25011             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
25012         });
25013     //this.addSeparator();
25014         this.loading = this.navgroup.addItem({
25015             tooltip: this.refreshText,
25016             cls: "btn-outline-secondary",
25017             html : ' <i class="fa fa-refresh"></i>',
25018             preventDefault: true,
25019             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25020         });
25021         
25022     },
25023
25024     // private
25025     updateInfo : function(){
25026         if(this.displayEl){
25027             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25028             var msg = count == 0 ?
25029                 this.emptyMsg :
25030                 String.format(
25031                     this.displayMsg,
25032                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
25033                 );
25034             this.displayEl.update(msg);
25035         }
25036     },
25037
25038     // private
25039     onLoad : function(ds, r, o)
25040     {
25041         this.cursor = o.params.start ? o.params.start : 0;
25042         
25043         var d = this.getPageData(),
25044             ap = d.activePage,
25045             ps = d.pages;
25046         
25047         
25048         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25049         this.field.dom.value = ap;
25050         this.first.setDisabled(ap == 1);
25051         this.prev.setDisabled(ap == 1);
25052         this.next.setDisabled(ap == ps);
25053         this.last.setDisabled(ap == ps);
25054         this.loading.enable();
25055         this.updateInfo();
25056     },
25057
25058     // private
25059     getPageData : function(){
25060         var total = this.ds.getTotalCount();
25061         return {
25062             total : total,
25063             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25064             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25065         };
25066     },
25067
25068     // private
25069     onLoadError : function(){
25070         this.loading.enable();
25071     },
25072
25073     // private
25074     onPagingKeydown : function(e){
25075         var k = e.getKey();
25076         var d = this.getPageData();
25077         if(k == e.RETURN){
25078             var v = this.field.dom.value, pageNum;
25079             if(!v || isNaN(pageNum = parseInt(v, 10))){
25080                 this.field.dom.value = d.activePage;
25081                 return;
25082             }
25083             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25084             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25085             e.stopEvent();
25086         }
25087         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))
25088         {
25089           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25090           this.field.dom.value = pageNum;
25091           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25092           e.stopEvent();
25093         }
25094         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25095         {
25096           var v = this.field.dom.value, pageNum; 
25097           var increment = (e.shiftKey) ? 10 : 1;
25098           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25099                 increment *= -1;
25100           }
25101           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25102             this.field.dom.value = d.activePage;
25103             return;
25104           }
25105           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25106           {
25107             this.field.dom.value = parseInt(v, 10) + increment;
25108             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25109             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25110           }
25111           e.stopEvent();
25112         }
25113     },
25114
25115     // private
25116     beforeLoad : function(){
25117         if(this.loading){
25118             this.loading.disable();
25119         }
25120     },
25121
25122     // private
25123     onClick : function(which){
25124         
25125         var ds = this.ds;
25126         if (!ds) {
25127             return;
25128         }
25129         
25130         switch(which){
25131             case "first":
25132                 ds.load({params:{start: 0, limit: this.pageSize}});
25133             break;
25134             case "prev":
25135                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25136             break;
25137             case "next":
25138                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25139             break;
25140             case "last":
25141                 var total = ds.getTotalCount();
25142                 var extra = total % this.pageSize;
25143                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25144                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25145             break;
25146             case "refresh":
25147                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25148             break;
25149         }
25150     },
25151
25152     /**
25153      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25154      * @param {Roo.data.Store} store The data store to unbind
25155      */
25156     unbind : function(ds){
25157         ds.un("beforeload", this.beforeLoad, this);
25158         ds.un("load", this.onLoad, this);
25159         ds.un("loadexception", this.onLoadError, this);
25160         ds.un("remove", this.updateInfo, this);
25161         ds.un("add", this.updateInfo, this);
25162         this.ds = undefined;
25163     },
25164
25165     /**
25166      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25167      * @param {Roo.data.Store} store The data store to bind
25168      */
25169     bind : function(ds){
25170         ds.on("beforeload", this.beforeLoad, this);
25171         ds.on("load", this.onLoad, this);
25172         ds.on("loadexception", this.onLoadError, this);
25173         ds.on("remove", this.updateInfo, this);
25174         ds.on("add", this.updateInfo, this);
25175         this.ds = ds;
25176     }
25177 });/*
25178  * - LGPL
25179  *
25180  * element
25181  * 
25182  */
25183
25184 /**
25185  * @class Roo.bootstrap.MessageBar
25186  * @extends Roo.bootstrap.Component
25187  * Bootstrap MessageBar class
25188  * @cfg {String} html contents of the MessageBar
25189  * @cfg {String} weight (info | success | warning | danger) default info
25190  * @cfg {String} beforeClass insert the bar before the given class
25191  * @cfg {Boolean} closable (true | false) default false
25192  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25193  * 
25194  * @constructor
25195  * Create a new Element
25196  * @param {Object} config The config object
25197  */
25198
25199 Roo.bootstrap.MessageBar = function(config){
25200     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25201 };
25202
25203 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25204     
25205     html: '',
25206     weight: 'info',
25207     closable: false,
25208     fixed: false,
25209     beforeClass: 'bootstrap-sticky-wrap',
25210     
25211     getAutoCreate : function(){
25212         
25213         var cfg = {
25214             tag: 'div',
25215             cls: 'alert alert-dismissable alert-' + this.weight,
25216             cn: [
25217                 {
25218                     tag: 'span',
25219                     cls: 'message',
25220                     html: this.html || ''
25221                 }
25222             ]
25223         };
25224         
25225         if(this.fixed){
25226             cfg.cls += ' alert-messages-fixed';
25227         }
25228         
25229         if(this.closable){
25230             cfg.cn.push({
25231                 tag: 'button',
25232                 cls: 'close',
25233                 html: 'x'
25234             });
25235         }
25236         
25237         return cfg;
25238     },
25239     
25240     onRender : function(ct, position)
25241     {
25242         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25243         
25244         if(!this.el){
25245             var cfg = Roo.apply({},  this.getAutoCreate());
25246             cfg.id = Roo.id();
25247             
25248             if (this.cls) {
25249                 cfg.cls += ' ' + this.cls;
25250             }
25251             if (this.style) {
25252                 cfg.style = this.style;
25253             }
25254             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25255             
25256             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25257         }
25258         
25259         this.el.select('>button.close').on('click', this.hide, this);
25260         
25261     },
25262     
25263     show : function()
25264     {
25265         if (!this.rendered) {
25266             this.render();
25267         }
25268         
25269         this.el.show();
25270         
25271         this.fireEvent('show', this);
25272         
25273     },
25274     
25275     hide : function()
25276     {
25277         if (!this.rendered) {
25278             this.render();
25279         }
25280         
25281         this.el.hide();
25282         
25283         this.fireEvent('hide', this);
25284     },
25285     
25286     update : function()
25287     {
25288 //        var e = this.el.dom.firstChild;
25289 //        
25290 //        if(this.closable){
25291 //            e = e.nextSibling;
25292 //        }
25293 //        
25294 //        e.data = this.html || '';
25295
25296         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25297     }
25298    
25299 });
25300
25301  
25302
25303      /*
25304  * - LGPL
25305  *
25306  * Graph
25307  * 
25308  */
25309
25310
25311 /**
25312  * @class Roo.bootstrap.Graph
25313  * @extends Roo.bootstrap.Component
25314  * Bootstrap Graph class
25315 > Prameters
25316  -sm {number} sm 4
25317  -md {number} md 5
25318  @cfg {String} graphtype  bar | vbar | pie
25319  @cfg {number} g_x coodinator | centre x (pie)
25320  @cfg {number} g_y coodinator | centre y (pie)
25321  @cfg {number} g_r radius (pie)
25322  @cfg {number} g_height height of the chart (respected by all elements in the set)
25323  @cfg {number} g_width width of the chart (respected by all elements in the set)
25324  @cfg {Object} title The title of the chart
25325     
25326  -{Array}  values
25327  -opts (object) options for the chart 
25328      o {
25329      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25330      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25331      o vgutter (number)
25332      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.
25333      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25334      o to
25335      o stretch (boolean)
25336      o }
25337  -opts (object) options for the pie
25338      o{
25339      o cut
25340      o startAngle (number)
25341      o endAngle (number)
25342      } 
25343  *
25344  * @constructor
25345  * Create a new Input
25346  * @param {Object} config The config object
25347  */
25348
25349 Roo.bootstrap.Graph = function(config){
25350     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25351     
25352     this.addEvents({
25353         // img events
25354         /**
25355          * @event click
25356          * The img click event for the img.
25357          * @param {Roo.EventObject} e
25358          */
25359         "click" : true
25360     });
25361 };
25362
25363 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25364     
25365     sm: 4,
25366     md: 5,
25367     graphtype: 'bar',
25368     g_height: 250,
25369     g_width: 400,
25370     g_x: 50,
25371     g_y: 50,
25372     g_r: 30,
25373     opts:{
25374         //g_colors: this.colors,
25375         g_type: 'soft',
25376         g_gutter: '20%'
25377
25378     },
25379     title : false,
25380
25381     getAutoCreate : function(){
25382         
25383         var cfg = {
25384             tag: 'div',
25385             html : null
25386         };
25387         
25388         
25389         return  cfg;
25390     },
25391
25392     onRender : function(ct,position){
25393         
25394         
25395         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25396         
25397         if (typeof(Raphael) == 'undefined') {
25398             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25399             return;
25400         }
25401         
25402         this.raphael = Raphael(this.el.dom);
25403         
25404                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25405                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25406                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25407                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25408                 /*
25409                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25410                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25411                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25412                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25413                 
25414                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25415                 r.barchart(330, 10, 300, 220, data1);
25416                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25417                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25418                 */
25419                 
25420                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25421                 // r.barchart(30, 30, 560, 250,  xdata, {
25422                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25423                 //     axis : "0 0 1 1",
25424                 //     axisxlabels :  xdata
25425                 //     //yvalues : cols,
25426                    
25427                 // });
25428 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25429 //        
25430 //        this.load(null,xdata,{
25431 //                axis : "0 0 1 1",
25432 //                axisxlabels :  xdata
25433 //                });
25434
25435     },
25436
25437     load : function(graphtype,xdata,opts)
25438     {
25439         this.raphael.clear();
25440         if(!graphtype) {
25441             graphtype = this.graphtype;
25442         }
25443         if(!opts){
25444             opts = this.opts;
25445         }
25446         var r = this.raphael,
25447             fin = function () {
25448                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25449             },
25450             fout = function () {
25451                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25452             },
25453             pfin = function() {
25454                 this.sector.stop();
25455                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25456
25457                 if (this.label) {
25458                     this.label[0].stop();
25459                     this.label[0].attr({ r: 7.5 });
25460                     this.label[1].attr({ "font-weight": 800 });
25461                 }
25462             },
25463             pfout = function() {
25464                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25465
25466                 if (this.label) {
25467                     this.label[0].animate({ r: 5 }, 500, "bounce");
25468                     this.label[1].attr({ "font-weight": 400 });
25469                 }
25470             };
25471
25472         switch(graphtype){
25473             case 'bar':
25474                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25475                 break;
25476             case 'hbar':
25477                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25478                 break;
25479             case 'pie':
25480 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25481 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25482 //            
25483                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25484                 
25485                 break;
25486
25487         }
25488         
25489         if(this.title){
25490             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25491         }
25492         
25493     },
25494     
25495     setTitle: function(o)
25496     {
25497         this.title = o;
25498     },
25499     
25500     initEvents: function() {
25501         
25502         if(!this.href){
25503             this.el.on('click', this.onClick, this);
25504         }
25505     },
25506     
25507     onClick : function(e)
25508     {
25509         Roo.log('img onclick');
25510         this.fireEvent('click', this, e);
25511     }
25512    
25513 });
25514
25515  
25516 /*
25517  * - LGPL
25518  *
25519  * numberBox
25520  * 
25521  */
25522 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25523
25524 /**
25525  * @class Roo.bootstrap.dash.NumberBox
25526  * @extends Roo.bootstrap.Component
25527  * Bootstrap NumberBox class
25528  * @cfg {String} headline Box headline
25529  * @cfg {String} content Box content
25530  * @cfg {String} icon Box icon
25531  * @cfg {String} footer Footer text
25532  * @cfg {String} fhref Footer href
25533  * 
25534  * @constructor
25535  * Create a new NumberBox
25536  * @param {Object} config The config object
25537  */
25538
25539
25540 Roo.bootstrap.dash.NumberBox = function(config){
25541     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25542     
25543 };
25544
25545 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25546     
25547     headline : '',
25548     content : '',
25549     icon : '',
25550     footer : '',
25551     fhref : '',
25552     ficon : '',
25553     
25554     getAutoCreate : function(){
25555         
25556         var cfg = {
25557             tag : 'div',
25558             cls : 'small-box ',
25559             cn : [
25560                 {
25561                     tag : 'div',
25562                     cls : 'inner',
25563                     cn :[
25564                         {
25565                             tag : 'h3',
25566                             cls : 'roo-headline',
25567                             html : this.headline
25568                         },
25569                         {
25570                             tag : 'p',
25571                             cls : 'roo-content',
25572                             html : this.content
25573                         }
25574                     ]
25575                 }
25576             ]
25577         };
25578         
25579         if(this.icon){
25580             cfg.cn.push({
25581                 tag : 'div',
25582                 cls : 'icon',
25583                 cn :[
25584                     {
25585                         tag : 'i',
25586                         cls : 'ion ' + this.icon
25587                     }
25588                 ]
25589             });
25590         }
25591         
25592         if(this.footer){
25593             var footer = {
25594                 tag : 'a',
25595                 cls : 'small-box-footer',
25596                 href : this.fhref || '#',
25597                 html : this.footer
25598             };
25599             
25600             cfg.cn.push(footer);
25601             
25602         }
25603         
25604         return  cfg;
25605     },
25606
25607     onRender : function(ct,position){
25608         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25609
25610
25611        
25612                 
25613     },
25614
25615     setHeadline: function (value)
25616     {
25617         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25618     },
25619     
25620     setFooter: function (value, href)
25621     {
25622         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25623         
25624         if(href){
25625             this.el.select('a.small-box-footer',true).first().attr('href', href);
25626         }
25627         
25628     },
25629
25630     setContent: function (value)
25631     {
25632         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25633     },
25634
25635     initEvents: function() 
25636     {   
25637         
25638     }
25639     
25640 });
25641
25642  
25643 /*
25644  * - LGPL
25645  *
25646  * TabBox
25647  * 
25648  */
25649 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25650
25651 /**
25652  * @class Roo.bootstrap.dash.TabBox
25653  * @extends Roo.bootstrap.Component
25654  * Bootstrap TabBox class
25655  * @cfg {String} title Title of the TabBox
25656  * @cfg {String} icon Icon of the TabBox
25657  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25658  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25659  * 
25660  * @constructor
25661  * Create a new TabBox
25662  * @param {Object} config The config object
25663  */
25664
25665
25666 Roo.bootstrap.dash.TabBox = function(config){
25667     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25668     this.addEvents({
25669         // raw events
25670         /**
25671          * @event addpane
25672          * When a pane is added
25673          * @param {Roo.bootstrap.dash.TabPane} pane
25674          */
25675         "addpane" : true,
25676         /**
25677          * @event activatepane
25678          * When a pane is activated
25679          * @param {Roo.bootstrap.dash.TabPane} pane
25680          */
25681         "activatepane" : true
25682         
25683          
25684     });
25685     
25686     this.panes = [];
25687 };
25688
25689 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25690
25691     title : '',
25692     icon : false,
25693     showtabs : true,
25694     tabScrollable : false,
25695     
25696     getChildContainer : function()
25697     {
25698         return this.el.select('.tab-content', true).first();
25699     },
25700     
25701     getAutoCreate : function(){
25702         
25703         var header = {
25704             tag: 'li',
25705             cls: 'pull-left header',
25706             html: this.title,
25707             cn : []
25708         };
25709         
25710         if(this.icon){
25711             header.cn.push({
25712                 tag: 'i',
25713                 cls: 'fa ' + this.icon
25714             });
25715         }
25716         
25717         var h = {
25718             tag: 'ul',
25719             cls: 'nav nav-tabs pull-right',
25720             cn: [
25721                 header
25722             ]
25723         };
25724         
25725         if(this.tabScrollable){
25726             h = {
25727                 tag: 'div',
25728                 cls: 'tab-header',
25729                 cn: [
25730                     {
25731                         tag: 'ul',
25732                         cls: 'nav nav-tabs pull-right',
25733                         cn: [
25734                             header
25735                         ]
25736                     }
25737                 ]
25738             };
25739         }
25740         
25741         var cfg = {
25742             tag: 'div',
25743             cls: 'nav-tabs-custom',
25744             cn: [
25745                 h,
25746                 {
25747                     tag: 'div',
25748                     cls: 'tab-content no-padding',
25749                     cn: []
25750                 }
25751             ]
25752         };
25753
25754         return  cfg;
25755     },
25756     initEvents : function()
25757     {
25758         //Roo.log('add add pane handler');
25759         this.on('addpane', this.onAddPane, this);
25760     },
25761      /**
25762      * Updates the box title
25763      * @param {String} html to set the title to.
25764      */
25765     setTitle : function(value)
25766     {
25767         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25768     },
25769     onAddPane : function(pane)
25770     {
25771         this.panes.push(pane);
25772         //Roo.log('addpane');
25773         //Roo.log(pane);
25774         // tabs are rendere left to right..
25775         if(!this.showtabs){
25776             return;
25777         }
25778         
25779         var ctr = this.el.select('.nav-tabs', true).first();
25780          
25781          
25782         var existing = ctr.select('.nav-tab',true);
25783         var qty = existing.getCount();;
25784         
25785         
25786         var tab = ctr.createChild({
25787             tag : 'li',
25788             cls : 'nav-tab' + (qty ? '' : ' active'),
25789             cn : [
25790                 {
25791                     tag : 'a',
25792                     href:'#',
25793                     html : pane.title
25794                 }
25795             ]
25796         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25797         pane.tab = tab;
25798         
25799         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25800         if (!qty) {
25801             pane.el.addClass('active');
25802         }
25803         
25804                 
25805     },
25806     onTabClick : function(ev,un,ob,pane)
25807     {
25808         //Roo.log('tab - prev default');
25809         ev.preventDefault();
25810         
25811         
25812         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25813         pane.tab.addClass('active');
25814         //Roo.log(pane.title);
25815         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25816         // technically we should have a deactivate event.. but maybe add later.
25817         // and it should not de-activate the selected tab...
25818         this.fireEvent('activatepane', pane);
25819         pane.el.addClass('active');
25820         pane.fireEvent('activate');
25821         
25822         
25823     },
25824     
25825     getActivePane : function()
25826     {
25827         var r = false;
25828         Roo.each(this.panes, function(p) {
25829             if(p.el.hasClass('active')){
25830                 r = p;
25831                 return false;
25832             }
25833             
25834             return;
25835         });
25836         
25837         return r;
25838     }
25839     
25840     
25841 });
25842
25843  
25844 /*
25845  * - LGPL
25846  *
25847  * Tab pane
25848  * 
25849  */
25850 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25851 /**
25852  * @class Roo.bootstrap.TabPane
25853  * @extends Roo.bootstrap.Component
25854  * Bootstrap TabPane class
25855  * @cfg {Boolean} active (false | true) Default false
25856  * @cfg {String} title title of panel
25857
25858  * 
25859  * @constructor
25860  * Create a new TabPane
25861  * @param {Object} config The config object
25862  */
25863
25864 Roo.bootstrap.dash.TabPane = function(config){
25865     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25866     
25867     this.addEvents({
25868         // raw events
25869         /**
25870          * @event activate
25871          * When a pane is activated
25872          * @param {Roo.bootstrap.dash.TabPane} pane
25873          */
25874         "activate" : true
25875          
25876     });
25877 };
25878
25879 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25880     
25881     active : false,
25882     title : '',
25883     
25884     // the tabBox that this is attached to.
25885     tab : false,
25886      
25887     getAutoCreate : function() 
25888     {
25889         var cfg = {
25890             tag: 'div',
25891             cls: 'tab-pane'
25892         };
25893         
25894         if(this.active){
25895             cfg.cls += ' active';
25896         }
25897         
25898         return cfg;
25899     },
25900     initEvents  : function()
25901     {
25902         //Roo.log('trigger add pane handler');
25903         this.parent().fireEvent('addpane', this)
25904     },
25905     
25906      /**
25907      * Updates the tab title 
25908      * @param {String} html to set the title to.
25909      */
25910     setTitle: function(str)
25911     {
25912         if (!this.tab) {
25913             return;
25914         }
25915         this.title = str;
25916         this.tab.select('a', true).first().dom.innerHTML = str;
25917         
25918     }
25919     
25920     
25921     
25922 });
25923
25924  
25925
25926
25927  /*
25928  * - LGPL
25929  *
25930  * menu
25931  * 
25932  */
25933 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25934
25935 /**
25936  * @class Roo.bootstrap.menu.Menu
25937  * @extends Roo.bootstrap.Component
25938  * Bootstrap Menu class - container for Menu
25939  * @cfg {String} html Text of the menu
25940  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25941  * @cfg {String} icon Font awesome icon
25942  * @cfg {String} pos Menu align to (top | bottom) default bottom
25943  * 
25944  * 
25945  * @constructor
25946  * Create a new Menu
25947  * @param {Object} config The config object
25948  */
25949
25950
25951 Roo.bootstrap.menu.Menu = function(config){
25952     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25953     
25954     this.addEvents({
25955         /**
25956          * @event beforeshow
25957          * Fires before this menu is displayed
25958          * @param {Roo.bootstrap.menu.Menu} this
25959          */
25960         beforeshow : true,
25961         /**
25962          * @event beforehide
25963          * Fires before this menu is hidden
25964          * @param {Roo.bootstrap.menu.Menu} this
25965          */
25966         beforehide : true,
25967         /**
25968          * @event show
25969          * Fires after this menu is displayed
25970          * @param {Roo.bootstrap.menu.Menu} this
25971          */
25972         show : true,
25973         /**
25974          * @event hide
25975          * Fires after this menu is hidden
25976          * @param {Roo.bootstrap.menu.Menu} this
25977          */
25978         hide : true,
25979         /**
25980          * @event click
25981          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25982          * @param {Roo.bootstrap.menu.Menu} this
25983          * @param {Roo.EventObject} e
25984          */
25985         click : true
25986     });
25987     
25988 };
25989
25990 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25991     
25992     submenu : false,
25993     html : '',
25994     weight : 'default',
25995     icon : false,
25996     pos : 'bottom',
25997     
25998     
25999     getChildContainer : function() {
26000         if(this.isSubMenu){
26001             return this.el;
26002         }
26003         
26004         return this.el.select('ul.dropdown-menu', true).first();  
26005     },
26006     
26007     getAutoCreate : function()
26008     {
26009         var text = [
26010             {
26011                 tag : 'span',
26012                 cls : 'roo-menu-text',
26013                 html : this.html
26014             }
26015         ];
26016         
26017         if(this.icon){
26018             text.unshift({
26019                 tag : 'i',
26020                 cls : 'fa ' + this.icon
26021             })
26022         }
26023         
26024         
26025         var cfg = {
26026             tag : 'div',
26027             cls : 'btn-group',
26028             cn : [
26029                 {
26030                     tag : 'button',
26031                     cls : 'dropdown-button btn btn-' + this.weight,
26032                     cn : text
26033                 },
26034                 {
26035                     tag : 'button',
26036                     cls : 'dropdown-toggle btn btn-' + this.weight,
26037                     cn : [
26038                         {
26039                             tag : 'span',
26040                             cls : 'caret'
26041                         }
26042                     ]
26043                 },
26044                 {
26045                     tag : 'ul',
26046                     cls : 'dropdown-menu'
26047                 }
26048             ]
26049             
26050         };
26051         
26052         if(this.pos == 'top'){
26053             cfg.cls += ' dropup';
26054         }
26055         
26056         if(this.isSubMenu){
26057             cfg = {
26058                 tag : 'ul',
26059                 cls : 'dropdown-menu'
26060             }
26061         }
26062         
26063         return cfg;
26064     },
26065     
26066     onRender : function(ct, position)
26067     {
26068         this.isSubMenu = ct.hasClass('dropdown-submenu');
26069         
26070         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26071     },
26072     
26073     initEvents : function() 
26074     {
26075         if(this.isSubMenu){
26076             return;
26077         }
26078         
26079         this.hidden = true;
26080         
26081         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26082         this.triggerEl.on('click', this.onTriggerPress, this);
26083         
26084         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26085         this.buttonEl.on('click', this.onClick, this);
26086         
26087     },
26088     
26089     list : function()
26090     {
26091         if(this.isSubMenu){
26092             return this.el;
26093         }
26094         
26095         return this.el.select('ul.dropdown-menu', true).first();
26096     },
26097     
26098     onClick : function(e)
26099     {
26100         this.fireEvent("click", this, e);
26101     },
26102     
26103     onTriggerPress  : function(e)
26104     {   
26105         if (this.isVisible()) {
26106             this.hide();
26107         } else {
26108             this.show();
26109         }
26110     },
26111     
26112     isVisible : function(){
26113         return !this.hidden;
26114     },
26115     
26116     show : function()
26117     {
26118         this.fireEvent("beforeshow", this);
26119         
26120         this.hidden = false;
26121         this.el.addClass('open');
26122         
26123         Roo.get(document).on("mouseup", this.onMouseUp, this);
26124         
26125         this.fireEvent("show", this);
26126         
26127         
26128     },
26129     
26130     hide : function()
26131     {
26132         this.fireEvent("beforehide", this);
26133         
26134         this.hidden = true;
26135         this.el.removeClass('open');
26136         
26137         Roo.get(document).un("mouseup", this.onMouseUp);
26138         
26139         this.fireEvent("hide", this);
26140     },
26141     
26142     onMouseUp : function()
26143     {
26144         this.hide();
26145     }
26146     
26147 });
26148
26149  
26150  /*
26151  * - LGPL
26152  *
26153  * menu item
26154  * 
26155  */
26156 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26157
26158 /**
26159  * @class Roo.bootstrap.menu.Item
26160  * @extends Roo.bootstrap.Component
26161  * Bootstrap MenuItem class
26162  * @cfg {Boolean} submenu (true | false) default false
26163  * @cfg {String} html text of the item
26164  * @cfg {String} href the link
26165  * @cfg {Boolean} disable (true | false) default false
26166  * @cfg {Boolean} preventDefault (true | false) default true
26167  * @cfg {String} icon Font awesome icon
26168  * @cfg {String} pos Submenu align to (left | right) default right 
26169  * 
26170  * 
26171  * @constructor
26172  * Create a new Item
26173  * @param {Object} config The config object
26174  */
26175
26176
26177 Roo.bootstrap.menu.Item = function(config){
26178     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26179     this.addEvents({
26180         /**
26181          * @event mouseover
26182          * Fires when the mouse is hovering over this menu
26183          * @param {Roo.bootstrap.menu.Item} this
26184          * @param {Roo.EventObject} e
26185          */
26186         mouseover : true,
26187         /**
26188          * @event mouseout
26189          * Fires when the mouse exits this menu
26190          * @param {Roo.bootstrap.menu.Item} this
26191          * @param {Roo.EventObject} e
26192          */
26193         mouseout : true,
26194         // raw events
26195         /**
26196          * @event click
26197          * The raw click event for the entire grid.
26198          * @param {Roo.EventObject} e
26199          */
26200         click : true
26201     });
26202 };
26203
26204 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26205     
26206     submenu : false,
26207     href : '',
26208     html : '',
26209     preventDefault: true,
26210     disable : false,
26211     icon : false,
26212     pos : 'right',
26213     
26214     getAutoCreate : function()
26215     {
26216         var text = [
26217             {
26218                 tag : 'span',
26219                 cls : 'roo-menu-item-text',
26220                 html : this.html
26221             }
26222         ];
26223         
26224         if(this.icon){
26225             text.unshift({
26226                 tag : 'i',
26227                 cls : 'fa ' + this.icon
26228             })
26229         }
26230         
26231         var cfg = {
26232             tag : 'li',
26233             cn : [
26234                 {
26235                     tag : 'a',
26236                     href : this.href || '#',
26237                     cn : text
26238                 }
26239             ]
26240         };
26241         
26242         if(this.disable){
26243             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26244         }
26245         
26246         if(this.submenu){
26247             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26248             
26249             if(this.pos == 'left'){
26250                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26251             }
26252         }
26253         
26254         return cfg;
26255     },
26256     
26257     initEvents : function() 
26258     {
26259         this.el.on('mouseover', this.onMouseOver, this);
26260         this.el.on('mouseout', this.onMouseOut, this);
26261         
26262         this.el.select('a', true).first().on('click', this.onClick, this);
26263         
26264     },
26265     
26266     onClick : function(e)
26267     {
26268         if(this.preventDefault){
26269             e.preventDefault();
26270         }
26271         
26272         this.fireEvent("click", this, e);
26273     },
26274     
26275     onMouseOver : function(e)
26276     {
26277         if(this.submenu && this.pos == 'left'){
26278             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26279         }
26280         
26281         this.fireEvent("mouseover", this, e);
26282     },
26283     
26284     onMouseOut : function(e)
26285     {
26286         this.fireEvent("mouseout", this, e);
26287     }
26288 });
26289
26290  
26291
26292  /*
26293  * - LGPL
26294  *
26295  * menu separator
26296  * 
26297  */
26298 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26299
26300 /**
26301  * @class Roo.bootstrap.menu.Separator
26302  * @extends Roo.bootstrap.Component
26303  * Bootstrap Separator class
26304  * 
26305  * @constructor
26306  * Create a new Separator
26307  * @param {Object} config The config object
26308  */
26309
26310
26311 Roo.bootstrap.menu.Separator = function(config){
26312     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26313 };
26314
26315 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26316     
26317     getAutoCreate : function(){
26318         var cfg = {
26319             tag : 'li',
26320             cls: 'divider'
26321         };
26322         
26323         return cfg;
26324     }
26325    
26326 });
26327
26328  
26329
26330  /*
26331  * - LGPL
26332  *
26333  * Tooltip
26334  * 
26335  */
26336
26337 /**
26338  * @class Roo.bootstrap.Tooltip
26339  * Bootstrap Tooltip class
26340  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26341  * to determine which dom element triggers the tooltip.
26342  * 
26343  * It needs to add support for additional attributes like tooltip-position
26344  * 
26345  * @constructor
26346  * Create a new Toolti
26347  * @param {Object} config The config object
26348  */
26349
26350 Roo.bootstrap.Tooltip = function(config){
26351     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26352     
26353     this.alignment = Roo.bootstrap.Tooltip.alignment;
26354     
26355     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26356         this.alignment = config.alignment;
26357     }
26358     
26359 };
26360
26361 Roo.apply(Roo.bootstrap.Tooltip, {
26362     /**
26363      * @function init initialize tooltip monitoring.
26364      * @static
26365      */
26366     currentEl : false,
26367     currentTip : false,
26368     currentRegion : false,
26369     
26370     //  init : delay?
26371     
26372     init : function()
26373     {
26374         Roo.get(document).on('mouseover', this.enter ,this);
26375         Roo.get(document).on('mouseout', this.leave, this);
26376          
26377         
26378         this.currentTip = new Roo.bootstrap.Tooltip();
26379     },
26380     
26381     enter : function(ev)
26382     {
26383         var dom = ev.getTarget();
26384         
26385         //Roo.log(['enter',dom]);
26386         var el = Roo.fly(dom);
26387         if (this.currentEl) {
26388             //Roo.log(dom);
26389             //Roo.log(this.currentEl);
26390             //Roo.log(this.currentEl.contains(dom));
26391             if (this.currentEl == el) {
26392                 return;
26393             }
26394             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26395                 return;
26396             }
26397
26398         }
26399         
26400         if (this.currentTip.el) {
26401             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26402         }    
26403         //Roo.log(ev);
26404         
26405         if(!el || el.dom == document){
26406             return;
26407         }
26408         
26409         var bindEl = el;
26410         
26411         // you can not look for children, as if el is the body.. then everythign is the child..
26412         if (!el.attr('tooltip')) { //
26413             if (!el.select("[tooltip]").elements.length) {
26414                 return;
26415             }
26416             // is the mouse over this child...?
26417             bindEl = el.select("[tooltip]").first();
26418             var xy = ev.getXY();
26419             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26420                 //Roo.log("not in region.");
26421                 return;
26422             }
26423             //Roo.log("child element over..");
26424             
26425         }
26426         this.currentEl = bindEl;
26427         this.currentTip.bind(bindEl);
26428         this.currentRegion = Roo.lib.Region.getRegion(dom);
26429         this.currentTip.enter();
26430         
26431     },
26432     leave : function(ev)
26433     {
26434         var dom = ev.getTarget();
26435         //Roo.log(['leave',dom]);
26436         if (!this.currentEl) {
26437             return;
26438         }
26439         
26440         
26441         if (dom != this.currentEl.dom) {
26442             return;
26443         }
26444         var xy = ev.getXY();
26445         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26446             return;
26447         }
26448         // only activate leave if mouse cursor is outside... bounding box..
26449         
26450         
26451         
26452         
26453         if (this.currentTip) {
26454             this.currentTip.leave();
26455         }
26456         //Roo.log('clear currentEl');
26457         this.currentEl = false;
26458         
26459         
26460     },
26461     alignment : {
26462         'left' : ['r-l', [-2,0], 'right'],
26463         'right' : ['l-r', [2,0], 'left'],
26464         'bottom' : ['t-b', [0,2], 'top'],
26465         'top' : [ 'b-t', [0,-2], 'bottom']
26466     }
26467     
26468 });
26469
26470
26471 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26472     
26473     
26474     bindEl : false,
26475     
26476     delay : null, // can be { show : 300 , hide: 500}
26477     
26478     timeout : null,
26479     
26480     hoverState : null, //???
26481     
26482     placement : 'bottom', 
26483     
26484     alignment : false,
26485     
26486     getAutoCreate : function(){
26487     
26488         var cfg = {
26489            cls : 'tooltip',
26490            role : 'tooltip',
26491            cn : [
26492                 {
26493                     cls : 'tooltip-arrow'
26494                 },
26495                 {
26496                     cls : 'tooltip-inner'
26497                 }
26498            ]
26499         };
26500         
26501         return cfg;
26502     },
26503     bind : function(el)
26504     {
26505         this.bindEl = el;
26506     },
26507       
26508     
26509     enter : function () {
26510        
26511         if (this.timeout != null) {
26512             clearTimeout(this.timeout);
26513         }
26514         
26515         this.hoverState = 'in';
26516          //Roo.log("enter - show");
26517         if (!this.delay || !this.delay.show) {
26518             this.show();
26519             return;
26520         }
26521         var _t = this;
26522         this.timeout = setTimeout(function () {
26523             if (_t.hoverState == 'in') {
26524                 _t.show();
26525             }
26526         }, this.delay.show);
26527     },
26528     leave : function()
26529     {
26530         clearTimeout(this.timeout);
26531     
26532         this.hoverState = 'out';
26533          if (!this.delay || !this.delay.hide) {
26534             this.hide();
26535             return;
26536         }
26537        
26538         var _t = this;
26539         this.timeout = setTimeout(function () {
26540             //Roo.log("leave - timeout");
26541             
26542             if (_t.hoverState == 'out') {
26543                 _t.hide();
26544                 Roo.bootstrap.Tooltip.currentEl = false;
26545             }
26546         }, delay);
26547     },
26548     
26549     show : function (msg)
26550     {
26551         if (!this.el) {
26552             this.render(document.body);
26553         }
26554         // set content.
26555         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26556         
26557         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26558         
26559         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26560         
26561         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26562         
26563         var placement = typeof this.placement == 'function' ?
26564             this.placement.call(this, this.el, on_el) :
26565             this.placement;
26566             
26567         var autoToken = /\s?auto?\s?/i;
26568         var autoPlace = autoToken.test(placement);
26569         if (autoPlace) {
26570             placement = placement.replace(autoToken, '') || 'top';
26571         }
26572         
26573         //this.el.detach()
26574         //this.el.setXY([0,0]);
26575         this.el.show();
26576         //this.el.dom.style.display='block';
26577         
26578         //this.el.appendTo(on_el);
26579         
26580         var p = this.getPosition();
26581         var box = this.el.getBox();
26582         
26583         if (autoPlace) {
26584             // fixme..
26585         }
26586         
26587         var align = this.alignment[placement];
26588         
26589         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26590         
26591         if(placement == 'top' || placement == 'bottom'){
26592             if(xy[0] < 0){
26593                 placement = 'right';
26594             }
26595             
26596             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26597                 placement = 'left';
26598             }
26599             
26600             var scroll = Roo.select('body', true).first().getScroll();
26601             
26602             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26603                 placement = 'top';
26604             }
26605             
26606             align = this.alignment[placement];
26607         }
26608         
26609         this.el.alignTo(this.bindEl, align[0],align[1]);
26610         //var arrow = this.el.select('.arrow',true).first();
26611         //arrow.set(align[2], 
26612         
26613         this.el.addClass(placement);
26614         
26615         this.el.addClass('in fade');
26616         
26617         this.hoverState = null;
26618         
26619         if (this.el.hasClass('fade')) {
26620             // fade it?
26621         }
26622         
26623     },
26624     hide : function()
26625     {
26626          
26627         if (!this.el) {
26628             return;
26629         }
26630         //this.el.setXY([0,0]);
26631         this.el.removeClass('in');
26632         //this.el.hide();
26633         
26634     }
26635     
26636 });
26637  
26638
26639  /*
26640  * - LGPL
26641  *
26642  * Location Picker
26643  * 
26644  */
26645
26646 /**
26647  * @class Roo.bootstrap.LocationPicker
26648  * @extends Roo.bootstrap.Component
26649  * Bootstrap LocationPicker class
26650  * @cfg {Number} latitude Position when init default 0
26651  * @cfg {Number} longitude Position when init default 0
26652  * @cfg {Number} zoom default 15
26653  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26654  * @cfg {Boolean} mapTypeControl default false
26655  * @cfg {Boolean} disableDoubleClickZoom default false
26656  * @cfg {Boolean} scrollwheel default true
26657  * @cfg {Boolean} streetViewControl default false
26658  * @cfg {Number} radius default 0
26659  * @cfg {String} locationName
26660  * @cfg {Boolean} draggable default true
26661  * @cfg {Boolean} enableAutocomplete default false
26662  * @cfg {Boolean} enableReverseGeocode default true
26663  * @cfg {String} markerTitle
26664  * 
26665  * @constructor
26666  * Create a new LocationPicker
26667  * @param {Object} config The config object
26668  */
26669
26670
26671 Roo.bootstrap.LocationPicker = function(config){
26672     
26673     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26674     
26675     this.addEvents({
26676         /**
26677          * @event initial
26678          * Fires when the picker initialized.
26679          * @param {Roo.bootstrap.LocationPicker} this
26680          * @param {Google Location} location
26681          */
26682         initial : true,
26683         /**
26684          * @event positionchanged
26685          * Fires when the picker position changed.
26686          * @param {Roo.bootstrap.LocationPicker} this
26687          * @param {Google Location} location
26688          */
26689         positionchanged : true,
26690         /**
26691          * @event resize
26692          * Fires when the map resize.
26693          * @param {Roo.bootstrap.LocationPicker} this
26694          */
26695         resize : true,
26696         /**
26697          * @event show
26698          * Fires when the map show.
26699          * @param {Roo.bootstrap.LocationPicker} this
26700          */
26701         show : true,
26702         /**
26703          * @event hide
26704          * Fires when the map hide.
26705          * @param {Roo.bootstrap.LocationPicker} this
26706          */
26707         hide : true,
26708         /**
26709          * @event mapClick
26710          * Fires when click the map.
26711          * @param {Roo.bootstrap.LocationPicker} this
26712          * @param {Map event} e
26713          */
26714         mapClick : true,
26715         /**
26716          * @event mapRightClick
26717          * Fires when right click the map.
26718          * @param {Roo.bootstrap.LocationPicker} this
26719          * @param {Map event} e
26720          */
26721         mapRightClick : true,
26722         /**
26723          * @event markerClick
26724          * Fires when click the marker.
26725          * @param {Roo.bootstrap.LocationPicker} this
26726          * @param {Map event} e
26727          */
26728         markerClick : true,
26729         /**
26730          * @event markerRightClick
26731          * Fires when right click the marker.
26732          * @param {Roo.bootstrap.LocationPicker} this
26733          * @param {Map event} e
26734          */
26735         markerRightClick : true,
26736         /**
26737          * @event OverlayViewDraw
26738          * Fires when OverlayView Draw
26739          * @param {Roo.bootstrap.LocationPicker} this
26740          */
26741         OverlayViewDraw : true,
26742         /**
26743          * @event OverlayViewOnAdd
26744          * Fires when OverlayView Draw
26745          * @param {Roo.bootstrap.LocationPicker} this
26746          */
26747         OverlayViewOnAdd : true,
26748         /**
26749          * @event OverlayViewOnRemove
26750          * Fires when OverlayView Draw
26751          * @param {Roo.bootstrap.LocationPicker} this
26752          */
26753         OverlayViewOnRemove : true,
26754         /**
26755          * @event OverlayViewShow
26756          * Fires when OverlayView Draw
26757          * @param {Roo.bootstrap.LocationPicker} this
26758          * @param {Pixel} cpx
26759          */
26760         OverlayViewShow : true,
26761         /**
26762          * @event OverlayViewHide
26763          * Fires when OverlayView Draw
26764          * @param {Roo.bootstrap.LocationPicker} this
26765          */
26766         OverlayViewHide : true,
26767         /**
26768          * @event loadexception
26769          * Fires when load google lib failed.
26770          * @param {Roo.bootstrap.LocationPicker} this
26771          */
26772         loadexception : true
26773     });
26774         
26775 };
26776
26777 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26778     
26779     gMapContext: false,
26780     
26781     latitude: 0,
26782     longitude: 0,
26783     zoom: 15,
26784     mapTypeId: false,
26785     mapTypeControl: false,
26786     disableDoubleClickZoom: false,
26787     scrollwheel: true,
26788     streetViewControl: false,
26789     radius: 0,
26790     locationName: '',
26791     draggable: true,
26792     enableAutocomplete: false,
26793     enableReverseGeocode: true,
26794     markerTitle: '',
26795     
26796     getAutoCreate: function()
26797     {
26798
26799         var cfg = {
26800             tag: 'div',
26801             cls: 'roo-location-picker'
26802         };
26803         
26804         return cfg
26805     },
26806     
26807     initEvents: function(ct, position)
26808     {       
26809         if(!this.el.getWidth() || this.isApplied()){
26810             return;
26811         }
26812         
26813         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26814         
26815         this.initial();
26816     },
26817     
26818     initial: function()
26819     {
26820         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26821             this.fireEvent('loadexception', this);
26822             return;
26823         }
26824         
26825         if(!this.mapTypeId){
26826             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26827         }
26828         
26829         this.gMapContext = this.GMapContext();
26830         
26831         this.initOverlayView();
26832         
26833         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26834         
26835         var _this = this;
26836                 
26837         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26838             _this.setPosition(_this.gMapContext.marker.position);
26839         });
26840         
26841         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26842             _this.fireEvent('mapClick', this, event);
26843             
26844         });
26845
26846         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26847             _this.fireEvent('mapRightClick', this, event);
26848             
26849         });
26850         
26851         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26852             _this.fireEvent('markerClick', this, event);
26853             
26854         });
26855
26856         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26857             _this.fireEvent('markerRightClick', this, event);
26858             
26859         });
26860         
26861         this.setPosition(this.gMapContext.location);
26862         
26863         this.fireEvent('initial', this, this.gMapContext.location);
26864     },
26865     
26866     initOverlayView: function()
26867     {
26868         var _this = this;
26869         
26870         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26871             
26872             draw: function()
26873             {
26874                 _this.fireEvent('OverlayViewDraw', _this);
26875             },
26876             
26877             onAdd: function()
26878             {
26879                 _this.fireEvent('OverlayViewOnAdd', _this);
26880             },
26881             
26882             onRemove: function()
26883             {
26884                 _this.fireEvent('OverlayViewOnRemove', _this);
26885             },
26886             
26887             show: function(cpx)
26888             {
26889                 _this.fireEvent('OverlayViewShow', _this, cpx);
26890             },
26891             
26892             hide: function()
26893             {
26894                 _this.fireEvent('OverlayViewHide', _this);
26895             }
26896             
26897         });
26898     },
26899     
26900     fromLatLngToContainerPixel: function(event)
26901     {
26902         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26903     },
26904     
26905     isApplied: function() 
26906     {
26907         return this.getGmapContext() == false ? false : true;
26908     },
26909     
26910     getGmapContext: function() 
26911     {
26912         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26913     },
26914     
26915     GMapContext: function() 
26916     {
26917         var position = new google.maps.LatLng(this.latitude, this.longitude);
26918         
26919         var _map = new google.maps.Map(this.el.dom, {
26920             center: position,
26921             zoom: this.zoom,
26922             mapTypeId: this.mapTypeId,
26923             mapTypeControl: this.mapTypeControl,
26924             disableDoubleClickZoom: this.disableDoubleClickZoom,
26925             scrollwheel: this.scrollwheel,
26926             streetViewControl: this.streetViewControl,
26927             locationName: this.locationName,
26928             draggable: this.draggable,
26929             enableAutocomplete: this.enableAutocomplete,
26930             enableReverseGeocode: this.enableReverseGeocode
26931         });
26932         
26933         var _marker = new google.maps.Marker({
26934             position: position,
26935             map: _map,
26936             title: this.markerTitle,
26937             draggable: this.draggable
26938         });
26939         
26940         return {
26941             map: _map,
26942             marker: _marker,
26943             circle: null,
26944             location: position,
26945             radius: this.radius,
26946             locationName: this.locationName,
26947             addressComponents: {
26948                 formatted_address: null,
26949                 addressLine1: null,
26950                 addressLine2: null,
26951                 streetName: null,
26952                 streetNumber: null,
26953                 city: null,
26954                 district: null,
26955                 state: null,
26956                 stateOrProvince: null
26957             },
26958             settings: this,
26959             domContainer: this.el.dom,
26960             geodecoder: new google.maps.Geocoder()
26961         };
26962     },
26963     
26964     drawCircle: function(center, radius, options) 
26965     {
26966         if (this.gMapContext.circle != null) {
26967             this.gMapContext.circle.setMap(null);
26968         }
26969         if (radius > 0) {
26970             radius *= 1;
26971             options = Roo.apply({}, options, {
26972                 strokeColor: "#0000FF",
26973                 strokeOpacity: .35,
26974                 strokeWeight: 2,
26975                 fillColor: "#0000FF",
26976                 fillOpacity: .2
26977             });
26978             
26979             options.map = this.gMapContext.map;
26980             options.radius = radius;
26981             options.center = center;
26982             this.gMapContext.circle = new google.maps.Circle(options);
26983             return this.gMapContext.circle;
26984         }
26985         
26986         return null;
26987     },
26988     
26989     setPosition: function(location) 
26990     {
26991         this.gMapContext.location = location;
26992         this.gMapContext.marker.setPosition(location);
26993         this.gMapContext.map.panTo(location);
26994         this.drawCircle(location, this.gMapContext.radius, {});
26995         
26996         var _this = this;
26997         
26998         if (this.gMapContext.settings.enableReverseGeocode) {
26999             this.gMapContext.geodecoder.geocode({
27000                 latLng: this.gMapContext.location
27001             }, function(results, status) {
27002                 
27003                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27004                     _this.gMapContext.locationName = results[0].formatted_address;
27005                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27006                     
27007                     _this.fireEvent('positionchanged', this, location);
27008                 }
27009             });
27010             
27011             return;
27012         }
27013         
27014         this.fireEvent('positionchanged', this, location);
27015     },
27016     
27017     resize: function()
27018     {
27019         google.maps.event.trigger(this.gMapContext.map, "resize");
27020         
27021         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27022         
27023         this.fireEvent('resize', this);
27024     },
27025     
27026     setPositionByLatLng: function(latitude, longitude)
27027     {
27028         this.setPosition(new google.maps.LatLng(latitude, longitude));
27029     },
27030     
27031     getCurrentPosition: function() 
27032     {
27033         return {
27034             latitude: this.gMapContext.location.lat(),
27035             longitude: this.gMapContext.location.lng()
27036         };
27037     },
27038     
27039     getAddressName: function() 
27040     {
27041         return this.gMapContext.locationName;
27042     },
27043     
27044     getAddressComponents: function() 
27045     {
27046         return this.gMapContext.addressComponents;
27047     },
27048     
27049     address_component_from_google_geocode: function(address_components) 
27050     {
27051         var result = {};
27052         
27053         for (var i = 0; i < address_components.length; i++) {
27054             var component = address_components[i];
27055             if (component.types.indexOf("postal_code") >= 0) {
27056                 result.postalCode = component.short_name;
27057             } else if (component.types.indexOf("street_number") >= 0) {
27058                 result.streetNumber = component.short_name;
27059             } else if (component.types.indexOf("route") >= 0) {
27060                 result.streetName = component.short_name;
27061             } else if (component.types.indexOf("neighborhood") >= 0) {
27062                 result.city = component.short_name;
27063             } else if (component.types.indexOf("locality") >= 0) {
27064                 result.city = component.short_name;
27065             } else if (component.types.indexOf("sublocality") >= 0) {
27066                 result.district = component.short_name;
27067             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27068                 result.stateOrProvince = component.short_name;
27069             } else if (component.types.indexOf("country") >= 0) {
27070                 result.country = component.short_name;
27071             }
27072         }
27073         
27074         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27075         result.addressLine2 = "";
27076         return result;
27077     },
27078     
27079     setZoomLevel: function(zoom)
27080     {
27081         this.gMapContext.map.setZoom(zoom);
27082     },
27083     
27084     show: function()
27085     {
27086         if(!this.el){
27087             return;
27088         }
27089         
27090         this.el.show();
27091         
27092         this.resize();
27093         
27094         this.fireEvent('show', this);
27095     },
27096     
27097     hide: function()
27098     {
27099         if(!this.el){
27100             return;
27101         }
27102         
27103         this.el.hide();
27104         
27105         this.fireEvent('hide', this);
27106     }
27107     
27108 });
27109
27110 Roo.apply(Roo.bootstrap.LocationPicker, {
27111     
27112     OverlayView : function(map, options)
27113     {
27114         options = options || {};
27115         
27116         this.setMap(map);
27117     }
27118     
27119     
27120 });/*
27121  * - LGPL
27122  *
27123  * Alert
27124  * 
27125  */
27126
27127 /**
27128  * @class Roo.bootstrap.Alert
27129  * @extends Roo.bootstrap.Component
27130  * Bootstrap Alert class
27131  * @cfg {String} title The title of alert
27132  * @cfg {String} html The content of alert
27133  * @cfg {String} weight (  success | info | warning | danger )
27134  * @cfg {String} faicon font-awesomeicon
27135  * 
27136  * @constructor
27137  * Create a new alert
27138  * @param {Object} config The config object
27139  */
27140
27141
27142 Roo.bootstrap.Alert = function(config){
27143     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27144     
27145 };
27146
27147 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27148     
27149     title: '',
27150     html: '',
27151     weight: false,
27152     faicon: false,
27153     
27154     getAutoCreate : function()
27155     {
27156         
27157         var cfg = {
27158             tag : 'div',
27159             cls : 'alert',
27160             cn : [
27161                 {
27162                     tag : 'i',
27163                     cls : 'roo-alert-icon'
27164                     
27165                 },
27166                 {
27167                     tag : 'b',
27168                     cls : 'roo-alert-title',
27169                     html : this.title
27170                 },
27171                 {
27172                     tag : 'span',
27173                     cls : 'roo-alert-text',
27174                     html : this.html
27175                 }
27176             ]
27177         };
27178         
27179         if(this.faicon){
27180             cfg.cn[0].cls += ' fa ' + this.faicon;
27181         }
27182         
27183         if(this.weight){
27184             cfg.cls += ' alert-' + this.weight;
27185         }
27186         
27187         return cfg;
27188     },
27189     
27190     initEvents: function() 
27191     {
27192         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27193     },
27194     
27195     setTitle : function(str)
27196     {
27197         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27198     },
27199     
27200     setText : function(str)
27201     {
27202         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27203     },
27204     
27205     setWeight : function(weight)
27206     {
27207         if(this.weight){
27208             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27209         }
27210         
27211         this.weight = weight;
27212         
27213         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27214     },
27215     
27216     setIcon : function(icon)
27217     {
27218         if(this.faicon){
27219             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27220         }
27221         
27222         this.faicon = icon;
27223         
27224         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27225     },
27226     
27227     hide: function() 
27228     {
27229         this.el.hide();   
27230     },
27231     
27232     show: function() 
27233     {  
27234         this.el.show();   
27235     }
27236     
27237 });
27238
27239  
27240 /*
27241 * Licence: LGPL
27242 */
27243
27244 /**
27245  * @class Roo.bootstrap.UploadCropbox
27246  * @extends Roo.bootstrap.Component
27247  * Bootstrap UploadCropbox class
27248  * @cfg {String} emptyText show when image has been loaded
27249  * @cfg {String} rotateNotify show when image too small to rotate
27250  * @cfg {Number} errorTimeout default 3000
27251  * @cfg {Number} minWidth default 300
27252  * @cfg {Number} minHeight default 300
27253  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27254  * @cfg {Boolean} isDocument (true|false) default false
27255  * @cfg {String} url action url
27256  * @cfg {String} paramName default 'imageUpload'
27257  * @cfg {String} method default POST
27258  * @cfg {Boolean} loadMask (true|false) default true
27259  * @cfg {Boolean} loadingText default 'Loading...'
27260  * 
27261  * @constructor
27262  * Create a new UploadCropbox
27263  * @param {Object} config The config object
27264  */
27265
27266 Roo.bootstrap.UploadCropbox = function(config){
27267     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27268     
27269     this.addEvents({
27270         /**
27271          * @event beforeselectfile
27272          * Fire before select file
27273          * @param {Roo.bootstrap.UploadCropbox} this
27274          */
27275         "beforeselectfile" : true,
27276         /**
27277          * @event initial
27278          * Fire after initEvent
27279          * @param {Roo.bootstrap.UploadCropbox} this
27280          */
27281         "initial" : true,
27282         /**
27283          * @event crop
27284          * Fire after initEvent
27285          * @param {Roo.bootstrap.UploadCropbox} this
27286          * @param {String} data
27287          */
27288         "crop" : true,
27289         /**
27290          * @event prepare
27291          * Fire when preparing the file data
27292          * @param {Roo.bootstrap.UploadCropbox} this
27293          * @param {Object} file
27294          */
27295         "prepare" : true,
27296         /**
27297          * @event exception
27298          * Fire when get exception
27299          * @param {Roo.bootstrap.UploadCropbox} this
27300          * @param {XMLHttpRequest} xhr
27301          */
27302         "exception" : true,
27303         /**
27304          * @event beforeloadcanvas
27305          * Fire before load the canvas
27306          * @param {Roo.bootstrap.UploadCropbox} this
27307          * @param {String} src
27308          */
27309         "beforeloadcanvas" : true,
27310         /**
27311          * @event trash
27312          * Fire when trash image
27313          * @param {Roo.bootstrap.UploadCropbox} this
27314          */
27315         "trash" : true,
27316         /**
27317          * @event download
27318          * Fire when download the image
27319          * @param {Roo.bootstrap.UploadCropbox} this
27320          */
27321         "download" : true,
27322         /**
27323          * @event footerbuttonclick
27324          * Fire when footerbuttonclick
27325          * @param {Roo.bootstrap.UploadCropbox} this
27326          * @param {String} type
27327          */
27328         "footerbuttonclick" : true,
27329         /**
27330          * @event resize
27331          * Fire when resize
27332          * @param {Roo.bootstrap.UploadCropbox} this
27333          */
27334         "resize" : true,
27335         /**
27336          * @event rotate
27337          * Fire when rotate the image
27338          * @param {Roo.bootstrap.UploadCropbox} this
27339          * @param {String} pos
27340          */
27341         "rotate" : true,
27342         /**
27343          * @event inspect
27344          * Fire when inspect the file
27345          * @param {Roo.bootstrap.UploadCropbox} this
27346          * @param {Object} file
27347          */
27348         "inspect" : true,
27349         /**
27350          * @event upload
27351          * Fire when xhr upload the file
27352          * @param {Roo.bootstrap.UploadCropbox} this
27353          * @param {Object} data
27354          */
27355         "upload" : true,
27356         /**
27357          * @event arrange
27358          * Fire when arrange the file data
27359          * @param {Roo.bootstrap.UploadCropbox} this
27360          * @param {Object} formData
27361          */
27362         "arrange" : true
27363     });
27364     
27365     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27366 };
27367
27368 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27369     
27370     emptyText : 'Click to upload image',
27371     rotateNotify : 'Image is too small to rotate',
27372     errorTimeout : 3000,
27373     scale : 0,
27374     baseScale : 1,
27375     rotate : 0,
27376     dragable : false,
27377     pinching : false,
27378     mouseX : 0,
27379     mouseY : 0,
27380     cropData : false,
27381     minWidth : 300,
27382     minHeight : 300,
27383     file : false,
27384     exif : {},
27385     baseRotate : 1,
27386     cropType : 'image/jpeg',
27387     buttons : false,
27388     canvasLoaded : false,
27389     isDocument : false,
27390     method : 'POST',
27391     paramName : 'imageUpload',
27392     loadMask : true,
27393     loadingText : 'Loading...',
27394     maskEl : false,
27395     
27396     getAutoCreate : function()
27397     {
27398         var cfg = {
27399             tag : 'div',
27400             cls : 'roo-upload-cropbox',
27401             cn : [
27402                 {
27403                     tag : 'input',
27404                     cls : 'roo-upload-cropbox-selector',
27405                     type : 'file'
27406                 },
27407                 {
27408                     tag : 'div',
27409                     cls : 'roo-upload-cropbox-body',
27410                     style : 'cursor:pointer',
27411                     cn : [
27412                         {
27413                             tag : 'div',
27414                             cls : 'roo-upload-cropbox-preview'
27415                         },
27416                         {
27417                             tag : 'div',
27418                             cls : 'roo-upload-cropbox-thumb'
27419                         },
27420                         {
27421                             tag : 'div',
27422                             cls : 'roo-upload-cropbox-empty-notify',
27423                             html : this.emptyText
27424                         },
27425                         {
27426                             tag : 'div',
27427                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27428                             html : this.rotateNotify
27429                         }
27430                     ]
27431                 },
27432                 {
27433                     tag : 'div',
27434                     cls : 'roo-upload-cropbox-footer',
27435                     cn : {
27436                         tag : 'div',
27437                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27438                         cn : []
27439                     }
27440                 }
27441             ]
27442         };
27443         
27444         return cfg;
27445     },
27446     
27447     onRender : function(ct, position)
27448     {
27449         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27450         
27451         if (this.buttons.length) {
27452             
27453             Roo.each(this.buttons, function(bb) {
27454                 
27455                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27456                 
27457                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27458                 
27459             }, this);
27460         }
27461         
27462         if(this.loadMask){
27463             this.maskEl = this.el;
27464         }
27465     },
27466     
27467     initEvents : function()
27468     {
27469         this.urlAPI = (window.createObjectURL && window) || 
27470                                 (window.URL && URL.revokeObjectURL && URL) || 
27471                                 (window.webkitURL && webkitURL);
27472                         
27473         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27474         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27475         
27476         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27477         this.selectorEl.hide();
27478         
27479         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27480         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27481         
27482         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27483         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27484         this.thumbEl.hide();
27485         
27486         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27487         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27488         
27489         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27490         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27491         this.errorEl.hide();
27492         
27493         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27494         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27495         this.footerEl.hide();
27496         
27497         this.setThumbBoxSize();
27498         
27499         this.bind();
27500         
27501         this.resize();
27502         
27503         this.fireEvent('initial', this);
27504     },
27505
27506     bind : function()
27507     {
27508         var _this = this;
27509         
27510         window.addEventListener("resize", function() { _this.resize(); } );
27511         
27512         this.bodyEl.on('click', this.beforeSelectFile, this);
27513         
27514         if(Roo.isTouch){
27515             this.bodyEl.on('touchstart', this.onTouchStart, this);
27516             this.bodyEl.on('touchmove', this.onTouchMove, this);
27517             this.bodyEl.on('touchend', this.onTouchEnd, this);
27518         }
27519         
27520         if(!Roo.isTouch){
27521             this.bodyEl.on('mousedown', this.onMouseDown, this);
27522             this.bodyEl.on('mousemove', this.onMouseMove, this);
27523             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27524             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27525             Roo.get(document).on('mouseup', this.onMouseUp, this);
27526         }
27527         
27528         this.selectorEl.on('change', this.onFileSelected, this);
27529     },
27530     
27531     reset : function()
27532     {    
27533         this.scale = 0;
27534         this.baseScale = 1;
27535         this.rotate = 0;
27536         this.baseRotate = 1;
27537         this.dragable = false;
27538         this.pinching = false;
27539         this.mouseX = 0;
27540         this.mouseY = 0;
27541         this.cropData = false;
27542         this.notifyEl.dom.innerHTML = this.emptyText;
27543         
27544         this.selectorEl.dom.value = '';
27545         
27546     },
27547     
27548     resize : function()
27549     {
27550         if(this.fireEvent('resize', this) != false){
27551             this.setThumbBoxPosition();
27552             this.setCanvasPosition();
27553         }
27554     },
27555     
27556     onFooterButtonClick : function(e, el, o, type)
27557     {
27558         switch (type) {
27559             case 'rotate-left' :
27560                 this.onRotateLeft(e);
27561                 break;
27562             case 'rotate-right' :
27563                 this.onRotateRight(e);
27564                 break;
27565             case 'picture' :
27566                 this.beforeSelectFile(e);
27567                 break;
27568             case 'trash' :
27569                 this.trash(e);
27570                 break;
27571             case 'crop' :
27572                 this.crop(e);
27573                 break;
27574             case 'download' :
27575                 this.download(e);
27576                 break;
27577             default :
27578                 break;
27579         }
27580         
27581         this.fireEvent('footerbuttonclick', this, type);
27582     },
27583     
27584     beforeSelectFile : function(e)
27585     {
27586         e.preventDefault();
27587         
27588         if(this.fireEvent('beforeselectfile', this) != false){
27589             this.selectorEl.dom.click();
27590         }
27591     },
27592     
27593     onFileSelected : function(e)
27594     {
27595         e.preventDefault();
27596         
27597         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27598             return;
27599         }
27600         
27601         var file = this.selectorEl.dom.files[0];
27602         
27603         if(this.fireEvent('inspect', this, file) != false){
27604             this.prepare(file);
27605         }
27606         
27607     },
27608     
27609     trash : function(e)
27610     {
27611         this.fireEvent('trash', this);
27612     },
27613     
27614     download : function(e)
27615     {
27616         this.fireEvent('download', this);
27617     },
27618     
27619     loadCanvas : function(src)
27620     {   
27621         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27622             
27623             this.reset();
27624             
27625             this.imageEl = document.createElement('img');
27626             
27627             var _this = this;
27628             
27629             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27630             
27631             this.imageEl.src = src;
27632         }
27633     },
27634     
27635     onLoadCanvas : function()
27636     {   
27637         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27638         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27639         
27640         this.bodyEl.un('click', this.beforeSelectFile, this);
27641         
27642         this.notifyEl.hide();
27643         this.thumbEl.show();
27644         this.footerEl.show();
27645         
27646         this.baseRotateLevel();
27647         
27648         if(this.isDocument){
27649             this.setThumbBoxSize();
27650         }
27651         
27652         this.setThumbBoxPosition();
27653         
27654         this.baseScaleLevel();
27655         
27656         this.draw();
27657         
27658         this.resize();
27659         
27660         this.canvasLoaded = true;
27661         
27662         if(this.loadMask){
27663             this.maskEl.unmask();
27664         }
27665         
27666     },
27667     
27668     setCanvasPosition : function()
27669     {   
27670         if(!this.canvasEl){
27671             return;
27672         }
27673         
27674         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27675         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27676         
27677         this.previewEl.setLeft(pw);
27678         this.previewEl.setTop(ph);
27679         
27680     },
27681     
27682     onMouseDown : function(e)
27683     {   
27684         e.stopEvent();
27685         
27686         this.dragable = true;
27687         this.pinching = false;
27688         
27689         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27690             this.dragable = false;
27691             return;
27692         }
27693         
27694         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27695         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27696         
27697     },
27698     
27699     onMouseMove : function(e)
27700     {   
27701         e.stopEvent();
27702         
27703         if(!this.canvasLoaded){
27704             return;
27705         }
27706         
27707         if (!this.dragable){
27708             return;
27709         }
27710         
27711         var minX = Math.ceil(this.thumbEl.getLeft(true));
27712         var minY = Math.ceil(this.thumbEl.getTop(true));
27713         
27714         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27715         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27716         
27717         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27718         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27719         
27720         x = x - this.mouseX;
27721         y = y - this.mouseY;
27722         
27723         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27724         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27725         
27726         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27727         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27728         
27729         this.previewEl.setLeft(bgX);
27730         this.previewEl.setTop(bgY);
27731         
27732         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27733         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27734     },
27735     
27736     onMouseUp : function(e)
27737     {   
27738         e.stopEvent();
27739         
27740         this.dragable = false;
27741     },
27742     
27743     onMouseWheel : function(e)
27744     {   
27745         e.stopEvent();
27746         
27747         this.startScale = this.scale;
27748         
27749         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27750         
27751         if(!this.zoomable()){
27752             this.scale = this.startScale;
27753             return;
27754         }
27755         
27756         this.draw();
27757         
27758         return;
27759     },
27760     
27761     zoomable : function()
27762     {
27763         var minScale = this.thumbEl.getWidth() / this.minWidth;
27764         
27765         if(this.minWidth < this.minHeight){
27766             minScale = this.thumbEl.getHeight() / this.minHeight;
27767         }
27768         
27769         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27770         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27771         
27772         if(
27773                 this.isDocument &&
27774                 (this.rotate == 0 || this.rotate == 180) && 
27775                 (
27776                     width > this.imageEl.OriginWidth || 
27777                     height > this.imageEl.OriginHeight ||
27778                     (width < this.minWidth && height < this.minHeight)
27779                 )
27780         ){
27781             return false;
27782         }
27783         
27784         if(
27785                 this.isDocument &&
27786                 (this.rotate == 90 || this.rotate == 270) && 
27787                 (
27788                     width > this.imageEl.OriginWidth || 
27789                     height > this.imageEl.OriginHeight ||
27790                     (width < this.minHeight && height < this.minWidth)
27791                 )
27792         ){
27793             return false;
27794         }
27795         
27796         if(
27797                 !this.isDocument &&
27798                 (this.rotate == 0 || this.rotate == 180) && 
27799                 (
27800                     width < this.minWidth || 
27801                     width > this.imageEl.OriginWidth || 
27802                     height < this.minHeight || 
27803                     height > this.imageEl.OriginHeight
27804                 )
27805         ){
27806             return false;
27807         }
27808         
27809         if(
27810                 !this.isDocument &&
27811                 (this.rotate == 90 || this.rotate == 270) && 
27812                 (
27813                     width < this.minHeight || 
27814                     width > this.imageEl.OriginWidth || 
27815                     height < this.minWidth || 
27816                     height > this.imageEl.OriginHeight
27817                 )
27818         ){
27819             return false;
27820         }
27821         
27822         return true;
27823         
27824     },
27825     
27826     onRotateLeft : function(e)
27827     {   
27828         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27829             
27830             var minScale = this.thumbEl.getWidth() / this.minWidth;
27831             
27832             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27833             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27834             
27835             this.startScale = this.scale;
27836             
27837             while (this.getScaleLevel() < minScale){
27838             
27839                 this.scale = this.scale + 1;
27840                 
27841                 if(!this.zoomable()){
27842                     break;
27843                 }
27844                 
27845                 if(
27846                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27847                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27848                 ){
27849                     continue;
27850                 }
27851                 
27852                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27853
27854                 this.draw();
27855                 
27856                 return;
27857             }
27858             
27859             this.scale = this.startScale;
27860             
27861             this.onRotateFail();
27862             
27863             return false;
27864         }
27865         
27866         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27867
27868         if(this.isDocument){
27869             this.setThumbBoxSize();
27870             this.setThumbBoxPosition();
27871             this.setCanvasPosition();
27872         }
27873         
27874         this.draw();
27875         
27876         this.fireEvent('rotate', this, 'left');
27877         
27878     },
27879     
27880     onRotateRight : function(e)
27881     {
27882         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27883             
27884             var minScale = this.thumbEl.getWidth() / this.minWidth;
27885         
27886             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27887             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27888             
27889             this.startScale = this.scale;
27890             
27891             while (this.getScaleLevel() < minScale){
27892             
27893                 this.scale = this.scale + 1;
27894                 
27895                 if(!this.zoomable()){
27896                     break;
27897                 }
27898                 
27899                 if(
27900                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27901                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27902                 ){
27903                     continue;
27904                 }
27905                 
27906                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27907
27908                 this.draw();
27909                 
27910                 return;
27911             }
27912             
27913             this.scale = this.startScale;
27914             
27915             this.onRotateFail();
27916             
27917             return false;
27918         }
27919         
27920         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27921
27922         if(this.isDocument){
27923             this.setThumbBoxSize();
27924             this.setThumbBoxPosition();
27925             this.setCanvasPosition();
27926         }
27927         
27928         this.draw();
27929         
27930         this.fireEvent('rotate', this, 'right');
27931     },
27932     
27933     onRotateFail : function()
27934     {
27935         this.errorEl.show(true);
27936         
27937         var _this = this;
27938         
27939         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27940     },
27941     
27942     draw : function()
27943     {
27944         this.previewEl.dom.innerHTML = '';
27945         
27946         var canvasEl = document.createElement("canvas");
27947         
27948         var contextEl = canvasEl.getContext("2d");
27949         
27950         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27951         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27952         var center = this.imageEl.OriginWidth / 2;
27953         
27954         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27955             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27956             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27957             center = this.imageEl.OriginHeight / 2;
27958         }
27959         
27960         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27961         
27962         contextEl.translate(center, center);
27963         contextEl.rotate(this.rotate * Math.PI / 180);
27964
27965         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27966         
27967         this.canvasEl = document.createElement("canvas");
27968         
27969         this.contextEl = this.canvasEl.getContext("2d");
27970         
27971         switch (this.rotate) {
27972             case 0 :
27973                 
27974                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27975                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27976                 
27977                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27978                 
27979                 break;
27980             case 90 : 
27981                 
27982                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27983                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27984                 
27985                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27986                     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);
27987                     break;
27988                 }
27989                 
27990                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27991                 
27992                 break;
27993             case 180 :
27994                 
27995                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27996                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27997                 
27998                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27999                     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);
28000                     break;
28001                 }
28002                 
28003                 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28004                 
28005                 break;
28006             case 270 :
28007                 
28008                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28009                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28010         
28011                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28012                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28013                     break;
28014                 }
28015                 
28016                 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28017                 
28018                 break;
28019             default : 
28020                 break;
28021         }
28022         
28023         this.previewEl.appendChild(this.canvasEl);
28024         
28025         this.setCanvasPosition();
28026     },
28027     
28028     crop : function()
28029     {
28030         if(!this.canvasLoaded){
28031             return;
28032         }
28033         
28034         var imageCanvas = document.createElement("canvas");
28035         
28036         var imageContext = imageCanvas.getContext("2d");
28037         
28038         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28039         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28040         
28041         var center = imageCanvas.width / 2;
28042         
28043         imageContext.translate(center, center);
28044         
28045         imageContext.rotate(this.rotate * Math.PI / 180);
28046         
28047         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28048         
28049         var canvas = document.createElement("canvas");
28050         
28051         var context = canvas.getContext("2d");
28052                 
28053         canvas.width = this.minWidth;
28054         canvas.height = this.minHeight;
28055
28056         switch (this.rotate) {
28057             case 0 :
28058                 
28059                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28060                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28061                 
28062                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28063                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28064                 
28065                 var targetWidth = this.minWidth - 2 * x;
28066                 var targetHeight = this.minHeight - 2 * y;
28067                 
28068                 var scale = 1;
28069                 
28070                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28071                     scale = targetWidth / width;
28072                 }
28073                 
28074                 if(x > 0 && y == 0){
28075                     scale = targetHeight / height;
28076                 }
28077                 
28078                 if(x > 0 && y > 0){
28079                     scale = targetWidth / width;
28080                     
28081                     if(width < height){
28082                         scale = targetHeight / height;
28083                     }
28084                 }
28085                 
28086                 context.scale(scale, scale);
28087                 
28088                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28089                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28090
28091                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28092                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28093
28094                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28095                 
28096                 break;
28097             case 90 : 
28098                 
28099                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28100                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28101                 
28102                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28103                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28104                 
28105                 var targetWidth = this.minWidth - 2 * x;
28106                 var targetHeight = this.minHeight - 2 * y;
28107                 
28108                 var scale = 1;
28109                 
28110                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28111                     scale = targetWidth / width;
28112                 }
28113                 
28114                 if(x > 0 && y == 0){
28115                     scale = targetHeight / height;
28116                 }
28117                 
28118                 if(x > 0 && y > 0){
28119                     scale = targetWidth / width;
28120                     
28121                     if(width < height){
28122                         scale = targetHeight / height;
28123                     }
28124                 }
28125                 
28126                 context.scale(scale, scale);
28127                 
28128                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28129                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28130
28131                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28132                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28133                 
28134                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28135                 
28136                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28137                 
28138                 break;
28139             case 180 :
28140                 
28141                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28142                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28143                 
28144                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28145                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28146                 
28147                 var targetWidth = this.minWidth - 2 * x;
28148                 var targetHeight = this.minHeight - 2 * y;
28149                 
28150                 var scale = 1;
28151                 
28152                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28153                     scale = targetWidth / width;
28154                 }
28155                 
28156                 if(x > 0 && y == 0){
28157                     scale = targetHeight / height;
28158                 }
28159                 
28160                 if(x > 0 && y > 0){
28161                     scale = targetWidth / width;
28162                     
28163                     if(width < height){
28164                         scale = targetHeight / height;
28165                     }
28166                 }
28167                 
28168                 context.scale(scale, scale);
28169                 
28170                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28171                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28172
28173                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28174                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28175
28176                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28177                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28178                 
28179                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28180                 
28181                 break;
28182             case 270 :
28183                 
28184                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28185                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28186                 
28187                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28188                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28189                 
28190                 var targetWidth = this.minWidth - 2 * x;
28191                 var targetHeight = this.minHeight - 2 * y;
28192                 
28193                 var scale = 1;
28194                 
28195                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28196                     scale = targetWidth / width;
28197                 }
28198                 
28199                 if(x > 0 && y == 0){
28200                     scale = targetHeight / height;
28201                 }
28202                 
28203                 if(x > 0 && y > 0){
28204                     scale = targetWidth / width;
28205                     
28206                     if(width < height){
28207                         scale = targetHeight / height;
28208                     }
28209                 }
28210                 
28211                 context.scale(scale, scale);
28212                 
28213                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28214                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28215
28216                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28217                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28218                 
28219                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28220                 
28221                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28222                 
28223                 break;
28224             default : 
28225                 break;
28226         }
28227         
28228         this.cropData = canvas.toDataURL(this.cropType);
28229         
28230         if(this.fireEvent('crop', this, this.cropData) !== false){
28231             this.process(this.file, this.cropData);
28232         }
28233         
28234         return;
28235         
28236     },
28237     
28238     setThumbBoxSize : function()
28239     {
28240         var width, height;
28241         
28242         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28243             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28244             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28245             
28246             this.minWidth = width;
28247             this.minHeight = height;
28248             
28249             if(this.rotate == 90 || this.rotate == 270){
28250                 this.minWidth = height;
28251                 this.minHeight = width;
28252             }
28253         }
28254         
28255         height = 300;
28256         width = Math.ceil(this.minWidth * height / this.minHeight);
28257         
28258         if(this.minWidth > this.minHeight){
28259             width = 300;
28260             height = Math.ceil(this.minHeight * width / this.minWidth);
28261         }
28262         
28263         this.thumbEl.setStyle({
28264             width : width + 'px',
28265             height : height + 'px'
28266         });
28267
28268         return;
28269             
28270     },
28271     
28272     setThumbBoxPosition : function()
28273     {
28274         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28275         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28276         
28277         this.thumbEl.setLeft(x);
28278         this.thumbEl.setTop(y);
28279         
28280     },
28281     
28282     baseRotateLevel : function()
28283     {
28284         this.baseRotate = 1;
28285         
28286         if(
28287                 typeof(this.exif) != 'undefined' &&
28288                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28289                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28290         ){
28291             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28292         }
28293         
28294         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28295         
28296     },
28297     
28298     baseScaleLevel : function()
28299     {
28300         var width, height;
28301         
28302         if(this.isDocument){
28303             
28304             if(this.baseRotate == 6 || this.baseRotate == 8){
28305             
28306                 height = this.thumbEl.getHeight();
28307                 this.baseScale = height / this.imageEl.OriginWidth;
28308
28309                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28310                     width = this.thumbEl.getWidth();
28311                     this.baseScale = width / this.imageEl.OriginHeight;
28312                 }
28313
28314                 return;
28315             }
28316
28317             height = this.thumbEl.getHeight();
28318             this.baseScale = height / this.imageEl.OriginHeight;
28319
28320             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28321                 width = this.thumbEl.getWidth();
28322                 this.baseScale = width / this.imageEl.OriginWidth;
28323             }
28324
28325             return;
28326         }
28327         
28328         if(this.baseRotate == 6 || this.baseRotate == 8){
28329             
28330             width = this.thumbEl.getHeight();
28331             this.baseScale = width / this.imageEl.OriginHeight;
28332             
28333             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28334                 height = this.thumbEl.getWidth();
28335                 this.baseScale = height / this.imageEl.OriginHeight;
28336             }
28337             
28338             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28339                 height = this.thumbEl.getWidth();
28340                 this.baseScale = height / this.imageEl.OriginHeight;
28341                 
28342                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28343                     width = this.thumbEl.getHeight();
28344                     this.baseScale = width / this.imageEl.OriginWidth;
28345                 }
28346             }
28347             
28348             return;
28349         }
28350         
28351         width = this.thumbEl.getWidth();
28352         this.baseScale = width / this.imageEl.OriginWidth;
28353         
28354         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28355             height = this.thumbEl.getHeight();
28356             this.baseScale = height / this.imageEl.OriginHeight;
28357         }
28358         
28359         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28360             
28361             height = this.thumbEl.getHeight();
28362             this.baseScale = height / this.imageEl.OriginHeight;
28363             
28364             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28365                 width = this.thumbEl.getWidth();
28366                 this.baseScale = width / this.imageEl.OriginWidth;
28367             }
28368             
28369         }
28370         
28371         return;
28372     },
28373     
28374     getScaleLevel : function()
28375     {
28376         return this.baseScale * Math.pow(1.1, this.scale);
28377     },
28378     
28379     onTouchStart : function(e)
28380     {
28381         if(!this.canvasLoaded){
28382             this.beforeSelectFile(e);
28383             return;
28384         }
28385         
28386         var touches = e.browserEvent.touches;
28387         
28388         if(!touches){
28389             return;
28390         }
28391         
28392         if(touches.length == 1){
28393             this.onMouseDown(e);
28394             return;
28395         }
28396         
28397         if(touches.length != 2){
28398             return;
28399         }
28400         
28401         var coords = [];
28402         
28403         for(var i = 0, finger; finger = touches[i]; i++){
28404             coords.push(finger.pageX, finger.pageY);
28405         }
28406         
28407         var x = Math.pow(coords[0] - coords[2], 2);
28408         var y = Math.pow(coords[1] - coords[3], 2);
28409         
28410         this.startDistance = Math.sqrt(x + y);
28411         
28412         this.startScale = this.scale;
28413         
28414         this.pinching = true;
28415         this.dragable = false;
28416         
28417     },
28418     
28419     onTouchMove : function(e)
28420     {
28421         if(!this.pinching && !this.dragable){
28422             return;
28423         }
28424         
28425         var touches = e.browserEvent.touches;
28426         
28427         if(!touches){
28428             return;
28429         }
28430         
28431         if(this.dragable){
28432             this.onMouseMove(e);
28433             return;
28434         }
28435         
28436         var coords = [];
28437         
28438         for(var i = 0, finger; finger = touches[i]; i++){
28439             coords.push(finger.pageX, finger.pageY);
28440         }
28441         
28442         var x = Math.pow(coords[0] - coords[2], 2);
28443         var y = Math.pow(coords[1] - coords[3], 2);
28444         
28445         this.endDistance = Math.sqrt(x + y);
28446         
28447         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28448         
28449         if(!this.zoomable()){
28450             this.scale = this.startScale;
28451             return;
28452         }
28453         
28454         this.draw();
28455         
28456     },
28457     
28458     onTouchEnd : function(e)
28459     {
28460         this.pinching = false;
28461         this.dragable = false;
28462         
28463     },
28464     
28465     process : function(file, crop)
28466     {
28467         if(this.loadMask){
28468             this.maskEl.mask(this.loadingText);
28469         }
28470         
28471         this.xhr = new XMLHttpRequest();
28472         
28473         file.xhr = this.xhr;
28474
28475         this.xhr.open(this.method, this.url, true);
28476         
28477         var headers = {
28478             "Accept": "application/json",
28479             "Cache-Control": "no-cache",
28480             "X-Requested-With": "XMLHttpRequest"
28481         };
28482         
28483         for (var headerName in headers) {
28484             var headerValue = headers[headerName];
28485             if (headerValue) {
28486                 this.xhr.setRequestHeader(headerName, headerValue);
28487             }
28488         }
28489         
28490         var _this = this;
28491         
28492         this.xhr.onload = function()
28493         {
28494             _this.xhrOnLoad(_this.xhr);
28495         }
28496         
28497         this.xhr.onerror = function()
28498         {
28499             _this.xhrOnError(_this.xhr);
28500         }
28501         
28502         var formData = new FormData();
28503
28504         formData.append('returnHTML', 'NO');
28505         
28506         if(crop){
28507             formData.append('crop', crop);
28508         }
28509         
28510         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28511             formData.append(this.paramName, file, file.name);
28512         }
28513         
28514         if(typeof(file.filename) != 'undefined'){
28515             formData.append('filename', file.filename);
28516         }
28517         
28518         if(typeof(file.mimetype) != 'undefined'){
28519             formData.append('mimetype', file.mimetype);
28520         }
28521         
28522         if(this.fireEvent('arrange', this, formData) != false){
28523             this.xhr.send(formData);
28524         };
28525     },
28526     
28527     xhrOnLoad : function(xhr)
28528     {
28529         if(this.loadMask){
28530             this.maskEl.unmask();
28531         }
28532         
28533         if (xhr.readyState !== 4) {
28534             this.fireEvent('exception', this, xhr);
28535             return;
28536         }
28537
28538         var response = Roo.decode(xhr.responseText);
28539         
28540         if(!response.success){
28541             this.fireEvent('exception', this, xhr);
28542             return;
28543         }
28544         
28545         var response = Roo.decode(xhr.responseText);
28546         
28547         this.fireEvent('upload', this, response);
28548         
28549     },
28550     
28551     xhrOnError : function()
28552     {
28553         if(this.loadMask){
28554             this.maskEl.unmask();
28555         }
28556         
28557         Roo.log('xhr on error');
28558         
28559         var response = Roo.decode(xhr.responseText);
28560           
28561         Roo.log(response);
28562         
28563     },
28564     
28565     prepare : function(file)
28566     {   
28567         if(this.loadMask){
28568             this.maskEl.mask(this.loadingText);
28569         }
28570         
28571         this.file = false;
28572         this.exif = {};
28573         
28574         if(typeof(file) === 'string'){
28575             this.loadCanvas(file);
28576             return;
28577         }
28578         
28579         if(!file || !this.urlAPI){
28580             return;
28581         }
28582         
28583         this.file = file;
28584         this.cropType = file.type;
28585         
28586         var _this = this;
28587         
28588         if(this.fireEvent('prepare', this, this.file) != false){
28589             
28590             var reader = new FileReader();
28591             
28592             reader.onload = function (e) {
28593                 if (e.target.error) {
28594                     Roo.log(e.target.error);
28595                     return;
28596                 }
28597                 
28598                 var buffer = e.target.result,
28599                     dataView = new DataView(buffer),
28600                     offset = 2,
28601                     maxOffset = dataView.byteLength - 4,
28602                     markerBytes,
28603                     markerLength;
28604                 
28605                 if (dataView.getUint16(0) === 0xffd8) {
28606                     while (offset < maxOffset) {
28607                         markerBytes = dataView.getUint16(offset);
28608                         
28609                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28610                             markerLength = dataView.getUint16(offset + 2) + 2;
28611                             if (offset + markerLength > dataView.byteLength) {
28612                                 Roo.log('Invalid meta data: Invalid segment size.');
28613                                 break;
28614                             }
28615                             
28616                             if(markerBytes == 0xffe1){
28617                                 _this.parseExifData(
28618                                     dataView,
28619                                     offset,
28620                                     markerLength
28621                                 );
28622                             }
28623                             
28624                             offset += markerLength;
28625                             
28626                             continue;
28627                         }
28628                         
28629                         break;
28630                     }
28631                     
28632                 }
28633                 
28634                 var url = _this.urlAPI.createObjectURL(_this.file);
28635                 
28636                 _this.loadCanvas(url);
28637                 
28638                 return;
28639             }
28640             
28641             reader.readAsArrayBuffer(this.file);
28642             
28643         }
28644         
28645     },
28646     
28647     parseExifData : function(dataView, offset, length)
28648     {
28649         var tiffOffset = offset + 10,
28650             littleEndian,
28651             dirOffset;
28652     
28653         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28654             // No Exif data, might be XMP data instead
28655             return;
28656         }
28657         
28658         // Check for the ASCII code for "Exif" (0x45786966):
28659         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28660             // No Exif data, might be XMP data instead
28661             return;
28662         }
28663         if (tiffOffset + 8 > dataView.byteLength) {
28664             Roo.log('Invalid Exif data: Invalid segment size.');
28665             return;
28666         }
28667         // Check for the two null bytes:
28668         if (dataView.getUint16(offset + 8) !== 0x0000) {
28669             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28670             return;
28671         }
28672         // Check the byte alignment:
28673         switch (dataView.getUint16(tiffOffset)) {
28674         case 0x4949:
28675             littleEndian = true;
28676             break;
28677         case 0x4D4D:
28678             littleEndian = false;
28679             break;
28680         default:
28681             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28682             return;
28683         }
28684         // Check for the TIFF tag marker (0x002A):
28685         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28686             Roo.log('Invalid Exif data: Missing TIFF marker.');
28687             return;
28688         }
28689         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28690         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28691         
28692         this.parseExifTags(
28693             dataView,
28694             tiffOffset,
28695             tiffOffset + dirOffset,
28696             littleEndian
28697         );
28698     },
28699     
28700     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28701     {
28702         var tagsNumber,
28703             dirEndOffset,
28704             i;
28705         if (dirOffset + 6 > dataView.byteLength) {
28706             Roo.log('Invalid Exif data: Invalid directory offset.');
28707             return;
28708         }
28709         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28710         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28711         if (dirEndOffset + 4 > dataView.byteLength) {
28712             Roo.log('Invalid Exif data: Invalid directory size.');
28713             return;
28714         }
28715         for (i = 0; i < tagsNumber; i += 1) {
28716             this.parseExifTag(
28717                 dataView,
28718                 tiffOffset,
28719                 dirOffset + 2 + 12 * i, // tag offset
28720                 littleEndian
28721             );
28722         }
28723         // Return the offset to the next directory:
28724         return dataView.getUint32(dirEndOffset, littleEndian);
28725     },
28726     
28727     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28728     {
28729         var tag = dataView.getUint16(offset, littleEndian);
28730         
28731         this.exif[tag] = this.getExifValue(
28732             dataView,
28733             tiffOffset,
28734             offset,
28735             dataView.getUint16(offset + 2, littleEndian), // tag type
28736             dataView.getUint32(offset + 4, littleEndian), // tag length
28737             littleEndian
28738         );
28739     },
28740     
28741     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28742     {
28743         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28744             tagSize,
28745             dataOffset,
28746             values,
28747             i,
28748             str,
28749             c;
28750     
28751         if (!tagType) {
28752             Roo.log('Invalid Exif data: Invalid tag type.');
28753             return;
28754         }
28755         
28756         tagSize = tagType.size * length;
28757         // Determine if the value is contained in the dataOffset bytes,
28758         // or if the value at the dataOffset is a pointer to the actual data:
28759         dataOffset = tagSize > 4 ?
28760                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28761         if (dataOffset + tagSize > dataView.byteLength) {
28762             Roo.log('Invalid Exif data: Invalid data offset.');
28763             return;
28764         }
28765         if (length === 1) {
28766             return tagType.getValue(dataView, dataOffset, littleEndian);
28767         }
28768         values = [];
28769         for (i = 0; i < length; i += 1) {
28770             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28771         }
28772         
28773         if (tagType.ascii) {
28774             str = '';
28775             // Concatenate the chars:
28776             for (i = 0; i < values.length; i += 1) {
28777                 c = values[i];
28778                 // Ignore the terminating NULL byte(s):
28779                 if (c === '\u0000') {
28780                     break;
28781                 }
28782                 str += c;
28783             }
28784             return str;
28785         }
28786         return values;
28787     }
28788     
28789 });
28790
28791 Roo.apply(Roo.bootstrap.UploadCropbox, {
28792     tags : {
28793         'Orientation': 0x0112
28794     },
28795     
28796     Orientation: {
28797             1: 0, //'top-left',
28798 //            2: 'top-right',
28799             3: 180, //'bottom-right',
28800 //            4: 'bottom-left',
28801 //            5: 'left-top',
28802             6: 90, //'right-top',
28803 //            7: 'right-bottom',
28804             8: 270 //'left-bottom'
28805     },
28806     
28807     exifTagTypes : {
28808         // byte, 8-bit unsigned int:
28809         1: {
28810             getValue: function (dataView, dataOffset) {
28811                 return dataView.getUint8(dataOffset);
28812             },
28813             size: 1
28814         },
28815         // ascii, 8-bit byte:
28816         2: {
28817             getValue: function (dataView, dataOffset) {
28818                 return String.fromCharCode(dataView.getUint8(dataOffset));
28819             },
28820             size: 1,
28821             ascii: true
28822         },
28823         // short, 16 bit int:
28824         3: {
28825             getValue: function (dataView, dataOffset, littleEndian) {
28826                 return dataView.getUint16(dataOffset, littleEndian);
28827             },
28828             size: 2
28829         },
28830         // long, 32 bit int:
28831         4: {
28832             getValue: function (dataView, dataOffset, littleEndian) {
28833                 return dataView.getUint32(dataOffset, littleEndian);
28834             },
28835             size: 4
28836         },
28837         // rational = two long values, first is numerator, second is denominator:
28838         5: {
28839             getValue: function (dataView, dataOffset, littleEndian) {
28840                 return dataView.getUint32(dataOffset, littleEndian) /
28841                     dataView.getUint32(dataOffset + 4, littleEndian);
28842             },
28843             size: 8
28844         },
28845         // slong, 32 bit signed int:
28846         9: {
28847             getValue: function (dataView, dataOffset, littleEndian) {
28848                 return dataView.getInt32(dataOffset, littleEndian);
28849             },
28850             size: 4
28851         },
28852         // srational, two slongs, first is numerator, second is denominator:
28853         10: {
28854             getValue: function (dataView, dataOffset, littleEndian) {
28855                 return dataView.getInt32(dataOffset, littleEndian) /
28856                     dataView.getInt32(dataOffset + 4, littleEndian);
28857             },
28858             size: 8
28859         }
28860     },
28861     
28862     footer : {
28863         STANDARD : [
28864             {
28865                 tag : 'div',
28866                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28867                 action : 'rotate-left',
28868                 cn : [
28869                     {
28870                         tag : 'button',
28871                         cls : 'btn btn-default',
28872                         html : '<i class="fa fa-undo"></i>'
28873                     }
28874                 ]
28875             },
28876             {
28877                 tag : 'div',
28878                 cls : 'btn-group roo-upload-cropbox-picture',
28879                 action : 'picture',
28880                 cn : [
28881                     {
28882                         tag : 'button',
28883                         cls : 'btn btn-default',
28884                         html : '<i class="fa fa-picture-o"></i>'
28885                     }
28886                 ]
28887             },
28888             {
28889                 tag : 'div',
28890                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28891                 action : 'rotate-right',
28892                 cn : [
28893                     {
28894                         tag : 'button',
28895                         cls : 'btn btn-default',
28896                         html : '<i class="fa fa-repeat"></i>'
28897                     }
28898                 ]
28899             }
28900         ],
28901         DOCUMENT : [
28902             {
28903                 tag : 'div',
28904                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28905                 action : 'rotate-left',
28906                 cn : [
28907                     {
28908                         tag : 'button',
28909                         cls : 'btn btn-default',
28910                         html : '<i class="fa fa-undo"></i>'
28911                     }
28912                 ]
28913             },
28914             {
28915                 tag : 'div',
28916                 cls : 'btn-group roo-upload-cropbox-download',
28917                 action : 'download',
28918                 cn : [
28919                     {
28920                         tag : 'button',
28921                         cls : 'btn btn-default',
28922                         html : '<i class="fa fa-download"></i>'
28923                     }
28924                 ]
28925             },
28926             {
28927                 tag : 'div',
28928                 cls : 'btn-group roo-upload-cropbox-crop',
28929                 action : 'crop',
28930                 cn : [
28931                     {
28932                         tag : 'button',
28933                         cls : 'btn btn-default',
28934                         html : '<i class="fa fa-crop"></i>'
28935                     }
28936                 ]
28937             },
28938             {
28939                 tag : 'div',
28940                 cls : 'btn-group roo-upload-cropbox-trash',
28941                 action : 'trash',
28942                 cn : [
28943                     {
28944                         tag : 'button',
28945                         cls : 'btn btn-default',
28946                         html : '<i class="fa fa-trash"></i>'
28947                     }
28948                 ]
28949             },
28950             {
28951                 tag : 'div',
28952                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28953                 action : 'rotate-right',
28954                 cn : [
28955                     {
28956                         tag : 'button',
28957                         cls : 'btn btn-default',
28958                         html : '<i class="fa fa-repeat"></i>'
28959                     }
28960                 ]
28961             }
28962         ],
28963         ROTATOR : [
28964             {
28965                 tag : 'div',
28966                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28967                 action : 'rotate-left',
28968                 cn : [
28969                     {
28970                         tag : 'button',
28971                         cls : 'btn btn-default',
28972                         html : '<i class="fa fa-undo"></i>'
28973                     }
28974                 ]
28975             },
28976             {
28977                 tag : 'div',
28978                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28979                 action : 'rotate-right',
28980                 cn : [
28981                     {
28982                         tag : 'button',
28983                         cls : 'btn btn-default',
28984                         html : '<i class="fa fa-repeat"></i>'
28985                     }
28986                 ]
28987             }
28988         ]
28989     }
28990 });
28991
28992 /*
28993 * Licence: LGPL
28994 */
28995
28996 /**
28997  * @class Roo.bootstrap.DocumentManager
28998  * @extends Roo.bootstrap.Component
28999  * Bootstrap DocumentManager class
29000  * @cfg {String} paramName default 'imageUpload'
29001  * @cfg {String} toolTipName default 'filename'
29002  * @cfg {String} method default POST
29003  * @cfg {String} url action url
29004  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29005  * @cfg {Boolean} multiple multiple upload default true
29006  * @cfg {Number} thumbSize default 300
29007  * @cfg {String} fieldLabel
29008  * @cfg {Number} labelWidth default 4
29009  * @cfg {String} labelAlign (left|top) default left
29010  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29011 * @cfg {Number} labellg set the width of label (1-12)
29012  * @cfg {Number} labelmd set the width of label (1-12)
29013  * @cfg {Number} labelsm set the width of label (1-12)
29014  * @cfg {Number} labelxs set the width of label (1-12)
29015  * 
29016  * @constructor
29017  * Create a new DocumentManager
29018  * @param {Object} config The config object
29019  */
29020
29021 Roo.bootstrap.DocumentManager = function(config){
29022     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29023     
29024     this.files = [];
29025     this.delegates = [];
29026     
29027     this.addEvents({
29028         /**
29029          * @event initial
29030          * Fire when initial the DocumentManager
29031          * @param {Roo.bootstrap.DocumentManager} this
29032          */
29033         "initial" : true,
29034         /**
29035          * @event inspect
29036          * inspect selected file
29037          * @param {Roo.bootstrap.DocumentManager} this
29038          * @param {File} file
29039          */
29040         "inspect" : true,
29041         /**
29042          * @event exception
29043          * Fire when xhr load exception
29044          * @param {Roo.bootstrap.DocumentManager} this
29045          * @param {XMLHttpRequest} xhr
29046          */
29047         "exception" : true,
29048         /**
29049          * @event afterupload
29050          * Fire when xhr load exception
29051          * @param {Roo.bootstrap.DocumentManager} this
29052          * @param {XMLHttpRequest} xhr
29053          */
29054         "afterupload" : true,
29055         /**
29056          * @event prepare
29057          * prepare the form data
29058          * @param {Roo.bootstrap.DocumentManager} this
29059          * @param {Object} formData
29060          */
29061         "prepare" : true,
29062         /**
29063          * @event remove
29064          * Fire when remove the file
29065          * @param {Roo.bootstrap.DocumentManager} this
29066          * @param {Object} file
29067          */
29068         "remove" : true,
29069         /**
29070          * @event refresh
29071          * Fire after refresh the file
29072          * @param {Roo.bootstrap.DocumentManager} this
29073          */
29074         "refresh" : true,
29075         /**
29076          * @event click
29077          * Fire after click the image
29078          * @param {Roo.bootstrap.DocumentManager} this
29079          * @param {Object} file
29080          */
29081         "click" : true,
29082         /**
29083          * @event edit
29084          * Fire when upload a image and editable set to true
29085          * @param {Roo.bootstrap.DocumentManager} this
29086          * @param {Object} file
29087          */
29088         "edit" : true,
29089         /**
29090          * @event beforeselectfile
29091          * Fire before select file
29092          * @param {Roo.bootstrap.DocumentManager} this
29093          */
29094         "beforeselectfile" : true,
29095         /**
29096          * @event process
29097          * Fire before process file
29098          * @param {Roo.bootstrap.DocumentManager} this
29099          * @param {Object} file
29100          */
29101         "process" : true,
29102         /**
29103          * @event previewrendered
29104          * Fire when preview rendered
29105          * @param {Roo.bootstrap.DocumentManager} this
29106          * @param {Object} file
29107          */
29108         "previewrendered" : true,
29109         /**
29110          */
29111         "previewResize" : true
29112         
29113     });
29114 };
29115
29116 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29117     
29118     boxes : 0,
29119     inputName : '',
29120     thumbSize : 300,
29121     multiple : true,
29122     files : false,
29123     method : 'POST',
29124     url : '',
29125     paramName : 'imageUpload',
29126     toolTipName : 'filename',
29127     fieldLabel : '',
29128     labelWidth : 4,
29129     labelAlign : 'left',
29130     editable : true,
29131     delegates : false,
29132     xhr : false, 
29133     
29134     labellg : 0,
29135     labelmd : 0,
29136     labelsm : 0,
29137     labelxs : 0,
29138     
29139     getAutoCreate : function()
29140     {   
29141         var managerWidget = {
29142             tag : 'div',
29143             cls : 'roo-document-manager',
29144             cn : [
29145                 {
29146                     tag : 'input',
29147                     cls : 'roo-document-manager-selector',
29148                     type : 'file'
29149                 },
29150                 {
29151                     tag : 'div',
29152                     cls : 'roo-document-manager-uploader',
29153                     cn : [
29154                         {
29155                             tag : 'div',
29156                             cls : 'roo-document-manager-upload-btn',
29157                             html : '<i class="fa fa-plus"></i>'
29158                         }
29159                     ]
29160                     
29161                 }
29162             ]
29163         };
29164         
29165         var content = [
29166             {
29167                 tag : 'div',
29168                 cls : 'column col-md-12',
29169                 cn : managerWidget
29170             }
29171         ];
29172         
29173         if(this.fieldLabel.length){
29174             
29175             content = [
29176                 {
29177                     tag : 'div',
29178                     cls : 'column col-md-12',
29179                     html : this.fieldLabel
29180                 },
29181                 {
29182                     tag : 'div',
29183                     cls : 'column col-md-12',
29184                     cn : managerWidget
29185                 }
29186             ];
29187
29188             if(this.labelAlign == 'left'){
29189                 content = [
29190                     {
29191                         tag : 'div',
29192                         cls : 'column',
29193                         html : this.fieldLabel
29194                     },
29195                     {
29196                         tag : 'div',
29197                         cls : 'column',
29198                         cn : managerWidget
29199                     }
29200                 ];
29201                 
29202                 if(this.labelWidth > 12){
29203                     content[0].style = "width: " + this.labelWidth + 'px';
29204                 }
29205
29206                 if(this.labelWidth < 13 && this.labelmd == 0){
29207                     this.labelmd = this.labelWidth;
29208                 }
29209
29210                 if(this.labellg > 0){
29211                     content[0].cls += ' col-lg-' + this.labellg;
29212                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29213                 }
29214
29215                 if(this.labelmd > 0){
29216                     content[0].cls += ' col-md-' + this.labelmd;
29217                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29218                 }
29219
29220                 if(this.labelsm > 0){
29221                     content[0].cls += ' col-sm-' + this.labelsm;
29222                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29223                 }
29224
29225                 if(this.labelxs > 0){
29226                     content[0].cls += ' col-xs-' + this.labelxs;
29227                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29228                 }
29229                 
29230             }
29231         }
29232         
29233         var cfg = {
29234             tag : 'div',
29235             cls : 'row clearfix',
29236             cn : content
29237         };
29238         
29239         return cfg;
29240         
29241     },
29242     
29243     initEvents : function()
29244     {
29245         this.managerEl = this.el.select('.roo-document-manager', true).first();
29246         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29247         
29248         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29249         this.selectorEl.hide();
29250         
29251         if(this.multiple){
29252             this.selectorEl.attr('multiple', 'multiple');
29253         }
29254         
29255         this.selectorEl.on('change', this.onFileSelected, this);
29256         
29257         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29258         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29259         
29260         this.uploader.on('click', this.onUploaderClick, this);
29261         
29262         this.renderProgressDialog();
29263         
29264         var _this = this;
29265         
29266         window.addEventListener("resize", function() { _this.refresh(); } );
29267         
29268         this.fireEvent('initial', this);
29269     },
29270     
29271     renderProgressDialog : function()
29272     {
29273         var _this = this;
29274         
29275         this.progressDialog = new Roo.bootstrap.Modal({
29276             cls : 'roo-document-manager-progress-dialog',
29277             allow_close : false,
29278             animate : false,
29279             title : '',
29280             buttons : [
29281                 {
29282                     name  :'cancel',
29283                     weight : 'danger',
29284                     html : 'Cancel'
29285                 }
29286             ], 
29287             listeners : { 
29288                 btnclick : function() {
29289                     _this.uploadCancel();
29290                     this.hide();
29291                 }
29292             }
29293         });
29294          
29295         this.progressDialog.render(Roo.get(document.body));
29296          
29297         this.progress = new Roo.bootstrap.Progress({
29298             cls : 'roo-document-manager-progress',
29299             active : true,
29300             striped : true
29301         });
29302         
29303         this.progress.render(this.progressDialog.getChildContainer());
29304         
29305         this.progressBar = new Roo.bootstrap.ProgressBar({
29306             cls : 'roo-document-manager-progress-bar',
29307             aria_valuenow : 0,
29308             aria_valuemin : 0,
29309             aria_valuemax : 12,
29310             panel : 'success'
29311         });
29312         
29313         this.progressBar.render(this.progress.getChildContainer());
29314     },
29315     
29316     onUploaderClick : function(e)
29317     {
29318         e.preventDefault();
29319      
29320         if(this.fireEvent('beforeselectfile', this) != false){
29321             this.selectorEl.dom.click();
29322         }
29323         
29324     },
29325     
29326     onFileSelected : function(e)
29327     {
29328         e.preventDefault();
29329         
29330         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29331             return;
29332         }
29333         
29334         Roo.each(this.selectorEl.dom.files, function(file){
29335             if(this.fireEvent('inspect', this, file) != false){
29336                 this.files.push(file);
29337             }
29338         }, this);
29339         
29340         this.queue();
29341         
29342     },
29343     
29344     queue : function()
29345     {
29346         this.selectorEl.dom.value = '';
29347         
29348         if(!this.files || !this.files.length){
29349             return;
29350         }
29351         
29352         if(this.boxes > 0 && this.files.length > this.boxes){
29353             this.files = this.files.slice(0, this.boxes);
29354         }
29355         
29356         this.uploader.show();
29357         
29358         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29359             this.uploader.hide();
29360         }
29361         
29362         var _this = this;
29363         
29364         var files = [];
29365         
29366         var docs = [];
29367         
29368         Roo.each(this.files, function(file){
29369             
29370             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29371                 var f = this.renderPreview(file);
29372                 files.push(f);
29373                 return;
29374             }
29375             
29376             if(file.type.indexOf('image') != -1){
29377                 this.delegates.push(
29378                     (function(){
29379                         _this.process(file);
29380                     }).createDelegate(this)
29381                 );
29382         
29383                 return;
29384             }
29385             
29386             docs.push(
29387                 (function(){
29388                     _this.process(file);
29389                 }).createDelegate(this)
29390             );
29391             
29392         }, this);
29393         
29394         this.files = files;
29395         
29396         this.delegates = this.delegates.concat(docs);
29397         
29398         if(!this.delegates.length){
29399             this.refresh();
29400             return;
29401         }
29402         
29403         this.progressBar.aria_valuemax = this.delegates.length;
29404         
29405         this.arrange();
29406         
29407         return;
29408     },
29409     
29410     arrange : function()
29411     {
29412         if(!this.delegates.length){
29413             this.progressDialog.hide();
29414             this.refresh();
29415             return;
29416         }
29417         
29418         var delegate = this.delegates.shift();
29419         
29420         this.progressDialog.show();
29421         
29422         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29423         
29424         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29425         
29426         delegate();
29427     },
29428     
29429     refresh : function()
29430     {
29431         this.uploader.show();
29432         
29433         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29434             this.uploader.hide();
29435         }
29436         
29437         Roo.isTouch ? this.closable(false) : this.closable(true);
29438         
29439         this.fireEvent('refresh', this);
29440     },
29441     
29442     onRemove : function(e, el, o)
29443     {
29444         e.preventDefault();
29445         
29446         this.fireEvent('remove', this, o);
29447         
29448     },
29449     
29450     remove : function(o)
29451     {
29452         var files = [];
29453         
29454         Roo.each(this.files, function(file){
29455             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29456                 files.push(file);
29457                 return;
29458             }
29459
29460             o.target.remove();
29461
29462         }, this);
29463         
29464         this.files = files;
29465         
29466         this.refresh();
29467     },
29468     
29469     clear : function()
29470     {
29471         Roo.each(this.files, function(file){
29472             if(!file.target){
29473                 return;
29474             }
29475             
29476             file.target.remove();
29477
29478         }, this);
29479         
29480         this.files = [];
29481         
29482         this.refresh();
29483     },
29484     
29485     onClick : function(e, el, o)
29486     {
29487         e.preventDefault();
29488         
29489         this.fireEvent('click', this, o);
29490         
29491     },
29492     
29493     closable : function(closable)
29494     {
29495         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29496             
29497             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29498             
29499             if(closable){
29500                 el.show();
29501                 return;
29502             }
29503             
29504             el.hide();
29505             
29506         }, this);
29507     },
29508     
29509     xhrOnLoad : function(xhr)
29510     {
29511         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29512             el.remove();
29513         }, this);
29514         
29515         if (xhr.readyState !== 4) {
29516             this.arrange();
29517             this.fireEvent('exception', this, xhr);
29518             return;
29519         }
29520
29521         var response = Roo.decode(xhr.responseText);
29522         
29523         if(!response.success){
29524             this.arrange();
29525             this.fireEvent('exception', this, xhr);
29526             return;
29527         }
29528         
29529         var file = this.renderPreview(response.data);
29530         
29531         this.files.push(file);
29532         
29533         this.arrange();
29534         
29535         this.fireEvent('afterupload', this, xhr);
29536         
29537     },
29538     
29539     xhrOnError : function(xhr)
29540     {
29541         Roo.log('xhr on error');
29542         
29543         var response = Roo.decode(xhr.responseText);
29544           
29545         Roo.log(response);
29546         
29547         this.arrange();
29548     },
29549     
29550     process : function(file)
29551     {
29552         if(this.fireEvent('process', this, file) !== false){
29553             if(this.editable && file.type.indexOf('image') != -1){
29554                 this.fireEvent('edit', this, file);
29555                 return;
29556             }
29557
29558             this.uploadStart(file, false);
29559
29560             return;
29561         }
29562         
29563     },
29564     
29565     uploadStart : function(file, crop)
29566     {
29567         this.xhr = new XMLHttpRequest();
29568         
29569         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29570             this.arrange();
29571             return;
29572         }
29573         
29574         file.xhr = this.xhr;
29575             
29576         this.managerEl.createChild({
29577             tag : 'div',
29578             cls : 'roo-document-manager-loading',
29579             cn : [
29580                 {
29581                     tag : 'div',
29582                     tooltip : file.name,
29583                     cls : 'roo-document-manager-thumb',
29584                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29585                 }
29586             ]
29587
29588         });
29589
29590         this.xhr.open(this.method, this.url, true);
29591         
29592         var headers = {
29593             "Accept": "application/json",
29594             "Cache-Control": "no-cache",
29595             "X-Requested-With": "XMLHttpRequest"
29596         };
29597         
29598         for (var headerName in headers) {
29599             var headerValue = headers[headerName];
29600             if (headerValue) {
29601                 this.xhr.setRequestHeader(headerName, headerValue);
29602             }
29603         }
29604         
29605         var _this = this;
29606         
29607         this.xhr.onload = function()
29608         {
29609             _this.xhrOnLoad(_this.xhr);
29610         }
29611         
29612         this.xhr.onerror = function()
29613         {
29614             _this.xhrOnError(_this.xhr);
29615         }
29616         
29617         var formData = new FormData();
29618
29619         formData.append('returnHTML', 'NO');
29620         
29621         if(crop){
29622             formData.append('crop', crop);
29623         }
29624         
29625         formData.append(this.paramName, file, file.name);
29626         
29627         var options = {
29628             file : file, 
29629             manually : false
29630         };
29631         
29632         if(this.fireEvent('prepare', this, formData, options) != false){
29633             
29634             if(options.manually){
29635                 return;
29636             }
29637             
29638             this.xhr.send(formData);
29639             return;
29640         };
29641         
29642         this.uploadCancel();
29643     },
29644     
29645     uploadCancel : function()
29646     {
29647         if (this.xhr) {
29648             this.xhr.abort();
29649         }
29650         
29651         this.delegates = [];
29652         
29653         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29654             el.remove();
29655         }, this);
29656         
29657         this.arrange();
29658     },
29659     
29660     renderPreview : function(file)
29661     {
29662         if(typeof(file.target) != 'undefined' && file.target){
29663             return file;
29664         }
29665         
29666         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29667         
29668         var previewEl = this.managerEl.createChild({
29669             tag : 'div',
29670             cls : 'roo-document-manager-preview',
29671             cn : [
29672                 {
29673                     tag : 'div',
29674                     tooltip : file[this.toolTipName],
29675                     cls : 'roo-document-manager-thumb',
29676                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29677                 },
29678                 {
29679                     tag : 'button',
29680                     cls : 'close',
29681                     html : '<i class="fa fa-times-circle"></i>'
29682                 }
29683             ]
29684         });
29685
29686         var close = previewEl.select('button.close', true).first();
29687
29688         close.on('click', this.onRemove, this, file);
29689
29690         file.target = previewEl;
29691
29692         var image = previewEl.select('img', true).first();
29693         
29694         var _this = this;
29695         
29696         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29697         
29698         image.on('click', this.onClick, this, file);
29699         
29700         this.fireEvent('previewrendered', this, file);
29701         
29702         return file;
29703         
29704     },
29705     
29706     onPreviewLoad : function(file, image)
29707     {
29708         if(typeof(file.target) == 'undefined' || !file.target){
29709             return;
29710         }
29711         
29712         var width = image.dom.naturalWidth || image.dom.width;
29713         var height = image.dom.naturalHeight || image.dom.height;
29714         
29715         if(!this.previewResize) {
29716             return;
29717         }
29718         
29719         if(width > height){
29720             file.target.addClass('wide');
29721             return;
29722         }
29723         
29724         file.target.addClass('tall');
29725         return;
29726         
29727     },
29728     
29729     uploadFromSource : function(file, crop)
29730     {
29731         this.xhr = new XMLHttpRequest();
29732         
29733         this.managerEl.createChild({
29734             tag : 'div',
29735             cls : 'roo-document-manager-loading',
29736             cn : [
29737                 {
29738                     tag : 'div',
29739                     tooltip : file.name,
29740                     cls : 'roo-document-manager-thumb',
29741                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29742                 }
29743             ]
29744
29745         });
29746
29747         this.xhr.open(this.method, this.url, true);
29748         
29749         var headers = {
29750             "Accept": "application/json",
29751             "Cache-Control": "no-cache",
29752             "X-Requested-With": "XMLHttpRequest"
29753         };
29754         
29755         for (var headerName in headers) {
29756             var headerValue = headers[headerName];
29757             if (headerValue) {
29758                 this.xhr.setRequestHeader(headerName, headerValue);
29759             }
29760         }
29761         
29762         var _this = this;
29763         
29764         this.xhr.onload = function()
29765         {
29766             _this.xhrOnLoad(_this.xhr);
29767         }
29768         
29769         this.xhr.onerror = function()
29770         {
29771             _this.xhrOnError(_this.xhr);
29772         }
29773         
29774         var formData = new FormData();
29775
29776         formData.append('returnHTML', 'NO');
29777         
29778         formData.append('crop', crop);
29779         
29780         if(typeof(file.filename) != 'undefined'){
29781             formData.append('filename', file.filename);
29782         }
29783         
29784         if(typeof(file.mimetype) != 'undefined'){
29785             formData.append('mimetype', file.mimetype);
29786         }
29787         
29788         Roo.log(formData);
29789         
29790         if(this.fireEvent('prepare', this, formData) != false){
29791             this.xhr.send(formData);
29792         };
29793     }
29794 });
29795
29796 /*
29797 * Licence: LGPL
29798 */
29799
29800 /**
29801  * @class Roo.bootstrap.DocumentViewer
29802  * @extends Roo.bootstrap.Component
29803  * Bootstrap DocumentViewer class
29804  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29805  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29806  * 
29807  * @constructor
29808  * Create a new DocumentViewer
29809  * @param {Object} config The config object
29810  */
29811
29812 Roo.bootstrap.DocumentViewer = function(config){
29813     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29814     
29815     this.addEvents({
29816         /**
29817          * @event initial
29818          * Fire after initEvent
29819          * @param {Roo.bootstrap.DocumentViewer} this
29820          */
29821         "initial" : true,
29822         /**
29823          * @event click
29824          * Fire after click
29825          * @param {Roo.bootstrap.DocumentViewer} this
29826          */
29827         "click" : true,
29828         /**
29829          * @event download
29830          * Fire after download button
29831          * @param {Roo.bootstrap.DocumentViewer} this
29832          */
29833         "download" : true,
29834         /**
29835          * @event trash
29836          * Fire after trash button
29837          * @param {Roo.bootstrap.DocumentViewer} this
29838          */
29839         "trash" : true
29840         
29841     });
29842 };
29843
29844 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29845     
29846     showDownload : true,
29847     
29848     showTrash : true,
29849     
29850     getAutoCreate : function()
29851     {
29852         var cfg = {
29853             tag : 'div',
29854             cls : 'roo-document-viewer',
29855             cn : [
29856                 {
29857                     tag : 'div',
29858                     cls : 'roo-document-viewer-body',
29859                     cn : [
29860                         {
29861                             tag : 'div',
29862                             cls : 'roo-document-viewer-thumb',
29863                             cn : [
29864                                 {
29865                                     tag : 'img',
29866                                     cls : 'roo-document-viewer-image'
29867                                 }
29868                             ]
29869                         }
29870                     ]
29871                 },
29872                 {
29873                     tag : 'div',
29874                     cls : 'roo-document-viewer-footer',
29875                     cn : {
29876                         tag : 'div',
29877                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29878                         cn : [
29879                             {
29880                                 tag : 'div',
29881                                 cls : 'btn-group roo-document-viewer-download',
29882                                 cn : [
29883                                     {
29884                                         tag : 'button',
29885                                         cls : 'btn btn-default',
29886                                         html : '<i class="fa fa-download"></i>'
29887                                     }
29888                                 ]
29889                             },
29890                             {
29891                                 tag : 'div',
29892                                 cls : 'btn-group roo-document-viewer-trash',
29893                                 cn : [
29894                                     {
29895                                         tag : 'button',
29896                                         cls : 'btn btn-default',
29897                                         html : '<i class="fa fa-trash"></i>'
29898                                     }
29899                                 ]
29900                             }
29901                         ]
29902                     }
29903                 }
29904             ]
29905         };
29906         
29907         return cfg;
29908     },
29909     
29910     initEvents : function()
29911     {
29912         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29913         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29914         
29915         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29916         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29917         
29918         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29919         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29920         
29921         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29922         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29923         
29924         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29925         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29926         
29927         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29928         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29929         
29930         this.bodyEl.on('click', this.onClick, this);
29931         this.downloadBtn.on('click', this.onDownload, this);
29932         this.trashBtn.on('click', this.onTrash, this);
29933         
29934         this.downloadBtn.hide();
29935         this.trashBtn.hide();
29936         
29937         if(this.showDownload){
29938             this.downloadBtn.show();
29939         }
29940         
29941         if(this.showTrash){
29942             this.trashBtn.show();
29943         }
29944         
29945         if(!this.showDownload && !this.showTrash) {
29946             this.footerEl.hide();
29947         }
29948         
29949     },
29950     
29951     initial : function()
29952     {
29953         this.fireEvent('initial', this);
29954         
29955     },
29956     
29957     onClick : function(e)
29958     {
29959         e.preventDefault();
29960         
29961         this.fireEvent('click', this);
29962     },
29963     
29964     onDownload : function(e)
29965     {
29966         e.preventDefault();
29967         
29968         this.fireEvent('download', this);
29969     },
29970     
29971     onTrash : function(e)
29972     {
29973         e.preventDefault();
29974         
29975         this.fireEvent('trash', this);
29976     }
29977     
29978 });
29979 /*
29980  * - LGPL
29981  *
29982  * nav progress bar
29983  * 
29984  */
29985
29986 /**
29987  * @class Roo.bootstrap.NavProgressBar
29988  * @extends Roo.bootstrap.Component
29989  * Bootstrap NavProgressBar class
29990  * 
29991  * @constructor
29992  * Create a new nav progress bar
29993  * @param {Object} config The config object
29994  */
29995
29996 Roo.bootstrap.NavProgressBar = function(config){
29997     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29998
29999     this.bullets = this.bullets || [];
30000    
30001 //    Roo.bootstrap.NavProgressBar.register(this);
30002      this.addEvents({
30003         /**
30004              * @event changed
30005              * Fires when the active item changes
30006              * @param {Roo.bootstrap.NavProgressBar} this
30007              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30008              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
30009          */
30010         'changed': true
30011      });
30012     
30013 };
30014
30015 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
30016     
30017     bullets : [],
30018     barItems : [],
30019     
30020     getAutoCreate : function()
30021     {
30022         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30023         
30024         cfg = {
30025             tag : 'div',
30026             cls : 'roo-navigation-bar-group',
30027             cn : [
30028                 {
30029                     tag : 'div',
30030                     cls : 'roo-navigation-top-bar'
30031                 },
30032                 {
30033                     tag : 'div',
30034                     cls : 'roo-navigation-bullets-bar',
30035                     cn : [
30036                         {
30037                             tag : 'ul',
30038                             cls : 'roo-navigation-bar'
30039                         }
30040                     ]
30041                 },
30042                 
30043                 {
30044                     tag : 'div',
30045                     cls : 'roo-navigation-bottom-bar'
30046                 }
30047             ]
30048             
30049         };
30050         
30051         return cfg;
30052         
30053     },
30054     
30055     initEvents: function() 
30056     {
30057         
30058     },
30059     
30060     onRender : function(ct, position) 
30061     {
30062         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30063         
30064         if(this.bullets.length){
30065             Roo.each(this.bullets, function(b){
30066                this.addItem(b);
30067             }, this);
30068         }
30069         
30070         this.format();
30071         
30072     },
30073     
30074     addItem : function(cfg)
30075     {
30076         var item = new Roo.bootstrap.NavProgressItem(cfg);
30077         
30078         item.parentId = this.id;
30079         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30080         
30081         if(cfg.html){
30082             var top = new Roo.bootstrap.Element({
30083                 tag : 'div',
30084                 cls : 'roo-navigation-bar-text'
30085             });
30086             
30087             var bottom = new Roo.bootstrap.Element({
30088                 tag : 'div',
30089                 cls : 'roo-navigation-bar-text'
30090             });
30091             
30092             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30093             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30094             
30095             var topText = new Roo.bootstrap.Element({
30096                 tag : 'span',
30097                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30098             });
30099             
30100             var bottomText = new Roo.bootstrap.Element({
30101                 tag : 'span',
30102                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30103             });
30104             
30105             topText.onRender(top.el, null);
30106             bottomText.onRender(bottom.el, null);
30107             
30108             item.topEl = top;
30109             item.bottomEl = bottom;
30110         }
30111         
30112         this.barItems.push(item);
30113         
30114         return item;
30115     },
30116     
30117     getActive : function()
30118     {
30119         var active = false;
30120         
30121         Roo.each(this.barItems, function(v){
30122             
30123             if (!v.isActive()) {
30124                 return;
30125             }
30126             
30127             active = v;
30128             return false;
30129             
30130         });
30131         
30132         return active;
30133     },
30134     
30135     setActiveItem : function(item)
30136     {
30137         var prev = false;
30138         
30139         Roo.each(this.barItems, function(v){
30140             if (v.rid == item.rid) {
30141                 return ;
30142             }
30143             
30144             if (v.isActive()) {
30145                 v.setActive(false);
30146                 prev = v;
30147             }
30148         });
30149
30150         item.setActive(true);
30151         
30152         this.fireEvent('changed', this, item, prev);
30153     },
30154     
30155     getBarItem: function(rid)
30156     {
30157         var ret = false;
30158         
30159         Roo.each(this.barItems, function(e) {
30160             if (e.rid != rid) {
30161                 return;
30162             }
30163             
30164             ret =  e;
30165             return false;
30166         });
30167         
30168         return ret;
30169     },
30170     
30171     indexOfItem : function(item)
30172     {
30173         var index = false;
30174         
30175         Roo.each(this.barItems, function(v, i){
30176             
30177             if (v.rid != item.rid) {
30178                 return;
30179             }
30180             
30181             index = i;
30182             return false
30183         });
30184         
30185         return index;
30186     },
30187     
30188     setActiveNext : function()
30189     {
30190         var i = this.indexOfItem(this.getActive());
30191         
30192         if (i > this.barItems.length) {
30193             return;
30194         }
30195         
30196         this.setActiveItem(this.barItems[i+1]);
30197     },
30198     
30199     setActivePrev : function()
30200     {
30201         var i = this.indexOfItem(this.getActive());
30202         
30203         if (i  < 1) {
30204             return;
30205         }
30206         
30207         this.setActiveItem(this.barItems[i-1]);
30208     },
30209     
30210     format : function()
30211     {
30212         if(!this.barItems.length){
30213             return;
30214         }
30215      
30216         var width = 100 / this.barItems.length;
30217         
30218         Roo.each(this.barItems, function(i){
30219             i.el.setStyle('width', width + '%');
30220             i.topEl.el.setStyle('width', width + '%');
30221             i.bottomEl.el.setStyle('width', width + '%');
30222         }, this);
30223         
30224     }
30225     
30226 });
30227 /*
30228  * - LGPL
30229  *
30230  * Nav Progress Item
30231  * 
30232  */
30233
30234 /**
30235  * @class Roo.bootstrap.NavProgressItem
30236  * @extends Roo.bootstrap.Component
30237  * Bootstrap NavProgressItem class
30238  * @cfg {String} rid the reference id
30239  * @cfg {Boolean} active (true|false) Is item active default false
30240  * @cfg {Boolean} disabled (true|false) Is item active default false
30241  * @cfg {String} html
30242  * @cfg {String} position (top|bottom) text position default bottom
30243  * @cfg {String} icon show icon instead of number
30244  * 
30245  * @constructor
30246  * Create a new NavProgressItem
30247  * @param {Object} config The config object
30248  */
30249 Roo.bootstrap.NavProgressItem = function(config){
30250     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30251     this.addEvents({
30252         // raw events
30253         /**
30254          * @event click
30255          * The raw click event for the entire grid.
30256          * @param {Roo.bootstrap.NavProgressItem} this
30257          * @param {Roo.EventObject} e
30258          */
30259         "click" : true
30260     });
30261    
30262 };
30263
30264 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30265     
30266     rid : '',
30267     active : false,
30268     disabled : false,
30269     html : '',
30270     position : 'bottom',
30271     icon : false,
30272     
30273     getAutoCreate : function()
30274     {
30275         var iconCls = 'roo-navigation-bar-item-icon';
30276         
30277         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30278         
30279         var cfg = {
30280             tag: 'li',
30281             cls: 'roo-navigation-bar-item',
30282             cn : [
30283                 {
30284                     tag : 'i',
30285                     cls : iconCls
30286                 }
30287             ]
30288         };
30289         
30290         if(this.active){
30291             cfg.cls += ' active';
30292         }
30293         if(this.disabled){
30294             cfg.cls += ' disabled';
30295         }
30296         
30297         return cfg;
30298     },
30299     
30300     disable : function()
30301     {
30302         this.setDisabled(true);
30303     },
30304     
30305     enable : function()
30306     {
30307         this.setDisabled(false);
30308     },
30309     
30310     initEvents: function() 
30311     {
30312         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30313         
30314         this.iconEl.on('click', this.onClick, this);
30315     },
30316     
30317     onClick : function(e)
30318     {
30319         e.preventDefault();
30320         
30321         if(this.disabled){
30322             return;
30323         }
30324         
30325         if(this.fireEvent('click', this, e) === false){
30326             return;
30327         };
30328         
30329         this.parent().setActiveItem(this);
30330     },
30331     
30332     isActive: function () 
30333     {
30334         return this.active;
30335     },
30336     
30337     setActive : function(state)
30338     {
30339         if(this.active == state){
30340             return;
30341         }
30342         
30343         this.active = state;
30344         
30345         if (state) {
30346             this.el.addClass('active');
30347             return;
30348         }
30349         
30350         this.el.removeClass('active');
30351         
30352         return;
30353     },
30354     
30355     setDisabled : function(state)
30356     {
30357         if(this.disabled == state){
30358             return;
30359         }
30360         
30361         this.disabled = state;
30362         
30363         if (state) {
30364             this.el.addClass('disabled');
30365             return;
30366         }
30367         
30368         this.el.removeClass('disabled');
30369     },
30370     
30371     tooltipEl : function()
30372     {
30373         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30374     }
30375 });
30376  
30377
30378  /*
30379  * - LGPL
30380  *
30381  * FieldLabel
30382  * 
30383  */
30384
30385 /**
30386  * @class Roo.bootstrap.FieldLabel
30387  * @extends Roo.bootstrap.Component
30388  * Bootstrap FieldLabel class
30389  * @cfg {String} html contents of the element
30390  * @cfg {String} tag tag of the element default label
30391  * @cfg {String} cls class of the element
30392  * @cfg {String} target label target 
30393  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30394  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30395  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30396  * @cfg {String} iconTooltip default "This field is required"
30397  * @cfg {String} indicatorpos (left|right) default left
30398  * 
30399  * @constructor
30400  * Create a new FieldLabel
30401  * @param {Object} config The config object
30402  */
30403
30404 Roo.bootstrap.FieldLabel = function(config){
30405     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30406     
30407     this.addEvents({
30408             /**
30409              * @event invalid
30410              * Fires after the field has been marked as invalid.
30411              * @param {Roo.form.FieldLabel} this
30412              * @param {String} msg The validation message
30413              */
30414             invalid : true,
30415             /**
30416              * @event valid
30417              * Fires after the field has been validated with no errors.
30418              * @param {Roo.form.FieldLabel} this
30419              */
30420             valid : true
30421         });
30422 };
30423
30424 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30425     
30426     tag: 'label',
30427     cls: '',
30428     html: '',
30429     target: '',
30430     allowBlank : true,
30431     invalidClass : 'has-warning',
30432     validClass : 'has-success',
30433     iconTooltip : 'This field is required',
30434     indicatorpos : 'left',
30435     
30436     getAutoCreate : function(){
30437         
30438         var cls = "";
30439         if (!this.allowBlank) {
30440             cls  = "visible";
30441         }
30442         
30443         var cfg = {
30444             tag : this.tag,
30445             cls : 'roo-bootstrap-field-label ' + this.cls,
30446             for : this.target,
30447             cn : [
30448                 {
30449                     tag : 'i',
30450                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30451                     tooltip : this.iconTooltip
30452                 },
30453                 {
30454                     tag : 'span',
30455                     html : this.html
30456                 }
30457             ] 
30458         };
30459         
30460         if(this.indicatorpos == 'right'){
30461             var cfg = {
30462                 tag : this.tag,
30463                 cls : 'roo-bootstrap-field-label ' + this.cls,
30464                 for : this.target,
30465                 cn : [
30466                     {
30467                         tag : 'span',
30468                         html : this.html
30469                     },
30470                     {
30471                         tag : 'i',
30472                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30473                         tooltip : this.iconTooltip
30474                     }
30475                 ] 
30476             };
30477         }
30478         
30479         return cfg;
30480     },
30481     
30482     initEvents: function() 
30483     {
30484         Roo.bootstrap.Element.superclass.initEvents.call(this);
30485         
30486         this.indicator = this.indicatorEl();
30487         
30488         if(this.indicator){
30489             this.indicator.removeClass('visible');
30490             this.indicator.addClass('invisible');
30491         }
30492         
30493         Roo.bootstrap.FieldLabel.register(this);
30494     },
30495     
30496     indicatorEl : function()
30497     {
30498         var indicator = this.el.select('i.roo-required-indicator',true).first();
30499         
30500         if(!indicator){
30501             return false;
30502         }
30503         
30504         return indicator;
30505         
30506     },
30507     
30508     /**
30509      * Mark this field as valid
30510      */
30511     markValid : function()
30512     {
30513         if(this.indicator){
30514             this.indicator.removeClass('visible');
30515             this.indicator.addClass('invisible');
30516         }
30517         if (Roo.bootstrap.version == 3) {
30518             this.el.removeClass(this.invalidClass);
30519             this.el.addClass(this.validClass);
30520         } else {
30521             this.el.removeClass('is-invalid');
30522             this.el.addClass('is-valid');
30523         }
30524         
30525         
30526         this.fireEvent('valid', this);
30527     },
30528     
30529     /**
30530      * Mark this field as invalid
30531      * @param {String} msg The validation message
30532      */
30533     markInvalid : function(msg)
30534     {
30535         if(this.indicator){
30536             this.indicator.removeClass('invisible');
30537             this.indicator.addClass('visible');
30538         }
30539           if (Roo.bootstrap.version == 3) {
30540             this.el.removeClass(this.validClass);
30541             this.el.addClass(this.invalidClass);
30542         } else {
30543             this.el.removeClass('is-valid');
30544             this.el.addClass('is-invalid');
30545         }
30546         
30547         
30548         this.fireEvent('invalid', this, msg);
30549     }
30550     
30551    
30552 });
30553
30554 Roo.apply(Roo.bootstrap.FieldLabel, {
30555     
30556     groups: {},
30557     
30558      /**
30559     * register a FieldLabel Group
30560     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30561     */
30562     register : function(label)
30563     {
30564         if(this.groups.hasOwnProperty(label.target)){
30565             return;
30566         }
30567      
30568         this.groups[label.target] = label;
30569         
30570     },
30571     /**
30572     * fetch a FieldLabel Group based on the target
30573     * @param {string} target
30574     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30575     */
30576     get: function(target) {
30577         if (typeof(this.groups[target]) == 'undefined') {
30578             return false;
30579         }
30580         
30581         return this.groups[target] ;
30582     }
30583 });
30584
30585  
30586
30587  /*
30588  * - LGPL
30589  *
30590  * page DateSplitField.
30591  * 
30592  */
30593
30594
30595 /**
30596  * @class Roo.bootstrap.DateSplitField
30597  * @extends Roo.bootstrap.Component
30598  * Bootstrap DateSplitField class
30599  * @cfg {string} fieldLabel - the label associated
30600  * @cfg {Number} labelWidth set the width of label (0-12)
30601  * @cfg {String} labelAlign (top|left)
30602  * @cfg {Boolean} dayAllowBlank (true|false) default false
30603  * @cfg {Boolean} monthAllowBlank (true|false) default false
30604  * @cfg {Boolean} yearAllowBlank (true|false) default false
30605  * @cfg {string} dayPlaceholder 
30606  * @cfg {string} monthPlaceholder
30607  * @cfg {string} yearPlaceholder
30608  * @cfg {string} dayFormat default 'd'
30609  * @cfg {string} monthFormat default 'm'
30610  * @cfg {string} yearFormat default 'Y'
30611  * @cfg {Number} labellg set the width of label (1-12)
30612  * @cfg {Number} labelmd set the width of label (1-12)
30613  * @cfg {Number} labelsm set the width of label (1-12)
30614  * @cfg {Number} labelxs set the width of label (1-12)
30615
30616  *     
30617  * @constructor
30618  * Create a new DateSplitField
30619  * @param {Object} config The config object
30620  */
30621
30622 Roo.bootstrap.DateSplitField = function(config){
30623     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30624     
30625     this.addEvents({
30626         // raw events
30627          /**
30628          * @event years
30629          * getting the data of years
30630          * @param {Roo.bootstrap.DateSplitField} this
30631          * @param {Object} years
30632          */
30633         "years" : true,
30634         /**
30635          * @event days
30636          * getting the data of days
30637          * @param {Roo.bootstrap.DateSplitField} this
30638          * @param {Object} days
30639          */
30640         "days" : true,
30641         /**
30642          * @event invalid
30643          * Fires after the field has been marked as invalid.
30644          * @param {Roo.form.Field} this
30645          * @param {String} msg The validation message
30646          */
30647         invalid : true,
30648        /**
30649          * @event valid
30650          * Fires after the field has been validated with no errors.
30651          * @param {Roo.form.Field} this
30652          */
30653         valid : true
30654     });
30655 };
30656
30657 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30658     
30659     fieldLabel : '',
30660     labelAlign : 'top',
30661     labelWidth : 3,
30662     dayAllowBlank : false,
30663     monthAllowBlank : false,
30664     yearAllowBlank : false,
30665     dayPlaceholder : '',
30666     monthPlaceholder : '',
30667     yearPlaceholder : '',
30668     dayFormat : 'd',
30669     monthFormat : 'm',
30670     yearFormat : 'Y',
30671     isFormField : true,
30672     labellg : 0,
30673     labelmd : 0,
30674     labelsm : 0,
30675     labelxs : 0,
30676     
30677     getAutoCreate : function()
30678     {
30679         var cfg = {
30680             tag : 'div',
30681             cls : 'row roo-date-split-field-group',
30682             cn : [
30683                 {
30684                     tag : 'input',
30685                     type : 'hidden',
30686                     cls : 'form-hidden-field roo-date-split-field-group-value',
30687                     name : this.name
30688                 }
30689             ]
30690         };
30691         
30692         var labelCls = 'col-md-12';
30693         var contentCls = 'col-md-4';
30694         
30695         if(this.fieldLabel){
30696             
30697             var label = {
30698                 tag : 'div',
30699                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30700                 cn : [
30701                     {
30702                         tag : 'label',
30703                         html : this.fieldLabel
30704                     }
30705                 ]
30706             };
30707             
30708             if(this.labelAlign == 'left'){
30709             
30710                 if(this.labelWidth > 12){
30711                     label.style = "width: " + this.labelWidth + 'px';
30712                 }
30713
30714                 if(this.labelWidth < 13 && this.labelmd == 0){
30715                     this.labelmd = this.labelWidth;
30716                 }
30717
30718                 if(this.labellg > 0){
30719                     labelCls = ' col-lg-' + this.labellg;
30720                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30721                 }
30722
30723                 if(this.labelmd > 0){
30724                     labelCls = ' col-md-' + this.labelmd;
30725                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30726                 }
30727
30728                 if(this.labelsm > 0){
30729                     labelCls = ' col-sm-' + this.labelsm;
30730                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30731                 }
30732
30733                 if(this.labelxs > 0){
30734                     labelCls = ' col-xs-' + this.labelxs;
30735                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30736                 }
30737             }
30738             
30739             label.cls += ' ' + labelCls;
30740             
30741             cfg.cn.push(label);
30742         }
30743         
30744         Roo.each(['day', 'month', 'year'], function(t){
30745             cfg.cn.push({
30746                 tag : 'div',
30747                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30748             });
30749         }, this);
30750         
30751         return cfg;
30752     },
30753     
30754     inputEl: function ()
30755     {
30756         return this.el.select('.roo-date-split-field-group-value', true).first();
30757     },
30758     
30759     onRender : function(ct, position) 
30760     {
30761         var _this = this;
30762         
30763         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30764         
30765         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30766         
30767         this.dayField = new Roo.bootstrap.ComboBox({
30768             allowBlank : this.dayAllowBlank,
30769             alwaysQuery : true,
30770             displayField : 'value',
30771             editable : false,
30772             fieldLabel : '',
30773             forceSelection : true,
30774             mode : 'local',
30775             placeholder : this.dayPlaceholder,
30776             selectOnFocus : true,
30777             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30778             triggerAction : 'all',
30779             typeAhead : true,
30780             valueField : 'value',
30781             store : new Roo.data.SimpleStore({
30782                 data : (function() {    
30783                     var days = [];
30784                     _this.fireEvent('days', _this, days);
30785                     return days;
30786                 })(),
30787                 fields : [ 'value' ]
30788             }),
30789             listeners : {
30790                 select : function (_self, record, index)
30791                 {
30792                     _this.setValue(_this.getValue());
30793                 }
30794             }
30795         });
30796
30797         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30798         
30799         this.monthField = new Roo.bootstrap.MonthField({
30800             after : '<i class=\"fa fa-calendar\"></i>',
30801             allowBlank : this.monthAllowBlank,
30802             placeholder : this.monthPlaceholder,
30803             readOnly : true,
30804             listeners : {
30805                 render : function (_self)
30806                 {
30807                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30808                         e.preventDefault();
30809                         _self.focus();
30810                     });
30811                 },
30812                 select : function (_self, oldvalue, newvalue)
30813                 {
30814                     _this.setValue(_this.getValue());
30815                 }
30816             }
30817         });
30818         
30819         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30820         
30821         this.yearField = new Roo.bootstrap.ComboBox({
30822             allowBlank : this.yearAllowBlank,
30823             alwaysQuery : true,
30824             displayField : 'value',
30825             editable : false,
30826             fieldLabel : '',
30827             forceSelection : true,
30828             mode : 'local',
30829             placeholder : this.yearPlaceholder,
30830             selectOnFocus : true,
30831             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30832             triggerAction : 'all',
30833             typeAhead : true,
30834             valueField : 'value',
30835             store : new Roo.data.SimpleStore({
30836                 data : (function() {
30837                     var years = [];
30838                     _this.fireEvent('years', _this, years);
30839                     return years;
30840                 })(),
30841                 fields : [ 'value' ]
30842             }),
30843             listeners : {
30844                 select : function (_self, record, index)
30845                 {
30846                     _this.setValue(_this.getValue());
30847                 }
30848             }
30849         });
30850
30851         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30852     },
30853     
30854     setValue : function(v, format)
30855     {
30856         this.inputEl.dom.value = v;
30857         
30858         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30859         
30860         var d = Date.parseDate(v, f);
30861         
30862         if(!d){
30863             this.validate();
30864             return;
30865         }
30866         
30867         this.setDay(d.format(this.dayFormat));
30868         this.setMonth(d.format(this.monthFormat));
30869         this.setYear(d.format(this.yearFormat));
30870         
30871         this.validate();
30872         
30873         return;
30874     },
30875     
30876     setDay : function(v)
30877     {
30878         this.dayField.setValue(v);
30879         this.inputEl.dom.value = this.getValue();
30880         this.validate();
30881         return;
30882     },
30883     
30884     setMonth : function(v)
30885     {
30886         this.monthField.setValue(v, true);
30887         this.inputEl.dom.value = this.getValue();
30888         this.validate();
30889         return;
30890     },
30891     
30892     setYear : function(v)
30893     {
30894         this.yearField.setValue(v);
30895         this.inputEl.dom.value = this.getValue();
30896         this.validate();
30897         return;
30898     },
30899     
30900     getDay : function()
30901     {
30902         return this.dayField.getValue();
30903     },
30904     
30905     getMonth : function()
30906     {
30907         return this.monthField.getValue();
30908     },
30909     
30910     getYear : function()
30911     {
30912         return this.yearField.getValue();
30913     },
30914     
30915     getValue : function()
30916     {
30917         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30918         
30919         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30920         
30921         return date;
30922     },
30923     
30924     reset : function()
30925     {
30926         this.setDay('');
30927         this.setMonth('');
30928         this.setYear('');
30929         this.inputEl.dom.value = '';
30930         this.validate();
30931         return;
30932     },
30933     
30934     validate : function()
30935     {
30936         var d = this.dayField.validate();
30937         var m = this.monthField.validate();
30938         var y = this.yearField.validate();
30939         
30940         var valid = true;
30941         
30942         if(
30943                 (!this.dayAllowBlank && !d) ||
30944                 (!this.monthAllowBlank && !m) ||
30945                 (!this.yearAllowBlank && !y)
30946         ){
30947             valid = false;
30948         }
30949         
30950         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30951             return valid;
30952         }
30953         
30954         if(valid){
30955             this.markValid();
30956             return valid;
30957         }
30958         
30959         this.markInvalid();
30960         
30961         return valid;
30962     },
30963     
30964     markValid : function()
30965     {
30966         
30967         var label = this.el.select('label', true).first();
30968         var icon = this.el.select('i.fa-star', true).first();
30969
30970         if(label && icon){
30971             icon.remove();
30972         }
30973         
30974         this.fireEvent('valid', this);
30975     },
30976     
30977      /**
30978      * Mark this field as invalid
30979      * @param {String} msg The validation message
30980      */
30981     markInvalid : function(msg)
30982     {
30983         
30984         var label = this.el.select('label', true).first();
30985         var icon = this.el.select('i.fa-star', true).first();
30986
30987         if(label && !icon){
30988             this.el.select('.roo-date-split-field-label', true).createChild({
30989                 tag : 'i',
30990                 cls : 'text-danger fa fa-lg fa-star',
30991                 tooltip : 'This field is required',
30992                 style : 'margin-right:5px;'
30993             }, label, true);
30994         }
30995         
30996         this.fireEvent('invalid', this, msg);
30997     },
30998     
30999     clearInvalid : function()
31000     {
31001         var label = this.el.select('label', true).first();
31002         var icon = this.el.select('i.fa-star', true).first();
31003
31004         if(label && icon){
31005             icon.remove();
31006         }
31007         
31008         this.fireEvent('valid', this);
31009     },
31010     
31011     getName: function()
31012     {
31013         return this.name;
31014     }
31015     
31016 });
31017
31018  /**
31019  *
31020  * This is based on 
31021  * http://masonry.desandro.com
31022  *
31023  * The idea is to render all the bricks based on vertical width...
31024  *
31025  * The original code extends 'outlayer' - we might need to use that....
31026  * 
31027  */
31028
31029
31030 /**
31031  * @class Roo.bootstrap.LayoutMasonry
31032  * @extends Roo.bootstrap.Component
31033  * Bootstrap Layout Masonry class
31034  * 
31035  * @constructor
31036  * Create a new Element
31037  * @param {Object} config The config object
31038  */
31039
31040 Roo.bootstrap.LayoutMasonry = function(config){
31041     
31042     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31043     
31044     this.bricks = [];
31045     
31046     Roo.bootstrap.LayoutMasonry.register(this);
31047     
31048     this.addEvents({
31049         // raw events
31050         /**
31051          * @event layout
31052          * Fire after layout the items
31053          * @param {Roo.bootstrap.LayoutMasonry} this
31054          * @param {Roo.EventObject} e
31055          */
31056         "layout" : true
31057     });
31058     
31059 };
31060
31061 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31062     
31063     /**
31064      * @cfg {Boolean} isLayoutInstant = no animation?
31065      */   
31066     isLayoutInstant : false, // needed?
31067    
31068     /**
31069      * @cfg {Number} boxWidth  width of the columns
31070      */   
31071     boxWidth : 450,
31072     
31073       /**
31074      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31075      */   
31076     boxHeight : 0,
31077     
31078     /**
31079      * @cfg {Number} padWidth padding below box..
31080      */   
31081     padWidth : 10, 
31082     
31083     /**
31084      * @cfg {Number} gutter gutter width..
31085      */   
31086     gutter : 10,
31087     
31088      /**
31089      * @cfg {Number} maxCols maximum number of columns
31090      */   
31091     
31092     maxCols: 0,
31093     
31094     /**
31095      * @cfg {Boolean} isAutoInitial defalut true
31096      */   
31097     isAutoInitial : true, 
31098     
31099     containerWidth: 0,
31100     
31101     /**
31102      * @cfg {Boolean} isHorizontal defalut false
31103      */   
31104     isHorizontal : false, 
31105
31106     currentSize : null,
31107     
31108     tag: 'div',
31109     
31110     cls: '',
31111     
31112     bricks: null, //CompositeElement
31113     
31114     cols : 1,
31115     
31116     _isLayoutInited : false,
31117     
31118 //    isAlternative : false, // only use for vertical layout...
31119     
31120     /**
31121      * @cfg {Number} alternativePadWidth padding below box..
31122      */   
31123     alternativePadWidth : 50,
31124     
31125     selectedBrick : [],
31126     
31127     getAutoCreate : function(){
31128         
31129         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31130         
31131         var cfg = {
31132             tag: this.tag,
31133             cls: 'blog-masonary-wrapper ' + this.cls,
31134             cn : {
31135                 cls : 'mas-boxes masonary'
31136             }
31137         };
31138         
31139         return cfg;
31140     },
31141     
31142     getChildContainer: function( )
31143     {
31144         if (this.boxesEl) {
31145             return this.boxesEl;
31146         }
31147         
31148         this.boxesEl = this.el.select('.mas-boxes').first();
31149         
31150         return this.boxesEl;
31151     },
31152     
31153     
31154     initEvents : function()
31155     {
31156         var _this = this;
31157         
31158         if(this.isAutoInitial){
31159             Roo.log('hook children rendered');
31160             this.on('childrenrendered', function() {
31161                 Roo.log('children rendered');
31162                 _this.initial();
31163             } ,this);
31164         }
31165     },
31166     
31167     initial : function()
31168     {
31169         this.selectedBrick = [];
31170         
31171         this.currentSize = this.el.getBox(true);
31172         
31173         Roo.EventManager.onWindowResize(this.resize, this); 
31174
31175         if(!this.isAutoInitial){
31176             this.layout();
31177             return;
31178         }
31179         
31180         this.layout();
31181         
31182         return;
31183         //this.layout.defer(500,this);
31184         
31185     },
31186     
31187     resize : function()
31188     {
31189         var cs = this.el.getBox(true);
31190         
31191         if (
31192                 this.currentSize.width == cs.width && 
31193                 this.currentSize.x == cs.x && 
31194                 this.currentSize.height == cs.height && 
31195                 this.currentSize.y == cs.y 
31196         ) {
31197             Roo.log("no change in with or X or Y");
31198             return;
31199         }
31200         
31201         this.currentSize = cs;
31202         
31203         this.layout();
31204         
31205     },
31206     
31207     layout : function()
31208     {   
31209         this._resetLayout();
31210         
31211         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31212         
31213         this.layoutItems( isInstant );
31214       
31215         this._isLayoutInited = true;
31216         
31217         this.fireEvent('layout', this);
31218         
31219     },
31220     
31221     _resetLayout : function()
31222     {
31223         if(this.isHorizontal){
31224             this.horizontalMeasureColumns();
31225             return;
31226         }
31227         
31228         this.verticalMeasureColumns();
31229         
31230     },
31231     
31232     verticalMeasureColumns : function()
31233     {
31234         this.getContainerWidth();
31235         
31236 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31237 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31238 //            return;
31239 //        }
31240         
31241         var boxWidth = this.boxWidth + this.padWidth;
31242         
31243         if(this.containerWidth < this.boxWidth){
31244             boxWidth = this.containerWidth
31245         }
31246         
31247         var containerWidth = this.containerWidth;
31248         
31249         var cols = Math.floor(containerWidth / boxWidth);
31250         
31251         this.cols = Math.max( cols, 1 );
31252         
31253         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31254         
31255         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31256         
31257         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31258         
31259         this.colWidth = boxWidth + avail - this.padWidth;
31260         
31261         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31262         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31263     },
31264     
31265     horizontalMeasureColumns : function()
31266     {
31267         this.getContainerWidth();
31268         
31269         var boxWidth = this.boxWidth;
31270         
31271         if(this.containerWidth < boxWidth){
31272             boxWidth = this.containerWidth;
31273         }
31274         
31275         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31276         
31277         this.el.setHeight(boxWidth);
31278         
31279     },
31280     
31281     getContainerWidth : function()
31282     {
31283         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31284     },
31285     
31286     layoutItems : function( isInstant )
31287     {
31288         Roo.log(this.bricks);
31289         
31290         var items = Roo.apply([], this.bricks);
31291         
31292         if(this.isHorizontal){
31293             this._horizontalLayoutItems( items , isInstant );
31294             return;
31295         }
31296         
31297 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31298 //            this._verticalAlternativeLayoutItems( items , isInstant );
31299 //            return;
31300 //        }
31301         
31302         this._verticalLayoutItems( items , isInstant );
31303         
31304     },
31305     
31306     _verticalLayoutItems : function ( items , isInstant)
31307     {
31308         if ( !items || !items.length ) {
31309             return;
31310         }
31311         
31312         var standard = [
31313             ['xs', 'xs', 'xs', 'tall'],
31314             ['xs', 'xs', 'tall'],
31315             ['xs', 'xs', 'sm'],
31316             ['xs', 'xs', 'xs'],
31317             ['xs', 'tall'],
31318             ['xs', 'sm'],
31319             ['xs', 'xs'],
31320             ['xs'],
31321             
31322             ['sm', 'xs', 'xs'],
31323             ['sm', 'xs'],
31324             ['sm'],
31325             
31326             ['tall', 'xs', 'xs', 'xs'],
31327             ['tall', 'xs', 'xs'],
31328             ['tall', 'xs'],
31329             ['tall']
31330             
31331         ];
31332         
31333         var queue = [];
31334         
31335         var boxes = [];
31336         
31337         var box = [];
31338         
31339         Roo.each(items, function(item, k){
31340             
31341             switch (item.size) {
31342                 // these layouts take up a full box,
31343                 case 'md' :
31344                 case 'md-left' :
31345                 case 'md-right' :
31346                 case 'wide' :
31347                     
31348                     if(box.length){
31349                         boxes.push(box);
31350                         box = [];
31351                     }
31352                     
31353                     boxes.push([item]);
31354                     
31355                     break;
31356                     
31357                 case 'xs' :
31358                 case 'sm' :
31359                 case 'tall' :
31360                     
31361                     box.push(item);
31362                     
31363                     break;
31364                 default :
31365                     break;
31366                     
31367             }
31368             
31369         }, this);
31370         
31371         if(box.length){
31372             boxes.push(box);
31373             box = [];
31374         }
31375         
31376         var filterPattern = function(box, length)
31377         {
31378             if(!box.length){
31379                 return;
31380             }
31381             
31382             var match = false;
31383             
31384             var pattern = box.slice(0, length);
31385             
31386             var format = [];
31387             
31388             Roo.each(pattern, function(i){
31389                 format.push(i.size);
31390             }, this);
31391             
31392             Roo.each(standard, function(s){
31393                 
31394                 if(String(s) != String(format)){
31395                     return;
31396                 }
31397                 
31398                 match = true;
31399                 return false;
31400                 
31401             }, this);
31402             
31403             if(!match && length == 1){
31404                 return;
31405             }
31406             
31407             if(!match){
31408                 filterPattern(box, length - 1);
31409                 return;
31410             }
31411                 
31412             queue.push(pattern);
31413
31414             box = box.slice(length, box.length);
31415
31416             filterPattern(box, 4);
31417
31418             return;
31419             
31420         }
31421         
31422         Roo.each(boxes, function(box, k){
31423             
31424             if(!box.length){
31425                 return;
31426             }
31427             
31428             if(box.length == 1){
31429                 queue.push(box);
31430                 return;
31431             }
31432             
31433             filterPattern(box, 4);
31434             
31435         }, this);
31436         
31437         this._processVerticalLayoutQueue( queue, isInstant );
31438         
31439     },
31440     
31441 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31442 //    {
31443 //        if ( !items || !items.length ) {
31444 //            return;
31445 //        }
31446 //
31447 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31448 //        
31449 //    },
31450     
31451     _horizontalLayoutItems : function ( items , isInstant)
31452     {
31453         if ( !items || !items.length || items.length < 3) {
31454             return;
31455         }
31456         
31457         items.reverse();
31458         
31459         var eItems = items.slice(0, 3);
31460         
31461         items = items.slice(3, items.length);
31462         
31463         var standard = [
31464             ['xs', 'xs', 'xs', 'wide'],
31465             ['xs', 'xs', 'wide'],
31466             ['xs', 'xs', 'sm'],
31467             ['xs', 'xs', 'xs'],
31468             ['xs', 'wide'],
31469             ['xs', 'sm'],
31470             ['xs', 'xs'],
31471             ['xs'],
31472             
31473             ['sm', 'xs', 'xs'],
31474             ['sm', 'xs'],
31475             ['sm'],
31476             
31477             ['wide', 'xs', 'xs', 'xs'],
31478             ['wide', 'xs', 'xs'],
31479             ['wide', 'xs'],
31480             ['wide'],
31481             
31482             ['wide-thin']
31483         ];
31484         
31485         var queue = [];
31486         
31487         var boxes = [];
31488         
31489         var box = [];
31490         
31491         Roo.each(items, function(item, k){
31492             
31493             switch (item.size) {
31494                 case 'md' :
31495                 case 'md-left' :
31496                 case 'md-right' :
31497                 case 'tall' :
31498                     
31499                     if(box.length){
31500                         boxes.push(box);
31501                         box = [];
31502                     }
31503                     
31504                     boxes.push([item]);
31505                     
31506                     break;
31507                     
31508                 case 'xs' :
31509                 case 'sm' :
31510                 case 'wide' :
31511                 case 'wide-thin' :
31512                     
31513                     box.push(item);
31514                     
31515                     break;
31516                 default :
31517                     break;
31518                     
31519             }
31520             
31521         }, this);
31522         
31523         if(box.length){
31524             boxes.push(box);
31525             box = [];
31526         }
31527         
31528         var filterPattern = function(box, length)
31529         {
31530             if(!box.length){
31531                 return;
31532             }
31533             
31534             var match = false;
31535             
31536             var pattern = box.slice(0, length);
31537             
31538             var format = [];
31539             
31540             Roo.each(pattern, function(i){
31541                 format.push(i.size);
31542             }, this);
31543             
31544             Roo.each(standard, function(s){
31545                 
31546                 if(String(s) != String(format)){
31547                     return;
31548                 }
31549                 
31550                 match = true;
31551                 return false;
31552                 
31553             }, this);
31554             
31555             if(!match && length == 1){
31556                 return;
31557             }
31558             
31559             if(!match){
31560                 filterPattern(box, length - 1);
31561                 return;
31562             }
31563                 
31564             queue.push(pattern);
31565
31566             box = box.slice(length, box.length);
31567
31568             filterPattern(box, 4);
31569
31570             return;
31571             
31572         }
31573         
31574         Roo.each(boxes, function(box, k){
31575             
31576             if(!box.length){
31577                 return;
31578             }
31579             
31580             if(box.length == 1){
31581                 queue.push(box);
31582                 return;
31583             }
31584             
31585             filterPattern(box, 4);
31586             
31587         }, this);
31588         
31589         
31590         var prune = [];
31591         
31592         var pos = this.el.getBox(true);
31593         
31594         var minX = pos.x;
31595         
31596         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31597         
31598         var hit_end = false;
31599         
31600         Roo.each(queue, function(box){
31601             
31602             if(hit_end){
31603                 
31604                 Roo.each(box, function(b){
31605                 
31606                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31607                     b.el.hide();
31608
31609                 }, this);
31610
31611                 return;
31612             }
31613             
31614             var mx = 0;
31615             
31616             Roo.each(box, function(b){
31617                 
31618                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31619                 b.el.show();
31620
31621                 mx = Math.max(mx, b.x);
31622                 
31623             }, this);
31624             
31625             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31626             
31627             if(maxX < minX){
31628                 
31629                 Roo.each(box, function(b){
31630                 
31631                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31632                     b.el.hide();
31633                     
31634                 }, this);
31635                 
31636                 hit_end = true;
31637                 
31638                 return;
31639             }
31640             
31641             prune.push(box);
31642             
31643         }, this);
31644         
31645         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31646     },
31647     
31648     /** Sets position of item in DOM
31649     * @param {Element} item
31650     * @param {Number} x - horizontal position
31651     * @param {Number} y - vertical position
31652     * @param {Boolean} isInstant - disables transitions
31653     */
31654     _processVerticalLayoutQueue : function( queue, isInstant )
31655     {
31656         var pos = this.el.getBox(true);
31657         var x = pos.x;
31658         var y = pos.y;
31659         var maxY = [];
31660         
31661         for (var i = 0; i < this.cols; i++){
31662             maxY[i] = pos.y;
31663         }
31664         
31665         Roo.each(queue, function(box, k){
31666             
31667             var col = k % this.cols;
31668             
31669             Roo.each(box, function(b,kk){
31670                 
31671                 b.el.position('absolute');
31672                 
31673                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31674                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31675                 
31676                 if(b.size == 'md-left' || b.size == 'md-right'){
31677                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31678                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31679                 }
31680                 
31681                 b.el.setWidth(width);
31682                 b.el.setHeight(height);
31683                 // iframe?
31684                 b.el.select('iframe',true).setSize(width,height);
31685                 
31686             }, this);
31687             
31688             for (var i = 0; i < this.cols; i++){
31689                 
31690                 if(maxY[i] < maxY[col]){
31691                     col = i;
31692                     continue;
31693                 }
31694                 
31695                 col = Math.min(col, i);
31696                 
31697             }
31698             
31699             x = pos.x + col * (this.colWidth + this.padWidth);
31700             
31701             y = maxY[col];
31702             
31703             var positions = [];
31704             
31705             switch (box.length){
31706                 case 1 :
31707                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31708                     break;
31709                 case 2 :
31710                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31711                     break;
31712                 case 3 :
31713                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31714                     break;
31715                 case 4 :
31716                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31717                     break;
31718                 default :
31719                     break;
31720             }
31721             
31722             Roo.each(box, function(b,kk){
31723                 
31724                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31725                 
31726                 var sz = b.el.getSize();
31727                 
31728                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31729                 
31730             }, this);
31731             
31732         }, this);
31733         
31734         var mY = 0;
31735         
31736         for (var i = 0; i < this.cols; i++){
31737             mY = Math.max(mY, maxY[i]);
31738         }
31739         
31740         this.el.setHeight(mY - pos.y);
31741         
31742     },
31743     
31744 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31745 //    {
31746 //        var pos = this.el.getBox(true);
31747 //        var x = pos.x;
31748 //        var y = pos.y;
31749 //        var maxX = pos.right;
31750 //        
31751 //        var maxHeight = 0;
31752 //        
31753 //        Roo.each(items, function(item, k){
31754 //            
31755 //            var c = k % 2;
31756 //            
31757 //            item.el.position('absolute');
31758 //                
31759 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31760 //
31761 //            item.el.setWidth(width);
31762 //
31763 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31764 //
31765 //            item.el.setHeight(height);
31766 //            
31767 //            if(c == 0){
31768 //                item.el.setXY([x, y], isInstant ? false : true);
31769 //            } else {
31770 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31771 //            }
31772 //            
31773 //            y = y + height + this.alternativePadWidth;
31774 //            
31775 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31776 //            
31777 //        }, this);
31778 //        
31779 //        this.el.setHeight(maxHeight);
31780 //        
31781 //    },
31782     
31783     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31784     {
31785         var pos = this.el.getBox(true);
31786         
31787         var minX = pos.x;
31788         var minY = pos.y;
31789         
31790         var maxX = pos.right;
31791         
31792         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31793         
31794         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31795         
31796         Roo.each(queue, function(box, k){
31797             
31798             Roo.each(box, function(b, kk){
31799                 
31800                 b.el.position('absolute');
31801                 
31802                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31803                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31804                 
31805                 if(b.size == 'md-left' || b.size == 'md-right'){
31806                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31807                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31808                 }
31809                 
31810                 b.el.setWidth(width);
31811                 b.el.setHeight(height);
31812                 
31813             }, this);
31814             
31815             if(!box.length){
31816                 return;
31817             }
31818             
31819             var positions = [];
31820             
31821             switch (box.length){
31822                 case 1 :
31823                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31824                     break;
31825                 case 2 :
31826                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31827                     break;
31828                 case 3 :
31829                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31830                     break;
31831                 case 4 :
31832                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31833                     break;
31834                 default :
31835                     break;
31836             }
31837             
31838             Roo.each(box, function(b,kk){
31839                 
31840                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31841                 
31842                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31843                 
31844             }, this);
31845             
31846         }, this);
31847         
31848     },
31849     
31850     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31851     {
31852         Roo.each(eItems, function(b,k){
31853             
31854             b.size = (k == 0) ? 'sm' : 'xs';
31855             b.x = (k == 0) ? 2 : 1;
31856             b.y = (k == 0) ? 2 : 1;
31857             
31858             b.el.position('absolute');
31859             
31860             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31861                 
31862             b.el.setWidth(width);
31863             
31864             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31865             
31866             b.el.setHeight(height);
31867             
31868         }, this);
31869
31870         var positions = [];
31871         
31872         positions.push({
31873             x : maxX - this.unitWidth * 2 - this.gutter,
31874             y : minY
31875         });
31876         
31877         positions.push({
31878             x : maxX - this.unitWidth,
31879             y : minY + (this.unitWidth + this.gutter) * 2
31880         });
31881         
31882         positions.push({
31883             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31884             y : minY
31885         });
31886         
31887         Roo.each(eItems, function(b,k){
31888             
31889             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31890
31891         }, this);
31892         
31893     },
31894     
31895     getVerticalOneBoxColPositions : function(x, y, box)
31896     {
31897         var pos = [];
31898         
31899         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31900         
31901         if(box[0].size == 'md-left'){
31902             rand = 0;
31903         }
31904         
31905         if(box[0].size == 'md-right'){
31906             rand = 1;
31907         }
31908         
31909         pos.push({
31910             x : x + (this.unitWidth + this.gutter) * rand,
31911             y : y
31912         });
31913         
31914         return pos;
31915     },
31916     
31917     getVerticalTwoBoxColPositions : function(x, y, box)
31918     {
31919         var pos = [];
31920         
31921         if(box[0].size == 'xs'){
31922             
31923             pos.push({
31924                 x : x,
31925                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31926             });
31927
31928             pos.push({
31929                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31930                 y : y
31931             });
31932             
31933             return pos;
31934             
31935         }
31936         
31937         pos.push({
31938             x : x,
31939             y : y
31940         });
31941
31942         pos.push({
31943             x : x + (this.unitWidth + this.gutter) * 2,
31944             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31945         });
31946         
31947         return pos;
31948         
31949     },
31950     
31951     getVerticalThreeBoxColPositions : function(x, y, box)
31952     {
31953         var pos = [];
31954         
31955         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31956             
31957             pos.push({
31958                 x : x,
31959                 y : y
31960             });
31961
31962             pos.push({
31963                 x : x + (this.unitWidth + this.gutter) * 1,
31964                 y : y
31965             });
31966             
31967             pos.push({
31968                 x : x + (this.unitWidth + this.gutter) * 2,
31969                 y : y
31970             });
31971             
31972             return pos;
31973             
31974         }
31975         
31976         if(box[0].size == 'xs' && box[1].size == 'xs'){
31977             
31978             pos.push({
31979                 x : x,
31980                 y : y
31981             });
31982
31983             pos.push({
31984                 x : x,
31985                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31986             });
31987             
31988             pos.push({
31989                 x : x + (this.unitWidth + this.gutter) * 1,
31990                 y : y
31991             });
31992             
31993             return pos;
31994             
31995         }
31996         
31997         pos.push({
31998             x : x,
31999             y : y
32000         });
32001
32002         pos.push({
32003             x : x + (this.unitWidth + this.gutter) * 2,
32004             y : y
32005         });
32006
32007         pos.push({
32008             x : x + (this.unitWidth + this.gutter) * 2,
32009             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32010         });
32011             
32012         return pos;
32013         
32014     },
32015     
32016     getVerticalFourBoxColPositions : function(x, y, box)
32017     {
32018         var pos = [];
32019         
32020         if(box[0].size == 'xs'){
32021             
32022             pos.push({
32023                 x : x,
32024                 y : y
32025             });
32026
32027             pos.push({
32028                 x : x,
32029                 y : y + (this.unitHeight + this.gutter) * 1
32030             });
32031             
32032             pos.push({
32033                 x : x,
32034                 y : y + (this.unitHeight + this.gutter) * 2
32035             });
32036             
32037             pos.push({
32038                 x : x + (this.unitWidth + this.gutter) * 1,
32039                 y : y
32040             });
32041             
32042             return pos;
32043             
32044         }
32045         
32046         pos.push({
32047             x : x,
32048             y : y
32049         });
32050
32051         pos.push({
32052             x : x + (this.unitWidth + this.gutter) * 2,
32053             y : y
32054         });
32055
32056         pos.push({
32057             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32058             y : y + (this.unitHeight + this.gutter) * 1
32059         });
32060
32061         pos.push({
32062             x : x + (this.unitWidth + this.gutter) * 2,
32063             y : y + (this.unitWidth + this.gutter) * 2
32064         });
32065
32066         return pos;
32067         
32068     },
32069     
32070     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32071     {
32072         var pos = [];
32073         
32074         if(box[0].size == 'md-left'){
32075             pos.push({
32076                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32077                 y : minY
32078             });
32079             
32080             return pos;
32081         }
32082         
32083         if(box[0].size == 'md-right'){
32084             pos.push({
32085                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32086                 y : minY + (this.unitWidth + this.gutter) * 1
32087             });
32088             
32089             return pos;
32090         }
32091         
32092         var rand = Math.floor(Math.random() * (4 - box[0].y));
32093         
32094         pos.push({
32095             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32096             y : minY + (this.unitWidth + this.gutter) * rand
32097         });
32098         
32099         return pos;
32100         
32101     },
32102     
32103     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32104     {
32105         var pos = [];
32106         
32107         if(box[0].size == 'xs'){
32108             
32109             pos.push({
32110                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32111                 y : minY
32112             });
32113
32114             pos.push({
32115                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32116                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32117             });
32118             
32119             return pos;
32120             
32121         }
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) * 2
32131         });
32132         
32133         return pos;
32134         
32135     },
32136     
32137     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32138     {
32139         var pos = [];
32140         
32141         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32142             
32143             pos.push({
32144                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32145                 y : minY
32146             });
32147
32148             pos.push({
32149                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32150                 y : minY + (this.unitWidth + this.gutter) * 1
32151             });
32152             
32153             pos.push({
32154                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32155                 y : minY + (this.unitWidth + this.gutter) * 2
32156             });
32157             
32158             return pos;
32159             
32160         }
32161         
32162         if(box[0].size == 'xs' && box[1].size == 'xs'){
32163             
32164             pos.push({
32165                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32166                 y : minY
32167             });
32168
32169             pos.push({
32170                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32171                 y : minY
32172             });
32173             
32174             pos.push({
32175                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32176                 y : minY + (this.unitWidth + this.gutter) * 1
32177             });
32178             
32179             return pos;
32180             
32181         }
32182         
32183         pos.push({
32184             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32185             y : minY
32186         });
32187
32188         pos.push({
32189             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32190             y : minY + (this.unitWidth + this.gutter) * 2
32191         });
32192
32193         pos.push({
32194             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32195             y : minY + (this.unitWidth + this.gutter) * 2
32196         });
32197             
32198         return pos;
32199         
32200     },
32201     
32202     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32203     {
32204         var pos = [];
32205         
32206         if(box[0].size == 'xs'){
32207             
32208             pos.push({
32209                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32210                 y : minY
32211             });
32212
32213             pos.push({
32214                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32215                 y : minY
32216             });
32217             
32218             pos.push({
32219                 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),
32220                 y : minY
32221             });
32222             
32223             pos.push({
32224                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32225                 y : minY + (this.unitWidth + this.gutter) * 1
32226             });
32227             
32228             return pos;
32229             
32230         }
32231         
32232         pos.push({
32233             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32234             y : minY
32235         });
32236         
32237         pos.push({
32238             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32239             y : minY + (this.unitWidth + this.gutter) * 2
32240         });
32241         
32242         pos.push({
32243             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32244             y : minY + (this.unitWidth + this.gutter) * 2
32245         });
32246         
32247         pos.push({
32248             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),
32249             y : minY + (this.unitWidth + this.gutter) * 2
32250         });
32251
32252         return pos;
32253         
32254     },
32255     
32256     /**
32257     * remove a Masonry Brick
32258     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32259     */
32260     removeBrick : function(brick_id)
32261     {
32262         if (!brick_id) {
32263             return;
32264         }
32265         
32266         for (var i = 0; i<this.bricks.length; i++) {
32267             if (this.bricks[i].id == brick_id) {
32268                 this.bricks.splice(i,1);
32269                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32270                 this.initial();
32271             }
32272         }
32273     },
32274     
32275     /**
32276     * adds a Masonry Brick
32277     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32278     */
32279     addBrick : function(cfg)
32280     {
32281         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32282         //this.register(cn);
32283         cn.parentId = this.id;
32284         cn.render(this.el);
32285         return cn;
32286     },
32287     
32288     /**
32289     * register a Masonry Brick
32290     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32291     */
32292     
32293     register : function(brick)
32294     {
32295         this.bricks.push(brick);
32296         brick.masonryId = this.id;
32297     },
32298     
32299     /**
32300     * clear all the Masonry Brick
32301     */
32302     clearAll : function()
32303     {
32304         this.bricks = [];
32305         //this.getChildContainer().dom.innerHTML = "";
32306         this.el.dom.innerHTML = '';
32307     },
32308     
32309     getSelected : function()
32310     {
32311         if (!this.selectedBrick) {
32312             return false;
32313         }
32314         
32315         return this.selectedBrick;
32316     }
32317 });
32318
32319 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32320     
32321     groups: {},
32322      /**
32323     * register a Masonry Layout
32324     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32325     */
32326     
32327     register : function(layout)
32328     {
32329         this.groups[layout.id] = layout;
32330     },
32331     /**
32332     * fetch a  Masonry Layout based on the masonry layout ID
32333     * @param {string} the masonry layout to add
32334     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32335     */
32336     
32337     get: function(layout_id) {
32338         if (typeof(this.groups[layout_id]) == 'undefined') {
32339             return false;
32340         }
32341         return this.groups[layout_id] ;
32342     }
32343     
32344     
32345     
32346 });
32347
32348  
32349
32350  /**
32351  *
32352  * This is based on 
32353  * http://masonry.desandro.com
32354  *
32355  * The idea is to render all the bricks based on vertical width...
32356  *
32357  * The original code extends 'outlayer' - we might need to use that....
32358  * 
32359  */
32360
32361
32362 /**
32363  * @class Roo.bootstrap.LayoutMasonryAuto
32364  * @extends Roo.bootstrap.Component
32365  * Bootstrap Layout Masonry class
32366  * 
32367  * @constructor
32368  * Create a new Element
32369  * @param {Object} config The config object
32370  */
32371
32372 Roo.bootstrap.LayoutMasonryAuto = function(config){
32373     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32374 };
32375
32376 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32377     
32378       /**
32379      * @cfg {Boolean} isFitWidth  - resize the width..
32380      */   
32381     isFitWidth : false,  // options..
32382     /**
32383      * @cfg {Boolean} isOriginLeft = left align?
32384      */   
32385     isOriginLeft : true,
32386     /**
32387      * @cfg {Boolean} isOriginTop = top align?
32388      */   
32389     isOriginTop : false,
32390     /**
32391      * @cfg {Boolean} isLayoutInstant = no animation?
32392      */   
32393     isLayoutInstant : false, // needed?
32394     /**
32395      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32396      */   
32397     isResizingContainer : true,
32398     /**
32399      * @cfg {Number} columnWidth  width of the columns 
32400      */   
32401     
32402     columnWidth : 0,
32403     
32404     /**
32405      * @cfg {Number} maxCols maximum number of columns
32406      */   
32407     
32408     maxCols: 0,
32409     /**
32410      * @cfg {Number} padHeight padding below box..
32411      */   
32412     
32413     padHeight : 10, 
32414     
32415     /**
32416      * @cfg {Boolean} isAutoInitial defalut true
32417      */   
32418     
32419     isAutoInitial : true, 
32420     
32421     // private?
32422     gutter : 0,
32423     
32424     containerWidth: 0,
32425     initialColumnWidth : 0,
32426     currentSize : null,
32427     
32428     colYs : null, // array.
32429     maxY : 0,
32430     padWidth: 10,
32431     
32432     
32433     tag: 'div',
32434     cls: '',
32435     bricks: null, //CompositeElement
32436     cols : 0, // array?
32437     // element : null, // wrapped now this.el
32438     _isLayoutInited : null, 
32439     
32440     
32441     getAutoCreate : function(){
32442         
32443         var cfg = {
32444             tag: this.tag,
32445             cls: 'blog-masonary-wrapper ' + this.cls,
32446             cn : {
32447                 cls : 'mas-boxes masonary'
32448             }
32449         };
32450         
32451         return cfg;
32452     },
32453     
32454     getChildContainer: function( )
32455     {
32456         if (this.boxesEl) {
32457             return this.boxesEl;
32458         }
32459         
32460         this.boxesEl = this.el.select('.mas-boxes').first();
32461         
32462         return this.boxesEl;
32463     },
32464     
32465     
32466     initEvents : function()
32467     {
32468         var _this = this;
32469         
32470         if(this.isAutoInitial){
32471             Roo.log('hook children rendered');
32472             this.on('childrenrendered', function() {
32473                 Roo.log('children rendered');
32474                 _this.initial();
32475             } ,this);
32476         }
32477         
32478     },
32479     
32480     initial : function()
32481     {
32482         this.reloadItems();
32483
32484         this.currentSize = this.el.getBox(true);
32485
32486         /// was window resize... - let's see if this works..
32487         Roo.EventManager.onWindowResize(this.resize, this); 
32488
32489         if(!this.isAutoInitial){
32490             this.layout();
32491             return;
32492         }
32493         
32494         this.layout.defer(500,this);
32495     },
32496     
32497     reloadItems: function()
32498     {
32499         this.bricks = this.el.select('.masonry-brick', true);
32500         
32501         this.bricks.each(function(b) {
32502             //Roo.log(b.getSize());
32503             if (!b.attr('originalwidth')) {
32504                 b.attr('originalwidth',  b.getSize().width);
32505             }
32506             
32507         });
32508         
32509         Roo.log(this.bricks.elements.length);
32510     },
32511     
32512     resize : function()
32513     {
32514         Roo.log('resize');
32515         var cs = this.el.getBox(true);
32516         
32517         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32518             Roo.log("no change in with or X");
32519             return;
32520         }
32521         this.currentSize = cs;
32522         this.layout();
32523     },
32524     
32525     layout : function()
32526     {
32527          Roo.log('layout');
32528         this._resetLayout();
32529         //this._manageStamps();
32530       
32531         // don't animate first layout
32532         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32533         this.layoutItems( isInstant );
32534       
32535         // flag for initalized
32536         this._isLayoutInited = true;
32537     },
32538     
32539     layoutItems : function( isInstant )
32540     {
32541         //var items = this._getItemsForLayout( this.items );
32542         // original code supports filtering layout items.. we just ignore it..
32543         
32544         this._layoutItems( this.bricks , isInstant );
32545       
32546         this._postLayout();
32547     },
32548     _layoutItems : function ( items , isInstant)
32549     {
32550        //this.fireEvent( 'layout', this, items );
32551     
32552
32553         if ( !items || !items.elements.length ) {
32554           // no items, emit event with empty array
32555             return;
32556         }
32557
32558         var queue = [];
32559         items.each(function(item) {
32560             Roo.log("layout item");
32561             Roo.log(item);
32562             // get x/y object from method
32563             var position = this._getItemLayoutPosition( item );
32564             // enqueue
32565             position.item = item;
32566             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32567             queue.push( position );
32568         }, this);
32569       
32570         this._processLayoutQueue( queue );
32571     },
32572     /** Sets position of item in DOM
32573     * @param {Element} item
32574     * @param {Number} x - horizontal position
32575     * @param {Number} y - vertical position
32576     * @param {Boolean} isInstant - disables transitions
32577     */
32578     _processLayoutQueue : function( queue )
32579     {
32580         for ( var i=0, len = queue.length; i < len; i++ ) {
32581             var obj = queue[i];
32582             obj.item.position('absolute');
32583             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32584         }
32585     },
32586       
32587     
32588     /**
32589     * Any logic you want to do after each layout,
32590     * i.e. size the container
32591     */
32592     _postLayout : function()
32593     {
32594         this.resizeContainer();
32595     },
32596     
32597     resizeContainer : function()
32598     {
32599         if ( !this.isResizingContainer ) {
32600             return;
32601         }
32602         var size = this._getContainerSize();
32603         if ( size ) {
32604             this.el.setSize(size.width,size.height);
32605             this.boxesEl.setSize(size.width,size.height);
32606         }
32607     },
32608     
32609     
32610     
32611     _resetLayout : function()
32612     {
32613         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32614         this.colWidth = this.el.getWidth();
32615         //this.gutter = this.el.getWidth(); 
32616         
32617         this.measureColumns();
32618
32619         // reset column Y
32620         var i = this.cols;
32621         this.colYs = [];
32622         while (i--) {
32623             this.colYs.push( 0 );
32624         }
32625     
32626         this.maxY = 0;
32627     },
32628
32629     measureColumns : function()
32630     {
32631         this.getContainerWidth();
32632       // if columnWidth is 0, default to outerWidth of first item
32633         if ( !this.columnWidth ) {
32634             var firstItem = this.bricks.first();
32635             Roo.log(firstItem);
32636             this.columnWidth  = this.containerWidth;
32637             if (firstItem && firstItem.attr('originalwidth') ) {
32638                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32639             }
32640             // columnWidth fall back to item of first element
32641             Roo.log("set column width?");
32642                         this.initialColumnWidth = this.columnWidth  ;
32643
32644             // if first elem has no width, default to size of container
32645             
32646         }
32647         
32648         
32649         if (this.initialColumnWidth) {
32650             this.columnWidth = this.initialColumnWidth;
32651         }
32652         
32653         
32654             
32655         // column width is fixed at the top - however if container width get's smaller we should
32656         // reduce it...
32657         
32658         // this bit calcs how man columns..
32659             
32660         var columnWidth = this.columnWidth += this.gutter;
32661       
32662         // calculate columns
32663         var containerWidth = this.containerWidth + this.gutter;
32664         
32665         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32666         // fix rounding errors, typically with gutters
32667         var excess = columnWidth - containerWidth % columnWidth;
32668         
32669         
32670         // if overshoot is less than a pixel, round up, otherwise floor it
32671         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32672         cols = Math[ mathMethod ]( cols );
32673         this.cols = Math.max( cols, 1 );
32674         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32675         
32676          // padding positioning..
32677         var totalColWidth = this.cols * this.columnWidth;
32678         var padavail = this.containerWidth - totalColWidth;
32679         // so for 2 columns - we need 3 'pads'
32680         
32681         var padNeeded = (1+this.cols) * this.padWidth;
32682         
32683         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32684         
32685         this.columnWidth += padExtra
32686         //this.padWidth = Math.floor(padavail /  ( this.cols));
32687         
32688         // adjust colum width so that padding is fixed??
32689         
32690         // we have 3 columns ... total = width * 3
32691         // we have X left over... that should be used by 
32692         
32693         //if (this.expandC) {
32694             
32695         //}
32696         
32697         
32698         
32699     },
32700     
32701     getContainerWidth : function()
32702     {
32703        /* // container is parent if fit width
32704         var container = this.isFitWidth ? this.element.parentNode : this.element;
32705         // check that this.size and size are there
32706         // IE8 triggers resize on body size change, so they might not be
32707         
32708         var size = getSize( container );  //FIXME
32709         this.containerWidth = size && size.innerWidth; //FIXME
32710         */
32711          
32712         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32713         
32714     },
32715     
32716     _getItemLayoutPosition : function( item )  // what is item?
32717     {
32718         // we resize the item to our columnWidth..
32719       
32720         item.setWidth(this.columnWidth);
32721         item.autoBoxAdjust  = false;
32722         
32723         var sz = item.getSize();
32724  
32725         // how many columns does this brick span
32726         var remainder = this.containerWidth % this.columnWidth;
32727         
32728         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32729         // round if off by 1 pixel, otherwise use ceil
32730         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32731         colSpan = Math.min( colSpan, this.cols );
32732         
32733         // normally this should be '1' as we dont' currently allow multi width columns..
32734         
32735         var colGroup = this._getColGroup( colSpan );
32736         // get the minimum Y value from the columns
32737         var minimumY = Math.min.apply( Math, colGroup );
32738         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32739         
32740         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32741          
32742         // position the brick
32743         var position = {
32744             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32745             y: this.currentSize.y + minimumY + this.padHeight
32746         };
32747         
32748         Roo.log(position);
32749         // apply setHeight to necessary columns
32750         var setHeight = minimumY + sz.height + this.padHeight;
32751         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32752         
32753         var setSpan = this.cols + 1 - colGroup.length;
32754         for ( var i = 0; i < setSpan; i++ ) {
32755           this.colYs[ shortColIndex + i ] = setHeight ;
32756         }
32757       
32758         return position;
32759     },
32760     
32761     /**
32762      * @param {Number} colSpan - number of columns the element spans
32763      * @returns {Array} colGroup
32764      */
32765     _getColGroup : function( colSpan )
32766     {
32767         if ( colSpan < 2 ) {
32768           // if brick spans only one column, use all the column Ys
32769           return this.colYs;
32770         }
32771       
32772         var colGroup = [];
32773         // how many different places could this brick fit horizontally
32774         var groupCount = this.cols + 1 - colSpan;
32775         // for each group potential horizontal position
32776         for ( var i = 0; i < groupCount; i++ ) {
32777           // make an array of colY values for that one group
32778           var groupColYs = this.colYs.slice( i, i + colSpan );
32779           // and get the max value of the array
32780           colGroup[i] = Math.max.apply( Math, groupColYs );
32781         }
32782         return colGroup;
32783     },
32784     /*
32785     _manageStamp : function( stamp )
32786     {
32787         var stampSize =  stamp.getSize();
32788         var offset = stamp.getBox();
32789         // get the columns that this stamp affects
32790         var firstX = this.isOriginLeft ? offset.x : offset.right;
32791         var lastX = firstX + stampSize.width;
32792         var firstCol = Math.floor( firstX / this.columnWidth );
32793         firstCol = Math.max( 0, firstCol );
32794         
32795         var lastCol = Math.floor( lastX / this.columnWidth );
32796         // lastCol should not go over if multiple of columnWidth #425
32797         lastCol -= lastX % this.columnWidth ? 0 : 1;
32798         lastCol = Math.min( this.cols - 1, lastCol );
32799         
32800         // set colYs to bottom of the stamp
32801         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32802             stampSize.height;
32803             
32804         for ( var i = firstCol; i <= lastCol; i++ ) {
32805           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32806         }
32807     },
32808     */
32809     
32810     _getContainerSize : function()
32811     {
32812         this.maxY = Math.max.apply( Math, this.colYs );
32813         var size = {
32814             height: this.maxY
32815         };
32816       
32817         if ( this.isFitWidth ) {
32818             size.width = this._getContainerFitWidth();
32819         }
32820       
32821         return size;
32822     },
32823     
32824     _getContainerFitWidth : function()
32825     {
32826         var unusedCols = 0;
32827         // count unused columns
32828         var i = this.cols;
32829         while ( --i ) {
32830           if ( this.colYs[i] !== 0 ) {
32831             break;
32832           }
32833           unusedCols++;
32834         }
32835         // fit container to columns that have been used
32836         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32837     },
32838     
32839     needsResizeLayout : function()
32840     {
32841         var previousWidth = this.containerWidth;
32842         this.getContainerWidth();
32843         return previousWidth !== this.containerWidth;
32844     }
32845  
32846 });
32847
32848  
32849
32850  /*
32851  * - LGPL
32852  *
32853  * element
32854  * 
32855  */
32856
32857 /**
32858  * @class Roo.bootstrap.MasonryBrick
32859  * @extends Roo.bootstrap.Component
32860  * Bootstrap MasonryBrick class
32861  * 
32862  * @constructor
32863  * Create a new MasonryBrick
32864  * @param {Object} config The config object
32865  */
32866
32867 Roo.bootstrap.MasonryBrick = function(config){
32868     
32869     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32870     
32871     Roo.bootstrap.MasonryBrick.register(this);
32872     
32873     this.addEvents({
32874         // raw events
32875         /**
32876          * @event click
32877          * When a MasonryBrick is clcik
32878          * @param {Roo.bootstrap.MasonryBrick} this
32879          * @param {Roo.EventObject} e
32880          */
32881         "click" : true
32882     });
32883 };
32884
32885 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32886     
32887     /**
32888      * @cfg {String} title
32889      */   
32890     title : '',
32891     /**
32892      * @cfg {String} html
32893      */   
32894     html : '',
32895     /**
32896      * @cfg {String} bgimage
32897      */   
32898     bgimage : '',
32899     /**
32900      * @cfg {String} videourl
32901      */   
32902     videourl : '',
32903     /**
32904      * @cfg {String} cls
32905      */   
32906     cls : '',
32907     /**
32908      * @cfg {String} href
32909      */   
32910     href : '',
32911     /**
32912      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32913      */   
32914     size : 'xs',
32915     
32916     /**
32917      * @cfg {String} placetitle (center|bottom)
32918      */   
32919     placetitle : '',
32920     
32921     /**
32922      * @cfg {Boolean} isFitContainer defalut true
32923      */   
32924     isFitContainer : true, 
32925     
32926     /**
32927      * @cfg {Boolean} preventDefault defalut false
32928      */   
32929     preventDefault : false, 
32930     
32931     /**
32932      * @cfg {Boolean} inverse defalut false
32933      */   
32934     maskInverse : false, 
32935     
32936     getAutoCreate : function()
32937     {
32938         if(!this.isFitContainer){
32939             return this.getSplitAutoCreate();
32940         }
32941         
32942         var cls = 'masonry-brick masonry-brick-full';
32943         
32944         if(this.href.length){
32945             cls += ' masonry-brick-link';
32946         }
32947         
32948         if(this.bgimage.length){
32949             cls += ' masonry-brick-image';
32950         }
32951         
32952         if(this.maskInverse){
32953             cls += ' mask-inverse';
32954         }
32955         
32956         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32957             cls += ' enable-mask';
32958         }
32959         
32960         if(this.size){
32961             cls += ' masonry-' + this.size + '-brick';
32962         }
32963         
32964         if(this.placetitle.length){
32965             
32966             switch (this.placetitle) {
32967                 case 'center' :
32968                     cls += ' masonry-center-title';
32969                     break;
32970                 case 'bottom' :
32971                     cls += ' masonry-bottom-title';
32972                     break;
32973                 default:
32974                     break;
32975             }
32976             
32977         } else {
32978             if(!this.html.length && !this.bgimage.length){
32979                 cls += ' masonry-center-title';
32980             }
32981
32982             if(!this.html.length && this.bgimage.length){
32983                 cls += ' masonry-bottom-title';
32984             }
32985         }
32986         
32987         if(this.cls){
32988             cls += ' ' + this.cls;
32989         }
32990         
32991         var cfg = {
32992             tag: (this.href.length) ? 'a' : 'div',
32993             cls: cls,
32994             cn: [
32995                 {
32996                     tag: 'div',
32997                     cls: 'masonry-brick-mask'
32998                 },
32999                 {
33000                     tag: 'div',
33001                     cls: 'masonry-brick-paragraph',
33002                     cn: []
33003                 }
33004             ]
33005         };
33006         
33007         if(this.href.length){
33008             cfg.href = this.href;
33009         }
33010         
33011         var cn = cfg.cn[1].cn;
33012         
33013         if(this.title.length){
33014             cn.push({
33015                 tag: 'h4',
33016                 cls: 'masonry-brick-title',
33017                 html: this.title
33018             });
33019         }
33020         
33021         if(this.html.length){
33022             cn.push({
33023                 tag: 'p',
33024                 cls: 'masonry-brick-text',
33025                 html: this.html
33026             });
33027         }
33028         
33029         if (!this.title.length && !this.html.length) {
33030             cfg.cn[1].cls += ' hide';
33031         }
33032         
33033         if(this.bgimage.length){
33034             cfg.cn.push({
33035                 tag: 'img',
33036                 cls: 'masonry-brick-image-view',
33037                 src: this.bgimage
33038             });
33039         }
33040         
33041         if(this.videourl.length){
33042             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33043             // youtube support only?
33044             cfg.cn.push({
33045                 tag: 'iframe',
33046                 cls: 'masonry-brick-image-view',
33047                 src: vurl,
33048                 frameborder : 0,
33049                 allowfullscreen : true
33050             });
33051         }
33052         
33053         return cfg;
33054         
33055     },
33056     
33057     getSplitAutoCreate : function()
33058     {
33059         var cls = 'masonry-brick masonry-brick-split';
33060         
33061         if(this.href.length){
33062             cls += ' masonry-brick-link';
33063         }
33064         
33065         if(this.bgimage.length){
33066             cls += ' masonry-brick-image';
33067         }
33068         
33069         if(this.size){
33070             cls += ' masonry-' + this.size + '-brick';
33071         }
33072         
33073         switch (this.placetitle) {
33074             case 'center' :
33075                 cls += ' masonry-center-title';
33076                 break;
33077             case 'bottom' :
33078                 cls += ' masonry-bottom-title';
33079                 break;
33080             default:
33081                 if(!this.bgimage.length){
33082                     cls += ' masonry-center-title';
33083                 }
33084
33085                 if(this.bgimage.length){
33086                     cls += ' masonry-bottom-title';
33087                 }
33088                 break;
33089         }
33090         
33091         if(this.cls){
33092             cls += ' ' + this.cls;
33093         }
33094         
33095         var cfg = {
33096             tag: (this.href.length) ? 'a' : 'div',
33097             cls: cls,
33098             cn: [
33099                 {
33100                     tag: 'div',
33101                     cls: 'masonry-brick-split-head',
33102                     cn: [
33103                         {
33104                             tag: 'div',
33105                             cls: 'masonry-brick-paragraph',
33106                             cn: []
33107                         }
33108                     ]
33109                 },
33110                 {
33111                     tag: 'div',
33112                     cls: 'masonry-brick-split-body',
33113                     cn: []
33114                 }
33115             ]
33116         };
33117         
33118         if(this.href.length){
33119             cfg.href = this.href;
33120         }
33121         
33122         if(this.title.length){
33123             cfg.cn[0].cn[0].cn.push({
33124                 tag: 'h4',
33125                 cls: 'masonry-brick-title',
33126                 html: this.title
33127             });
33128         }
33129         
33130         if(this.html.length){
33131             cfg.cn[1].cn.push({
33132                 tag: 'p',
33133                 cls: 'masonry-brick-text',
33134                 html: this.html
33135             });
33136         }
33137
33138         if(this.bgimage.length){
33139             cfg.cn[0].cn.push({
33140                 tag: 'img',
33141                 cls: 'masonry-brick-image-view',
33142                 src: this.bgimage
33143             });
33144         }
33145         
33146         if(this.videourl.length){
33147             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33148             // youtube support only?
33149             cfg.cn[0].cn.cn.push({
33150                 tag: 'iframe',
33151                 cls: 'masonry-brick-image-view',
33152                 src: vurl,
33153                 frameborder : 0,
33154                 allowfullscreen : true
33155             });
33156         }
33157         
33158         return cfg;
33159     },
33160     
33161     initEvents: function() 
33162     {
33163         switch (this.size) {
33164             case 'xs' :
33165                 this.x = 1;
33166                 this.y = 1;
33167                 break;
33168             case 'sm' :
33169                 this.x = 2;
33170                 this.y = 2;
33171                 break;
33172             case 'md' :
33173             case 'md-left' :
33174             case 'md-right' :
33175                 this.x = 3;
33176                 this.y = 3;
33177                 break;
33178             case 'tall' :
33179                 this.x = 2;
33180                 this.y = 3;
33181                 break;
33182             case 'wide' :
33183                 this.x = 3;
33184                 this.y = 2;
33185                 break;
33186             case 'wide-thin' :
33187                 this.x = 3;
33188                 this.y = 1;
33189                 break;
33190                         
33191             default :
33192                 break;
33193         }
33194         
33195         if(Roo.isTouch){
33196             this.el.on('touchstart', this.onTouchStart, this);
33197             this.el.on('touchmove', this.onTouchMove, this);
33198             this.el.on('touchend', this.onTouchEnd, this);
33199             this.el.on('contextmenu', this.onContextMenu, this);
33200         } else {
33201             this.el.on('mouseenter'  ,this.enter, this);
33202             this.el.on('mouseleave', this.leave, this);
33203             this.el.on('click', this.onClick, this);
33204         }
33205         
33206         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33207             this.parent().bricks.push(this);   
33208         }
33209         
33210     },
33211     
33212     onClick: function(e, el)
33213     {
33214         var time = this.endTimer - this.startTimer;
33215         // Roo.log(e.preventDefault());
33216         if(Roo.isTouch){
33217             if(time > 1000){
33218                 e.preventDefault();
33219                 return;
33220             }
33221         }
33222         
33223         if(!this.preventDefault){
33224             return;
33225         }
33226         
33227         e.preventDefault();
33228         
33229         if (this.activeClass != '') {
33230             this.selectBrick();
33231         }
33232         
33233         this.fireEvent('click', this, e);
33234     },
33235     
33236     enter: function(e, el)
33237     {
33238         e.preventDefault();
33239         
33240         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33241             return;
33242         }
33243         
33244         if(this.bgimage.length && this.html.length){
33245             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33246         }
33247     },
33248     
33249     leave: function(e, el)
33250     {
33251         e.preventDefault();
33252         
33253         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33254             return;
33255         }
33256         
33257         if(this.bgimage.length && this.html.length){
33258             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33259         }
33260     },
33261     
33262     onTouchStart: function(e, el)
33263     {
33264 //        e.preventDefault();
33265         
33266         this.touchmoved = false;
33267         
33268         if(!this.isFitContainer){
33269             return;
33270         }
33271         
33272         if(!this.bgimage.length || !this.html.length){
33273             return;
33274         }
33275         
33276         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33277         
33278         this.timer = new Date().getTime();
33279         
33280     },
33281     
33282     onTouchMove: function(e, el)
33283     {
33284         this.touchmoved = true;
33285     },
33286     
33287     onContextMenu : function(e,el)
33288     {
33289         e.preventDefault();
33290         e.stopPropagation();
33291         return false;
33292     },
33293     
33294     onTouchEnd: function(e, el)
33295     {
33296 //        e.preventDefault();
33297         
33298         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33299         
33300             this.leave(e,el);
33301             
33302             return;
33303         }
33304         
33305         if(!this.bgimage.length || !this.html.length){
33306             
33307             if(this.href.length){
33308                 window.location.href = this.href;
33309             }
33310             
33311             return;
33312         }
33313         
33314         if(!this.isFitContainer){
33315             return;
33316         }
33317         
33318         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33319         
33320         window.location.href = this.href;
33321     },
33322     
33323     //selection on single brick only
33324     selectBrick : function() {
33325         
33326         if (!this.parentId) {
33327             return;
33328         }
33329         
33330         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33331         var index = m.selectedBrick.indexOf(this.id);
33332         
33333         if ( index > -1) {
33334             m.selectedBrick.splice(index,1);
33335             this.el.removeClass(this.activeClass);
33336             return;
33337         }
33338         
33339         for(var i = 0; i < m.selectedBrick.length; i++) {
33340             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33341             b.el.removeClass(b.activeClass);
33342         }
33343         
33344         m.selectedBrick = [];
33345         
33346         m.selectedBrick.push(this.id);
33347         this.el.addClass(this.activeClass);
33348         return;
33349     },
33350     
33351     isSelected : function(){
33352         return this.el.hasClass(this.activeClass);
33353         
33354     }
33355 });
33356
33357 Roo.apply(Roo.bootstrap.MasonryBrick, {
33358     
33359     //groups: {},
33360     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33361      /**
33362     * register a Masonry Brick
33363     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33364     */
33365     
33366     register : function(brick)
33367     {
33368         //this.groups[brick.id] = brick;
33369         this.groups.add(brick.id, brick);
33370     },
33371     /**
33372     * fetch a  masonry brick based on the masonry brick ID
33373     * @param {string} the masonry brick to add
33374     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33375     */
33376     
33377     get: function(brick_id) 
33378     {
33379         // if (typeof(this.groups[brick_id]) == 'undefined') {
33380         //     return false;
33381         // }
33382         // return this.groups[brick_id] ;
33383         
33384         if(this.groups.key(brick_id)) {
33385             return this.groups.key(brick_id);
33386         }
33387         
33388         return false;
33389     }
33390     
33391     
33392     
33393 });
33394
33395  /*
33396  * - LGPL
33397  *
33398  * element
33399  * 
33400  */
33401
33402 /**
33403  * @class Roo.bootstrap.Brick
33404  * @extends Roo.bootstrap.Component
33405  * Bootstrap Brick class
33406  * 
33407  * @constructor
33408  * Create a new Brick
33409  * @param {Object} config The config object
33410  */
33411
33412 Roo.bootstrap.Brick = function(config){
33413     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33414     
33415     this.addEvents({
33416         // raw events
33417         /**
33418          * @event click
33419          * When a Brick is click
33420          * @param {Roo.bootstrap.Brick} this
33421          * @param {Roo.EventObject} e
33422          */
33423         "click" : true
33424     });
33425 };
33426
33427 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33428     
33429     /**
33430      * @cfg {String} title
33431      */   
33432     title : '',
33433     /**
33434      * @cfg {String} html
33435      */   
33436     html : '',
33437     /**
33438      * @cfg {String} bgimage
33439      */   
33440     bgimage : '',
33441     /**
33442      * @cfg {String} cls
33443      */   
33444     cls : '',
33445     /**
33446      * @cfg {String} href
33447      */   
33448     href : '',
33449     /**
33450      * @cfg {String} video
33451      */   
33452     video : '',
33453     /**
33454      * @cfg {Boolean} square
33455      */   
33456     square : true,
33457     
33458     getAutoCreate : function()
33459     {
33460         var cls = 'roo-brick';
33461         
33462         if(this.href.length){
33463             cls += ' roo-brick-link';
33464         }
33465         
33466         if(this.bgimage.length){
33467             cls += ' roo-brick-image';
33468         }
33469         
33470         if(!this.html.length && !this.bgimage.length){
33471             cls += ' roo-brick-center-title';
33472         }
33473         
33474         if(!this.html.length && this.bgimage.length){
33475             cls += ' roo-brick-bottom-title';
33476         }
33477         
33478         if(this.cls){
33479             cls += ' ' + this.cls;
33480         }
33481         
33482         var cfg = {
33483             tag: (this.href.length) ? 'a' : 'div',
33484             cls: cls,
33485             cn: [
33486                 {
33487                     tag: 'div',
33488                     cls: 'roo-brick-paragraph',
33489                     cn: []
33490                 }
33491             ]
33492         };
33493         
33494         if(this.href.length){
33495             cfg.href = this.href;
33496         }
33497         
33498         var cn = cfg.cn[0].cn;
33499         
33500         if(this.title.length){
33501             cn.push({
33502                 tag: 'h4',
33503                 cls: 'roo-brick-title',
33504                 html: this.title
33505             });
33506         }
33507         
33508         if(this.html.length){
33509             cn.push({
33510                 tag: 'p',
33511                 cls: 'roo-brick-text',
33512                 html: this.html
33513             });
33514         } else {
33515             cn.cls += ' hide';
33516         }
33517         
33518         if(this.bgimage.length){
33519             cfg.cn.push({
33520                 tag: 'img',
33521                 cls: 'roo-brick-image-view',
33522                 src: this.bgimage
33523             });
33524         }
33525         
33526         return cfg;
33527     },
33528     
33529     initEvents: function() 
33530     {
33531         if(this.title.length || this.html.length){
33532             this.el.on('mouseenter'  ,this.enter, this);
33533             this.el.on('mouseleave', this.leave, this);
33534         }
33535         
33536         Roo.EventManager.onWindowResize(this.resize, this); 
33537         
33538         if(this.bgimage.length){
33539             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33540             this.imageEl.on('load', this.onImageLoad, this);
33541             return;
33542         }
33543         
33544         this.resize();
33545     },
33546     
33547     onImageLoad : function()
33548     {
33549         this.resize();
33550     },
33551     
33552     resize : function()
33553     {
33554         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33555         
33556         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33557         
33558         if(this.bgimage.length){
33559             var image = this.el.select('.roo-brick-image-view', true).first();
33560             
33561             image.setWidth(paragraph.getWidth());
33562             
33563             if(this.square){
33564                 image.setHeight(paragraph.getWidth());
33565             }
33566             
33567             this.el.setHeight(image.getHeight());
33568             paragraph.setHeight(image.getHeight());
33569             
33570         }
33571         
33572     },
33573     
33574     enter: function(e, el)
33575     {
33576         e.preventDefault();
33577         
33578         if(this.bgimage.length){
33579             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33580             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33581         }
33582     },
33583     
33584     leave: function(e, el)
33585     {
33586         e.preventDefault();
33587         
33588         if(this.bgimage.length){
33589             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33590             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33591         }
33592     }
33593     
33594 });
33595
33596  
33597
33598  /*
33599  * - LGPL
33600  *
33601  * Number field 
33602  */
33603
33604 /**
33605  * @class Roo.bootstrap.NumberField
33606  * @extends Roo.bootstrap.Input
33607  * Bootstrap NumberField class
33608  * 
33609  * 
33610  * 
33611  * 
33612  * @constructor
33613  * Create a new NumberField
33614  * @param {Object} config The config object
33615  */
33616
33617 Roo.bootstrap.NumberField = function(config){
33618     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33619 };
33620
33621 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33622     
33623     /**
33624      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33625      */
33626     allowDecimals : true,
33627     /**
33628      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33629      */
33630     decimalSeparator : ".",
33631     /**
33632      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33633      */
33634     decimalPrecision : 2,
33635     /**
33636      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33637      */
33638     allowNegative : true,
33639     
33640     /**
33641      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33642      */
33643     allowZero: true,
33644     /**
33645      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33646      */
33647     minValue : Number.NEGATIVE_INFINITY,
33648     /**
33649      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33650      */
33651     maxValue : Number.MAX_VALUE,
33652     /**
33653      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33654      */
33655     minText : "The minimum value for this field is {0}",
33656     /**
33657      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33658      */
33659     maxText : "The maximum value for this field is {0}",
33660     /**
33661      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33662      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33663      */
33664     nanText : "{0} is not a valid number",
33665     /**
33666      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33667      */
33668     thousandsDelimiter : false,
33669     /**
33670      * @cfg {String} valueAlign alignment of value
33671      */
33672     valueAlign : "left",
33673
33674     getAutoCreate : function()
33675     {
33676         var hiddenInput = {
33677             tag: 'input',
33678             type: 'hidden',
33679             id: Roo.id(),
33680             cls: 'hidden-number-input'
33681         };
33682         
33683         if (this.name) {
33684             hiddenInput.name = this.name;
33685         }
33686         
33687         this.name = '';
33688         
33689         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33690         
33691         this.name = hiddenInput.name;
33692         
33693         if(cfg.cn.length > 0) {
33694             cfg.cn.push(hiddenInput);
33695         }
33696         
33697         return cfg;
33698     },
33699
33700     // private
33701     initEvents : function()
33702     {   
33703         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33704         
33705         var allowed = "0123456789";
33706         
33707         if(this.allowDecimals){
33708             allowed += this.decimalSeparator;
33709         }
33710         
33711         if(this.allowNegative){
33712             allowed += "-";
33713         }
33714         
33715         if(this.thousandsDelimiter) {
33716             allowed += ",";
33717         }
33718         
33719         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33720         
33721         var keyPress = function(e){
33722             
33723             var k = e.getKey();
33724             
33725             var c = e.getCharCode();
33726             
33727             if(
33728                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33729                     allowed.indexOf(String.fromCharCode(c)) === -1
33730             ){
33731                 e.stopEvent();
33732                 return;
33733             }
33734             
33735             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33736                 return;
33737             }
33738             
33739             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33740                 e.stopEvent();
33741             }
33742         };
33743         
33744         this.el.on("keypress", keyPress, this);
33745     },
33746     
33747     validateValue : function(value)
33748     {
33749         
33750         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33751             return false;
33752         }
33753         
33754         var num = this.parseValue(value);
33755         
33756         if(isNaN(num)){
33757             this.markInvalid(String.format(this.nanText, value));
33758             return false;
33759         }
33760         
33761         if(num < this.minValue){
33762             this.markInvalid(String.format(this.minText, this.minValue));
33763             return false;
33764         }
33765         
33766         if(num > this.maxValue){
33767             this.markInvalid(String.format(this.maxText, this.maxValue));
33768             return false;
33769         }
33770         
33771         return true;
33772     },
33773
33774     getValue : function()
33775     {
33776         var v = this.hiddenEl().getValue();
33777         
33778         return this.fixPrecision(this.parseValue(v));
33779     },
33780
33781     parseValue : function(value)
33782     {
33783         if(this.thousandsDelimiter) {
33784             value += "";
33785             r = new RegExp(",", "g");
33786             value = value.replace(r, "");
33787         }
33788         
33789         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33790         return isNaN(value) ? '' : value;
33791     },
33792
33793     fixPrecision : function(value)
33794     {
33795         if(this.thousandsDelimiter) {
33796             value += "";
33797             r = new RegExp(",", "g");
33798             value = value.replace(r, "");
33799         }
33800         
33801         var nan = isNaN(value);
33802         
33803         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33804             return nan ? '' : value;
33805         }
33806         return parseFloat(value).toFixed(this.decimalPrecision);
33807     },
33808
33809     setValue : function(v)
33810     {
33811         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33812         
33813         this.value = v;
33814         
33815         if(this.rendered){
33816             
33817             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33818             
33819             this.inputEl().dom.value = (v == '') ? '' :
33820                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33821             
33822             if(!this.allowZero && v === '0') {
33823                 this.hiddenEl().dom.value = '';
33824                 this.inputEl().dom.value = '';
33825             }
33826             
33827             this.validate();
33828         }
33829     },
33830
33831     decimalPrecisionFcn : function(v)
33832     {
33833         return Math.floor(v);
33834     },
33835
33836     beforeBlur : function()
33837     {
33838         var v = this.parseValue(this.getRawValue());
33839         
33840         if(v || v === 0 || v === ''){
33841             this.setValue(v);
33842         }
33843     },
33844     
33845     hiddenEl : function()
33846     {
33847         return this.el.select('input.hidden-number-input',true).first();
33848     }
33849     
33850 });
33851
33852  
33853
33854 /*
33855 * Licence: LGPL
33856 */
33857
33858 /**
33859  * @class Roo.bootstrap.DocumentSlider
33860  * @extends Roo.bootstrap.Component
33861  * Bootstrap DocumentSlider class
33862  * 
33863  * @constructor
33864  * Create a new DocumentViewer
33865  * @param {Object} config The config object
33866  */
33867
33868 Roo.bootstrap.DocumentSlider = function(config){
33869     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33870     
33871     this.files = [];
33872     
33873     this.addEvents({
33874         /**
33875          * @event initial
33876          * Fire after initEvent
33877          * @param {Roo.bootstrap.DocumentSlider} this
33878          */
33879         "initial" : true,
33880         /**
33881          * @event update
33882          * Fire after update
33883          * @param {Roo.bootstrap.DocumentSlider} this
33884          */
33885         "update" : true,
33886         /**
33887          * @event click
33888          * Fire after click
33889          * @param {Roo.bootstrap.DocumentSlider} this
33890          */
33891         "click" : true
33892     });
33893 };
33894
33895 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33896     
33897     files : false,
33898     
33899     indicator : 0,
33900     
33901     getAutoCreate : function()
33902     {
33903         var cfg = {
33904             tag : 'div',
33905             cls : 'roo-document-slider',
33906             cn : [
33907                 {
33908                     tag : 'div',
33909                     cls : 'roo-document-slider-header',
33910                     cn : [
33911                         {
33912                             tag : 'div',
33913                             cls : 'roo-document-slider-header-title'
33914                         }
33915                     ]
33916                 },
33917                 {
33918                     tag : 'div',
33919                     cls : 'roo-document-slider-body',
33920                     cn : [
33921                         {
33922                             tag : 'div',
33923                             cls : 'roo-document-slider-prev',
33924                             cn : [
33925                                 {
33926                                     tag : 'i',
33927                                     cls : 'fa fa-chevron-left'
33928                                 }
33929                             ]
33930                         },
33931                         {
33932                             tag : 'div',
33933                             cls : 'roo-document-slider-thumb',
33934                             cn : [
33935                                 {
33936                                     tag : 'img',
33937                                     cls : 'roo-document-slider-image'
33938                                 }
33939                             ]
33940                         },
33941                         {
33942                             tag : 'div',
33943                             cls : 'roo-document-slider-next',
33944                             cn : [
33945                                 {
33946                                     tag : 'i',
33947                                     cls : 'fa fa-chevron-right'
33948                                 }
33949                             ]
33950                         }
33951                     ]
33952                 }
33953             ]
33954         };
33955         
33956         return cfg;
33957     },
33958     
33959     initEvents : function()
33960     {
33961         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33962         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33963         
33964         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33965         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33966         
33967         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33968         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33969         
33970         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33971         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33972         
33973         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33974         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33975         
33976         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33977         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33978         
33979         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33980         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33981         
33982         this.thumbEl.on('click', this.onClick, this);
33983         
33984         this.prevIndicator.on('click', this.prev, this);
33985         
33986         this.nextIndicator.on('click', this.next, this);
33987         
33988     },
33989     
33990     initial : function()
33991     {
33992         if(this.files.length){
33993             this.indicator = 1;
33994             this.update()
33995         }
33996         
33997         this.fireEvent('initial', this);
33998     },
33999     
34000     update : function()
34001     {
34002         this.imageEl.attr('src', this.files[this.indicator - 1]);
34003         
34004         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34005         
34006         this.prevIndicator.show();
34007         
34008         if(this.indicator == 1){
34009             this.prevIndicator.hide();
34010         }
34011         
34012         this.nextIndicator.show();
34013         
34014         if(this.indicator == this.files.length){
34015             this.nextIndicator.hide();
34016         }
34017         
34018         this.thumbEl.scrollTo('top');
34019         
34020         this.fireEvent('update', this);
34021     },
34022     
34023     onClick : function(e)
34024     {
34025         e.preventDefault();
34026         
34027         this.fireEvent('click', this);
34028     },
34029     
34030     prev : function(e)
34031     {
34032         e.preventDefault();
34033         
34034         this.indicator = Math.max(1, this.indicator - 1);
34035         
34036         this.update();
34037     },
34038     
34039     next : function(e)
34040     {
34041         e.preventDefault();
34042         
34043         this.indicator = Math.min(this.files.length, this.indicator + 1);
34044         
34045         this.update();
34046     }
34047 });
34048 /*
34049  * - LGPL
34050  *
34051  * RadioSet
34052  *
34053  *
34054  */
34055
34056 /**
34057  * @class Roo.bootstrap.RadioSet
34058  * @extends Roo.bootstrap.Input
34059  * Bootstrap RadioSet class
34060  * @cfg {String} indicatorpos (left|right) default left
34061  * @cfg {Boolean} inline (true|false) inline the element (default true)
34062  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34063  * @constructor
34064  * Create a new RadioSet
34065  * @param {Object} config The config object
34066  */
34067
34068 Roo.bootstrap.RadioSet = function(config){
34069     
34070     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34071     
34072     this.radioes = [];
34073     
34074     Roo.bootstrap.RadioSet.register(this);
34075     
34076     this.addEvents({
34077         /**
34078         * @event check
34079         * Fires when the element is checked or unchecked.
34080         * @param {Roo.bootstrap.RadioSet} this This radio
34081         * @param {Roo.bootstrap.Radio} item The checked item
34082         */
34083        check : true,
34084        /**
34085         * @event click
34086         * Fires when the element is click.
34087         * @param {Roo.bootstrap.RadioSet} this This radio set
34088         * @param {Roo.bootstrap.Radio} item The checked item
34089         * @param {Roo.EventObject} e The event object
34090         */
34091        click : true
34092     });
34093     
34094 };
34095
34096 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34097
34098     radioes : false,
34099     
34100     inline : true,
34101     
34102     weight : '',
34103     
34104     indicatorpos : 'left',
34105     
34106     getAutoCreate : function()
34107     {
34108         var label = {
34109             tag : 'label',
34110             cls : 'roo-radio-set-label',
34111             cn : [
34112                 {
34113                     tag : 'span',
34114                     html : this.fieldLabel
34115                 }
34116             ]
34117         };
34118         if (Roo.bootstrap.version == 3) {
34119             
34120             
34121             if(this.indicatorpos == 'left'){
34122                 label.cn.unshift({
34123                     tag : 'i',
34124                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34125                     tooltip : 'This field is required'
34126                 });
34127             } else {
34128                 label.cn.push({
34129                     tag : 'i',
34130                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34131                     tooltip : 'This field is required'
34132                 });
34133             }
34134         }
34135         var items = {
34136             tag : 'div',
34137             cls : 'roo-radio-set-items'
34138         };
34139         
34140         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34141         
34142         if (align === 'left' && this.fieldLabel.length) {
34143             
34144             items = {
34145                 cls : "roo-radio-set-right", 
34146                 cn: [
34147                     items
34148                 ]
34149             };
34150             
34151             if(this.labelWidth > 12){
34152                 label.style = "width: " + this.labelWidth + 'px';
34153             }
34154             
34155             if(this.labelWidth < 13 && this.labelmd == 0){
34156                 this.labelmd = this.labelWidth;
34157             }
34158             
34159             if(this.labellg > 0){
34160                 label.cls += ' col-lg-' + this.labellg;
34161                 items.cls += ' col-lg-' + (12 - this.labellg);
34162             }
34163             
34164             if(this.labelmd > 0){
34165                 label.cls += ' col-md-' + this.labelmd;
34166                 items.cls += ' col-md-' + (12 - this.labelmd);
34167             }
34168             
34169             if(this.labelsm > 0){
34170                 label.cls += ' col-sm-' + this.labelsm;
34171                 items.cls += ' col-sm-' + (12 - this.labelsm);
34172             }
34173             
34174             if(this.labelxs > 0){
34175                 label.cls += ' col-xs-' + this.labelxs;
34176                 items.cls += ' col-xs-' + (12 - this.labelxs);
34177             }
34178         }
34179         
34180         var cfg = {
34181             tag : 'div',
34182             cls : 'roo-radio-set',
34183             cn : [
34184                 {
34185                     tag : 'input',
34186                     cls : 'roo-radio-set-input',
34187                     type : 'hidden',
34188                     name : this.name,
34189                     value : this.value ? this.value :  ''
34190                 },
34191                 label,
34192                 items
34193             ]
34194         };
34195         
34196         if(this.weight.length){
34197             cfg.cls += ' roo-radio-' + this.weight;
34198         }
34199         
34200         if(this.inline) {
34201             cfg.cls += ' roo-radio-set-inline';
34202         }
34203         
34204         var settings=this;
34205         ['xs','sm','md','lg'].map(function(size){
34206             if (settings[size]) {
34207                 cfg.cls += ' col-' + size + '-' + settings[size];
34208             }
34209         });
34210         
34211         return cfg;
34212         
34213     },
34214
34215     initEvents : function()
34216     {
34217         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34218         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34219         
34220         if(!this.fieldLabel.length){
34221             this.labelEl.hide();
34222         }
34223         
34224         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34225         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34226         
34227         this.indicator = this.indicatorEl();
34228         
34229         if(this.indicator){
34230             this.indicator.addClass('invisible');
34231         }
34232         
34233         this.originalValue = this.getValue();
34234         
34235     },
34236     
34237     inputEl: function ()
34238     {
34239         return this.el.select('.roo-radio-set-input', true).first();
34240     },
34241     
34242     getChildContainer : function()
34243     {
34244         return this.itemsEl;
34245     },
34246     
34247     register : function(item)
34248     {
34249         this.radioes.push(item);
34250         
34251     },
34252     
34253     validate : function()
34254     {   
34255         if(this.getVisibilityEl().hasClass('hidden')){
34256             return true;
34257         }
34258         
34259         var valid = false;
34260         
34261         Roo.each(this.radioes, function(i){
34262             if(!i.checked){
34263                 return;
34264             }
34265             
34266             valid = true;
34267             return false;
34268         });
34269         
34270         if(this.allowBlank) {
34271             return true;
34272         }
34273         
34274         if(this.disabled || valid){
34275             this.markValid();
34276             return true;
34277         }
34278         
34279         this.markInvalid();
34280         return false;
34281         
34282     },
34283     
34284     markValid : function()
34285     {
34286         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34287             this.indicatorEl().removeClass('visible');
34288             this.indicatorEl().addClass('invisible');
34289         }
34290         
34291         
34292         if (Roo.bootstrap.version == 3) {
34293             this.el.removeClass([this.invalidClass, this.validClass]);
34294             this.el.addClass(this.validClass);
34295         } else {
34296             this.el.removeClass(['is-invalid','is-valid']);
34297             this.el.addClass(['is-valid']);
34298         }
34299         this.fireEvent('valid', this);
34300     },
34301     
34302     markInvalid : function(msg)
34303     {
34304         if(this.allowBlank || this.disabled){
34305             return;
34306         }
34307         
34308         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34309             this.indicatorEl().removeClass('invisible');
34310             this.indicatorEl().addClass('visible');
34311         }
34312         if (Roo.bootstrap.version == 3) {
34313             this.el.removeClass([this.invalidClass, this.validClass]);
34314             this.el.addClass(this.invalidClass);
34315         } else {
34316             this.el.removeClass(['is-invalid','is-valid']);
34317             this.el.addClass(['is-invalid']);
34318         }
34319         
34320         this.fireEvent('invalid', this, msg);
34321         
34322     },
34323     
34324     setValue : function(v, suppressEvent)
34325     {   
34326         if(this.value === v){
34327             return;
34328         }
34329         
34330         this.value = v;
34331         
34332         if(this.rendered){
34333             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34334         }
34335         
34336         Roo.each(this.radioes, function(i){
34337             i.checked = false;
34338             i.el.removeClass('checked');
34339         });
34340         
34341         Roo.each(this.radioes, function(i){
34342             
34343             if(i.value === v || i.value.toString() === v.toString()){
34344                 i.checked = true;
34345                 i.el.addClass('checked');
34346                 
34347                 if(suppressEvent !== true){
34348                     this.fireEvent('check', this, i);
34349                 }
34350                 
34351                 return false;
34352             }
34353             
34354         }, this);
34355         
34356         this.validate();
34357     },
34358     
34359     clearInvalid : function(){
34360         
34361         if(!this.el || this.preventMark){
34362             return;
34363         }
34364         
34365         this.el.removeClass([this.invalidClass]);
34366         
34367         this.fireEvent('valid', this);
34368     }
34369     
34370 });
34371
34372 Roo.apply(Roo.bootstrap.RadioSet, {
34373     
34374     groups: {},
34375     
34376     register : function(set)
34377     {
34378         this.groups[set.name] = set;
34379     },
34380     
34381     get: function(name) 
34382     {
34383         if (typeof(this.groups[name]) == 'undefined') {
34384             return false;
34385         }
34386         
34387         return this.groups[name] ;
34388     }
34389     
34390 });
34391 /*
34392  * Based on:
34393  * Ext JS Library 1.1.1
34394  * Copyright(c) 2006-2007, Ext JS, LLC.
34395  *
34396  * Originally Released Under LGPL - original licence link has changed is not relivant.
34397  *
34398  * Fork - LGPL
34399  * <script type="text/javascript">
34400  */
34401
34402
34403 /**
34404  * @class Roo.bootstrap.SplitBar
34405  * @extends Roo.util.Observable
34406  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34407  * <br><br>
34408  * Usage:
34409  * <pre><code>
34410 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34411                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34412 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34413 split.minSize = 100;
34414 split.maxSize = 600;
34415 split.animate = true;
34416 split.on('moved', splitterMoved);
34417 </code></pre>
34418  * @constructor
34419  * Create a new SplitBar
34420  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34421  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34422  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34423  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34424                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34425                         position of the SplitBar).
34426  */
34427 Roo.bootstrap.SplitBar = function(cfg){
34428     
34429     /** @private */
34430     
34431     //{
34432     //  dragElement : elm
34433     //  resizingElement: el,
34434         // optional..
34435     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34436     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34437         // existingProxy ???
34438     //}
34439     
34440     this.el = Roo.get(cfg.dragElement, true);
34441     this.el.dom.unselectable = "on";
34442     /** @private */
34443     this.resizingEl = Roo.get(cfg.resizingElement, true);
34444
34445     /**
34446      * @private
34447      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34448      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34449      * @type Number
34450      */
34451     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34452     
34453     /**
34454      * The minimum size of the resizing element. (Defaults to 0)
34455      * @type Number
34456      */
34457     this.minSize = 0;
34458     
34459     /**
34460      * The maximum size of the resizing element. (Defaults to 2000)
34461      * @type Number
34462      */
34463     this.maxSize = 2000;
34464     
34465     /**
34466      * Whether to animate the transition to the new size
34467      * @type Boolean
34468      */
34469     this.animate = false;
34470     
34471     /**
34472      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34473      * @type Boolean
34474      */
34475     this.useShim = false;
34476     
34477     /** @private */
34478     this.shim = null;
34479     
34480     if(!cfg.existingProxy){
34481         /** @private */
34482         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34483     }else{
34484         this.proxy = Roo.get(cfg.existingProxy).dom;
34485     }
34486     /** @private */
34487     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34488     
34489     /** @private */
34490     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34491     
34492     /** @private */
34493     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34494     
34495     /** @private */
34496     this.dragSpecs = {};
34497     
34498     /**
34499      * @private The adapter to use to positon and resize elements
34500      */
34501     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34502     this.adapter.init(this);
34503     
34504     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34505         /** @private */
34506         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34507         this.el.addClass("roo-splitbar-h");
34508     }else{
34509         /** @private */
34510         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34511         this.el.addClass("roo-splitbar-v");
34512     }
34513     
34514     this.addEvents({
34515         /**
34516          * @event resize
34517          * Fires when the splitter is moved (alias for {@link #event-moved})
34518          * @param {Roo.bootstrap.SplitBar} this
34519          * @param {Number} newSize the new width or height
34520          */
34521         "resize" : true,
34522         /**
34523          * @event moved
34524          * Fires when the splitter is moved
34525          * @param {Roo.bootstrap.SplitBar} this
34526          * @param {Number} newSize the new width or height
34527          */
34528         "moved" : true,
34529         /**
34530          * @event beforeresize
34531          * Fires before the splitter is dragged
34532          * @param {Roo.bootstrap.SplitBar} this
34533          */
34534         "beforeresize" : true,
34535
34536         "beforeapply" : true
34537     });
34538
34539     Roo.util.Observable.call(this);
34540 };
34541
34542 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34543     onStartProxyDrag : function(x, y){
34544         this.fireEvent("beforeresize", this);
34545         if(!this.overlay){
34546             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34547             o.unselectable();
34548             o.enableDisplayMode("block");
34549             // all splitbars share the same overlay
34550             Roo.bootstrap.SplitBar.prototype.overlay = o;
34551         }
34552         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34553         this.overlay.show();
34554         Roo.get(this.proxy).setDisplayed("block");
34555         var size = this.adapter.getElementSize(this);
34556         this.activeMinSize = this.getMinimumSize();;
34557         this.activeMaxSize = this.getMaximumSize();;
34558         var c1 = size - this.activeMinSize;
34559         var c2 = Math.max(this.activeMaxSize - size, 0);
34560         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34561             this.dd.resetConstraints();
34562             this.dd.setXConstraint(
34563                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34564                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34565             );
34566             this.dd.setYConstraint(0, 0);
34567         }else{
34568             this.dd.resetConstraints();
34569             this.dd.setXConstraint(0, 0);
34570             this.dd.setYConstraint(
34571                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34572                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34573             );
34574          }
34575         this.dragSpecs.startSize = size;
34576         this.dragSpecs.startPoint = [x, y];
34577         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34578     },
34579     
34580     /** 
34581      * @private Called after the drag operation by the DDProxy
34582      */
34583     onEndProxyDrag : function(e){
34584         Roo.get(this.proxy).setDisplayed(false);
34585         var endPoint = Roo.lib.Event.getXY(e);
34586         if(this.overlay){
34587             this.overlay.hide();
34588         }
34589         var newSize;
34590         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34591             newSize = this.dragSpecs.startSize + 
34592                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34593                     endPoint[0] - this.dragSpecs.startPoint[0] :
34594                     this.dragSpecs.startPoint[0] - endPoint[0]
34595                 );
34596         }else{
34597             newSize = this.dragSpecs.startSize + 
34598                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34599                     endPoint[1] - this.dragSpecs.startPoint[1] :
34600                     this.dragSpecs.startPoint[1] - endPoint[1]
34601                 );
34602         }
34603         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34604         if(newSize != this.dragSpecs.startSize){
34605             if(this.fireEvent('beforeapply', this, newSize) !== false){
34606                 this.adapter.setElementSize(this, newSize);
34607                 this.fireEvent("moved", this, newSize);
34608                 this.fireEvent("resize", this, newSize);
34609             }
34610         }
34611     },
34612     
34613     /**
34614      * Get the adapter this SplitBar uses
34615      * @return The adapter object
34616      */
34617     getAdapter : function(){
34618         return this.adapter;
34619     },
34620     
34621     /**
34622      * Set the adapter this SplitBar uses
34623      * @param {Object} adapter A SplitBar adapter object
34624      */
34625     setAdapter : function(adapter){
34626         this.adapter = adapter;
34627         this.adapter.init(this);
34628     },
34629     
34630     /**
34631      * Gets the minimum size for the resizing element
34632      * @return {Number} The minimum size
34633      */
34634     getMinimumSize : function(){
34635         return this.minSize;
34636     },
34637     
34638     /**
34639      * Sets the minimum size for the resizing element
34640      * @param {Number} minSize The minimum size
34641      */
34642     setMinimumSize : function(minSize){
34643         this.minSize = minSize;
34644     },
34645     
34646     /**
34647      * Gets the maximum size for the resizing element
34648      * @return {Number} The maximum size
34649      */
34650     getMaximumSize : function(){
34651         return this.maxSize;
34652     },
34653     
34654     /**
34655      * Sets the maximum size for the resizing element
34656      * @param {Number} maxSize The maximum size
34657      */
34658     setMaximumSize : function(maxSize){
34659         this.maxSize = maxSize;
34660     },
34661     
34662     /**
34663      * Sets the initialize size for the resizing element
34664      * @param {Number} size The initial size
34665      */
34666     setCurrentSize : function(size){
34667         var oldAnimate = this.animate;
34668         this.animate = false;
34669         this.adapter.setElementSize(this, size);
34670         this.animate = oldAnimate;
34671     },
34672     
34673     /**
34674      * Destroy this splitbar. 
34675      * @param {Boolean} removeEl True to remove the element
34676      */
34677     destroy : function(removeEl){
34678         if(this.shim){
34679             this.shim.remove();
34680         }
34681         this.dd.unreg();
34682         this.proxy.parentNode.removeChild(this.proxy);
34683         if(removeEl){
34684             this.el.remove();
34685         }
34686     }
34687 });
34688
34689 /**
34690  * @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.
34691  */
34692 Roo.bootstrap.SplitBar.createProxy = function(dir){
34693     var proxy = new Roo.Element(document.createElement("div"));
34694     proxy.unselectable();
34695     var cls = 'roo-splitbar-proxy';
34696     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34697     document.body.appendChild(proxy.dom);
34698     return proxy.dom;
34699 };
34700
34701 /** 
34702  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34703  * Default Adapter. It assumes the splitter and resizing element are not positioned
34704  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34705  */
34706 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34707 };
34708
34709 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34710     // do nothing for now
34711     init : function(s){
34712     
34713     },
34714     /**
34715      * Called before drag operations to get the current size of the resizing element. 
34716      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34717      */
34718      getElementSize : function(s){
34719         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34720             return s.resizingEl.getWidth();
34721         }else{
34722             return s.resizingEl.getHeight();
34723         }
34724     },
34725     
34726     /**
34727      * Called after drag operations to set the size of the resizing element.
34728      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34729      * @param {Number} newSize The new size to set
34730      * @param {Function} onComplete A function to be invoked when resizing is complete
34731      */
34732     setElementSize : function(s, newSize, onComplete){
34733         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34734             if(!s.animate){
34735                 s.resizingEl.setWidth(newSize);
34736                 if(onComplete){
34737                     onComplete(s, newSize);
34738                 }
34739             }else{
34740                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34741             }
34742         }else{
34743             
34744             if(!s.animate){
34745                 s.resizingEl.setHeight(newSize);
34746                 if(onComplete){
34747                     onComplete(s, newSize);
34748                 }
34749             }else{
34750                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34751             }
34752         }
34753     }
34754 };
34755
34756 /** 
34757  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34758  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34759  * Adapter that  moves the splitter element to align with the resized sizing element. 
34760  * Used with an absolute positioned SplitBar.
34761  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34762  * document.body, make sure you assign an id to the body element.
34763  */
34764 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34765     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34766     this.container = Roo.get(container);
34767 };
34768
34769 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34770     init : function(s){
34771         this.basic.init(s);
34772     },
34773     
34774     getElementSize : function(s){
34775         return this.basic.getElementSize(s);
34776     },
34777     
34778     setElementSize : function(s, newSize, onComplete){
34779         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34780     },
34781     
34782     moveSplitter : function(s){
34783         var yes = Roo.bootstrap.SplitBar;
34784         switch(s.placement){
34785             case yes.LEFT:
34786                 s.el.setX(s.resizingEl.getRight());
34787                 break;
34788             case yes.RIGHT:
34789                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34790                 break;
34791             case yes.TOP:
34792                 s.el.setY(s.resizingEl.getBottom());
34793                 break;
34794             case yes.BOTTOM:
34795                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34796                 break;
34797         }
34798     }
34799 };
34800
34801 /**
34802  * Orientation constant - Create a vertical SplitBar
34803  * @static
34804  * @type Number
34805  */
34806 Roo.bootstrap.SplitBar.VERTICAL = 1;
34807
34808 /**
34809  * Orientation constant - Create a horizontal SplitBar
34810  * @static
34811  * @type Number
34812  */
34813 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34814
34815 /**
34816  * Placement constant - The resizing element is to the left of the splitter element
34817  * @static
34818  * @type Number
34819  */
34820 Roo.bootstrap.SplitBar.LEFT = 1;
34821
34822 /**
34823  * Placement constant - The resizing element is to the right of the splitter element
34824  * @static
34825  * @type Number
34826  */
34827 Roo.bootstrap.SplitBar.RIGHT = 2;
34828
34829 /**
34830  * Placement constant - The resizing element is positioned above the splitter element
34831  * @static
34832  * @type Number
34833  */
34834 Roo.bootstrap.SplitBar.TOP = 3;
34835
34836 /**
34837  * Placement constant - The resizing element is positioned under splitter element
34838  * @static
34839  * @type Number
34840  */
34841 Roo.bootstrap.SplitBar.BOTTOM = 4;
34842 Roo.namespace("Roo.bootstrap.layout");/*
34843  * Based on:
34844  * Ext JS Library 1.1.1
34845  * Copyright(c) 2006-2007, Ext JS, LLC.
34846  *
34847  * Originally Released Under LGPL - original licence link has changed is not relivant.
34848  *
34849  * Fork - LGPL
34850  * <script type="text/javascript">
34851  */
34852
34853 /**
34854  * @class Roo.bootstrap.layout.Manager
34855  * @extends Roo.bootstrap.Component
34856  * Base class for layout managers.
34857  */
34858 Roo.bootstrap.layout.Manager = function(config)
34859 {
34860     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34861
34862
34863
34864
34865
34866     /** false to disable window resize monitoring @type Boolean */
34867     this.monitorWindowResize = true;
34868     this.regions = {};
34869     this.addEvents({
34870         /**
34871          * @event layout
34872          * Fires when a layout is performed.
34873          * @param {Roo.LayoutManager} this
34874          */
34875         "layout" : true,
34876         /**
34877          * @event regionresized
34878          * Fires when the user resizes a region.
34879          * @param {Roo.LayoutRegion} region The resized region
34880          * @param {Number} newSize The new size (width for east/west, height for north/south)
34881          */
34882         "regionresized" : true,
34883         /**
34884          * @event regioncollapsed
34885          * Fires when a region is collapsed.
34886          * @param {Roo.LayoutRegion} region The collapsed region
34887          */
34888         "regioncollapsed" : true,
34889         /**
34890          * @event regionexpanded
34891          * Fires when a region is expanded.
34892          * @param {Roo.LayoutRegion} region The expanded region
34893          */
34894         "regionexpanded" : true
34895     });
34896     this.updating = false;
34897
34898     if (config.el) {
34899         this.el = Roo.get(config.el);
34900         this.initEvents();
34901     }
34902
34903 };
34904
34905 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34906
34907
34908     regions : null,
34909
34910     monitorWindowResize : true,
34911
34912
34913     updating : false,
34914
34915
34916     onRender : function(ct, position)
34917     {
34918         if(!this.el){
34919             this.el = Roo.get(ct);
34920             this.initEvents();
34921         }
34922         //this.fireEvent('render',this);
34923     },
34924
34925
34926     initEvents: function()
34927     {
34928
34929
34930         // ie scrollbar fix
34931         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34932             document.body.scroll = "no";
34933         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34934             this.el.position('relative');
34935         }
34936         this.id = this.el.id;
34937         this.el.addClass("roo-layout-container");
34938         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34939         if(this.el.dom != document.body ) {
34940             this.el.on('resize', this.layout,this);
34941             this.el.on('show', this.layout,this);
34942         }
34943
34944     },
34945
34946     /**
34947      * Returns true if this layout is currently being updated
34948      * @return {Boolean}
34949      */
34950     isUpdating : function(){
34951         return this.updating;
34952     },
34953
34954     /**
34955      * Suspend the LayoutManager from doing auto-layouts while
34956      * making multiple add or remove calls
34957      */
34958     beginUpdate : function(){
34959         this.updating = true;
34960     },
34961
34962     /**
34963      * Restore auto-layouts and optionally disable the manager from performing a layout
34964      * @param {Boolean} noLayout true to disable a layout update
34965      */
34966     endUpdate : function(noLayout){
34967         this.updating = false;
34968         if(!noLayout){
34969             this.layout();
34970         }
34971     },
34972
34973     layout: function(){
34974         // abstract...
34975     },
34976
34977     onRegionResized : function(region, newSize){
34978         this.fireEvent("regionresized", region, newSize);
34979         this.layout();
34980     },
34981
34982     onRegionCollapsed : function(region){
34983         this.fireEvent("regioncollapsed", region);
34984     },
34985
34986     onRegionExpanded : function(region){
34987         this.fireEvent("regionexpanded", region);
34988     },
34989
34990     /**
34991      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34992      * performs box-model adjustments.
34993      * @return {Object} The size as an object {width: (the width), height: (the height)}
34994      */
34995     getViewSize : function()
34996     {
34997         var size;
34998         if(this.el.dom != document.body){
34999             size = this.el.getSize();
35000         }else{
35001             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35002         }
35003         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35004         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35005         return size;
35006     },
35007
35008     /**
35009      * Returns the Element this layout is bound to.
35010      * @return {Roo.Element}
35011      */
35012     getEl : function(){
35013         return this.el;
35014     },
35015
35016     /**
35017      * Returns the specified region.
35018      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35019      * @return {Roo.LayoutRegion}
35020      */
35021     getRegion : function(target){
35022         return this.regions[target.toLowerCase()];
35023     },
35024
35025     onWindowResize : function(){
35026         if(this.monitorWindowResize){
35027             this.layout();
35028         }
35029     }
35030 });
35031 /*
35032  * Based on:
35033  * Ext JS Library 1.1.1
35034  * Copyright(c) 2006-2007, Ext JS, LLC.
35035  *
35036  * Originally Released Under LGPL - original licence link has changed is not relivant.
35037  *
35038  * Fork - LGPL
35039  * <script type="text/javascript">
35040  */
35041 /**
35042  * @class Roo.bootstrap.layout.Border
35043  * @extends Roo.bootstrap.layout.Manager
35044  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35045  * please see: examples/bootstrap/nested.html<br><br>
35046  
35047 <b>The container the layout is rendered into can be either the body element or any other element.
35048 If it is not the body element, the container needs to either be an absolute positioned element,
35049 or you will need to add "position:relative" to the css of the container.  You will also need to specify
35050 the container size if it is not the body element.</b>
35051
35052 * @constructor
35053 * Create a new Border
35054 * @param {Object} config Configuration options
35055  */
35056 Roo.bootstrap.layout.Border = function(config){
35057     config = config || {};
35058     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35059     
35060     
35061     
35062     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35063         if(config[region]){
35064             config[region].region = region;
35065             this.addRegion(config[region]);
35066         }
35067     },this);
35068     
35069 };
35070
35071 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35072
35073 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35074     /**
35075      * Creates and adds a new region if it doesn't already exist.
35076      * @param {String} target The target region key (north, south, east, west or center).
35077      * @param {Object} config The regions config object
35078      * @return {BorderLayoutRegion} The new region
35079      */
35080     addRegion : function(config)
35081     {
35082         if(!this.regions[config.region]){
35083             var r = this.factory(config);
35084             this.bindRegion(r);
35085         }
35086         return this.regions[config.region];
35087     },
35088
35089     // private (kinda)
35090     bindRegion : function(r){
35091         this.regions[r.config.region] = r;
35092         
35093         r.on("visibilitychange",    this.layout, this);
35094         r.on("paneladded",          this.layout, this);
35095         r.on("panelremoved",        this.layout, this);
35096         r.on("invalidated",         this.layout, this);
35097         r.on("resized",             this.onRegionResized, this);
35098         r.on("collapsed",           this.onRegionCollapsed, this);
35099         r.on("expanded",            this.onRegionExpanded, this);
35100     },
35101
35102     /**
35103      * Performs a layout update.
35104      */
35105     layout : function()
35106     {
35107         if(this.updating) {
35108             return;
35109         }
35110         
35111         // render all the rebions if they have not been done alreayd?
35112         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35113             if(this.regions[region] && !this.regions[region].bodyEl){
35114                 this.regions[region].onRender(this.el)
35115             }
35116         },this);
35117         
35118         var size = this.getViewSize();
35119         var w = size.width;
35120         var h = size.height;
35121         var centerW = w;
35122         var centerH = h;
35123         var centerY = 0;
35124         var centerX = 0;
35125         //var x = 0, y = 0;
35126
35127         var rs = this.regions;
35128         var north = rs["north"];
35129         var south = rs["south"]; 
35130         var west = rs["west"];
35131         var east = rs["east"];
35132         var center = rs["center"];
35133         //if(this.hideOnLayout){ // not supported anymore
35134             //c.el.setStyle("display", "none");
35135         //}
35136         if(north && north.isVisible()){
35137             var b = north.getBox();
35138             var m = north.getMargins();
35139             b.width = w - (m.left+m.right);
35140             b.x = m.left;
35141             b.y = m.top;
35142             centerY = b.height + b.y + m.bottom;
35143             centerH -= centerY;
35144             north.updateBox(this.safeBox(b));
35145         }
35146         if(south && south.isVisible()){
35147             var b = south.getBox();
35148             var m = south.getMargins();
35149             b.width = w - (m.left+m.right);
35150             b.x = m.left;
35151             var totalHeight = (b.height + m.top + m.bottom);
35152             b.y = h - totalHeight + m.top;
35153             centerH -= totalHeight;
35154             south.updateBox(this.safeBox(b));
35155         }
35156         if(west && west.isVisible()){
35157             var b = west.getBox();
35158             var m = west.getMargins();
35159             b.height = centerH - (m.top+m.bottom);
35160             b.x = m.left;
35161             b.y = centerY + m.top;
35162             var totalWidth = (b.width + m.left + m.right);
35163             centerX += totalWidth;
35164             centerW -= totalWidth;
35165             west.updateBox(this.safeBox(b));
35166         }
35167         if(east && east.isVisible()){
35168             var b = east.getBox();
35169             var m = east.getMargins();
35170             b.height = centerH - (m.top+m.bottom);
35171             var totalWidth = (b.width + m.left + m.right);
35172             b.x = w - totalWidth + m.left;
35173             b.y = centerY + m.top;
35174             centerW -= totalWidth;
35175             east.updateBox(this.safeBox(b));
35176         }
35177         if(center){
35178             var m = center.getMargins();
35179             var centerBox = {
35180                 x: centerX + m.left,
35181                 y: centerY + m.top,
35182                 width: centerW - (m.left+m.right),
35183                 height: centerH - (m.top+m.bottom)
35184             };
35185             //if(this.hideOnLayout){
35186                 //center.el.setStyle("display", "block");
35187             //}
35188             center.updateBox(this.safeBox(centerBox));
35189         }
35190         this.el.repaint();
35191         this.fireEvent("layout", this);
35192     },
35193
35194     // private
35195     safeBox : function(box){
35196         box.width = Math.max(0, box.width);
35197         box.height = Math.max(0, box.height);
35198         return box;
35199     },
35200
35201     /**
35202      * Adds a ContentPanel (or subclass) to this layout.
35203      * @param {String} target The target region key (north, south, east, west or center).
35204      * @param {Roo.ContentPanel} panel The panel to add
35205      * @return {Roo.ContentPanel} The added panel
35206      */
35207     add : function(target, panel){
35208          
35209         target = target.toLowerCase();
35210         return this.regions[target].add(panel);
35211     },
35212
35213     /**
35214      * Remove a ContentPanel (or subclass) to this layout.
35215      * @param {String} target The target region key (north, south, east, west or center).
35216      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35217      * @return {Roo.ContentPanel} The removed panel
35218      */
35219     remove : function(target, panel){
35220         target = target.toLowerCase();
35221         return this.regions[target].remove(panel);
35222     },
35223
35224     /**
35225      * Searches all regions for a panel with the specified id
35226      * @param {String} panelId
35227      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35228      */
35229     findPanel : function(panelId){
35230         var rs = this.regions;
35231         for(var target in rs){
35232             if(typeof rs[target] != "function"){
35233                 var p = rs[target].getPanel(panelId);
35234                 if(p){
35235                     return p;
35236                 }
35237             }
35238         }
35239         return null;
35240     },
35241
35242     /**
35243      * Searches all regions for a panel with the specified id and activates (shows) it.
35244      * @param {String/ContentPanel} panelId The panels id or the panel itself
35245      * @return {Roo.ContentPanel} The shown panel or null
35246      */
35247     showPanel : function(panelId) {
35248       var rs = this.regions;
35249       for(var target in rs){
35250          var r = rs[target];
35251          if(typeof r != "function"){
35252             if(r.hasPanel(panelId)){
35253                return r.showPanel(panelId);
35254             }
35255          }
35256       }
35257       return null;
35258    },
35259
35260    /**
35261      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35262      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35263      */
35264    /*
35265     restoreState : function(provider){
35266         if(!provider){
35267             provider = Roo.state.Manager;
35268         }
35269         var sm = new Roo.LayoutStateManager();
35270         sm.init(this, provider);
35271     },
35272 */
35273  
35274  
35275     /**
35276      * Adds a xtype elements to the layout.
35277      * <pre><code>
35278
35279 layout.addxtype({
35280        xtype : 'ContentPanel',
35281        region: 'west',
35282        items: [ .... ]
35283    }
35284 );
35285
35286 layout.addxtype({
35287         xtype : 'NestedLayoutPanel',
35288         region: 'west',
35289         layout: {
35290            center: { },
35291            west: { }   
35292         },
35293         items : [ ... list of content panels or nested layout panels.. ]
35294    }
35295 );
35296 </code></pre>
35297      * @param {Object} cfg Xtype definition of item to add.
35298      */
35299     addxtype : function(cfg)
35300     {
35301         // basically accepts a pannel...
35302         // can accept a layout region..!?!?
35303         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35304         
35305         
35306         // theory?  children can only be panels??
35307         
35308         //if (!cfg.xtype.match(/Panel$/)) {
35309         //    return false;
35310         //}
35311         var ret = false;
35312         
35313         if (typeof(cfg.region) == 'undefined') {
35314             Roo.log("Failed to add Panel, region was not set");
35315             Roo.log(cfg);
35316             return false;
35317         }
35318         var region = cfg.region;
35319         delete cfg.region;
35320         
35321           
35322         var xitems = [];
35323         if (cfg.items) {
35324             xitems = cfg.items;
35325             delete cfg.items;
35326         }
35327         var nb = false;
35328         
35329         switch(cfg.xtype) 
35330         {
35331             case 'Content':  // ContentPanel (el, cfg)
35332             case 'Scroll':  // ContentPanel (el, cfg)
35333             case 'View': 
35334                 cfg.autoCreate = true;
35335                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35336                 //} else {
35337                 //    var el = this.el.createChild();
35338                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35339                 //}
35340                 
35341                 this.add(region, ret);
35342                 break;
35343             
35344             /*
35345             case 'TreePanel': // our new panel!
35346                 cfg.el = this.el.createChild();
35347                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35348                 this.add(region, ret);
35349                 break;
35350             */
35351             
35352             case 'Nest': 
35353                 // create a new Layout (which is  a Border Layout...
35354                 
35355                 var clayout = cfg.layout;
35356                 clayout.el  = this.el.createChild();
35357                 clayout.items   = clayout.items  || [];
35358                 
35359                 delete cfg.layout;
35360                 
35361                 // replace this exitems with the clayout ones..
35362                 xitems = clayout.items;
35363                  
35364                 // force background off if it's in center...
35365                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35366                     cfg.background = false;
35367                 }
35368                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35369                 
35370                 
35371                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35372                 //console.log('adding nested layout panel '  + cfg.toSource());
35373                 this.add(region, ret);
35374                 nb = {}; /// find first...
35375                 break;
35376             
35377             case 'Grid':
35378                 
35379                 // needs grid and region
35380                 
35381                 //var el = this.getRegion(region).el.createChild();
35382                 /*
35383                  *var el = this.el.createChild();
35384                 // create the grid first...
35385                 cfg.grid.container = el;
35386                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35387                 */
35388                 
35389                 if (region == 'center' && this.active ) {
35390                     cfg.background = false;
35391                 }
35392                 
35393                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35394                 
35395                 this.add(region, ret);
35396                 /*
35397                 if (cfg.background) {
35398                     // render grid on panel activation (if panel background)
35399                     ret.on('activate', function(gp) {
35400                         if (!gp.grid.rendered) {
35401                     //        gp.grid.render(el);
35402                         }
35403                     });
35404                 } else {
35405                   //  cfg.grid.render(el);
35406                 }
35407                 */
35408                 break;
35409            
35410            
35411             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35412                 // it was the old xcomponent building that caused this before.
35413                 // espeically if border is the top element in the tree.
35414                 ret = this;
35415                 break; 
35416                 
35417                     
35418                 
35419                 
35420                 
35421             default:
35422                 /*
35423                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35424                     
35425                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35426                     this.add(region, ret);
35427                 } else {
35428                 */
35429                     Roo.log(cfg);
35430                     throw "Can not add '" + cfg.xtype + "' to Border";
35431                     return null;
35432              
35433                                 
35434              
35435         }
35436         this.beginUpdate();
35437         // add children..
35438         var region = '';
35439         var abn = {};
35440         Roo.each(xitems, function(i)  {
35441             region = nb && i.region ? i.region : false;
35442             
35443             var add = ret.addxtype(i);
35444            
35445             if (region) {
35446                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35447                 if (!i.background) {
35448                     abn[region] = nb[region] ;
35449                 }
35450             }
35451             
35452         });
35453         this.endUpdate();
35454
35455         // make the last non-background panel active..
35456         //if (nb) { Roo.log(abn); }
35457         if (nb) {
35458             
35459             for(var r in abn) {
35460                 region = this.getRegion(r);
35461                 if (region) {
35462                     // tried using nb[r], but it does not work..
35463                      
35464                     region.showPanel(abn[r]);
35465                    
35466                 }
35467             }
35468         }
35469         return ret;
35470         
35471     },
35472     
35473     
35474 // private
35475     factory : function(cfg)
35476     {
35477         
35478         var validRegions = Roo.bootstrap.layout.Border.regions;
35479
35480         var target = cfg.region;
35481         cfg.mgr = this;
35482         
35483         var r = Roo.bootstrap.layout;
35484         Roo.log(target);
35485         switch(target){
35486             case "north":
35487                 return new r.North(cfg);
35488             case "south":
35489                 return new r.South(cfg);
35490             case "east":
35491                 return new r.East(cfg);
35492             case "west":
35493                 return new r.West(cfg);
35494             case "center":
35495                 return new r.Center(cfg);
35496         }
35497         throw 'Layout region "'+target+'" not supported.';
35498     }
35499     
35500     
35501 });
35502  /*
35503  * Based on:
35504  * Ext JS Library 1.1.1
35505  * Copyright(c) 2006-2007, Ext JS, LLC.
35506  *
35507  * Originally Released Under LGPL - original licence link has changed is not relivant.
35508  *
35509  * Fork - LGPL
35510  * <script type="text/javascript">
35511  */
35512  
35513 /**
35514  * @class Roo.bootstrap.layout.Basic
35515  * @extends Roo.util.Observable
35516  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35517  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35518  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35519  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35520  * @cfg {string}   region  the region that it inhabits..
35521  * @cfg {bool}   skipConfig skip config?
35522  * 
35523
35524  */
35525 Roo.bootstrap.layout.Basic = function(config){
35526     
35527     this.mgr = config.mgr;
35528     
35529     this.position = config.region;
35530     
35531     var skipConfig = config.skipConfig;
35532     
35533     this.events = {
35534         /**
35535          * @scope Roo.BasicLayoutRegion
35536          */
35537         
35538         /**
35539          * @event beforeremove
35540          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35541          * @param {Roo.LayoutRegion} this
35542          * @param {Roo.ContentPanel} panel The panel
35543          * @param {Object} e The cancel event object
35544          */
35545         "beforeremove" : true,
35546         /**
35547          * @event invalidated
35548          * Fires when the layout for this region is changed.
35549          * @param {Roo.LayoutRegion} this
35550          */
35551         "invalidated" : true,
35552         /**
35553          * @event visibilitychange
35554          * Fires when this region is shown or hidden 
35555          * @param {Roo.LayoutRegion} this
35556          * @param {Boolean} visibility true or false
35557          */
35558         "visibilitychange" : true,
35559         /**
35560          * @event paneladded
35561          * Fires when a panel is added. 
35562          * @param {Roo.LayoutRegion} this
35563          * @param {Roo.ContentPanel} panel The panel
35564          */
35565         "paneladded" : true,
35566         /**
35567          * @event panelremoved
35568          * Fires when a panel is removed. 
35569          * @param {Roo.LayoutRegion} this
35570          * @param {Roo.ContentPanel} panel The panel
35571          */
35572         "panelremoved" : true,
35573         /**
35574          * @event beforecollapse
35575          * Fires when this region before collapse.
35576          * @param {Roo.LayoutRegion} this
35577          */
35578         "beforecollapse" : true,
35579         /**
35580          * @event collapsed
35581          * Fires when this region is collapsed.
35582          * @param {Roo.LayoutRegion} this
35583          */
35584         "collapsed" : true,
35585         /**
35586          * @event expanded
35587          * Fires when this region is expanded.
35588          * @param {Roo.LayoutRegion} this
35589          */
35590         "expanded" : true,
35591         /**
35592          * @event slideshow
35593          * Fires when this region is slid into view.
35594          * @param {Roo.LayoutRegion} this
35595          */
35596         "slideshow" : true,
35597         /**
35598          * @event slidehide
35599          * Fires when this region slides out of view. 
35600          * @param {Roo.LayoutRegion} this
35601          */
35602         "slidehide" : true,
35603         /**
35604          * @event panelactivated
35605          * Fires when a panel is activated. 
35606          * @param {Roo.LayoutRegion} this
35607          * @param {Roo.ContentPanel} panel The activated panel
35608          */
35609         "panelactivated" : true,
35610         /**
35611          * @event resized
35612          * Fires when the user resizes this region. 
35613          * @param {Roo.LayoutRegion} this
35614          * @param {Number} newSize The new size (width for east/west, height for north/south)
35615          */
35616         "resized" : true
35617     };
35618     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35619     this.panels = new Roo.util.MixedCollection();
35620     this.panels.getKey = this.getPanelId.createDelegate(this);
35621     this.box = null;
35622     this.activePanel = null;
35623     // ensure listeners are added...
35624     
35625     if (config.listeners || config.events) {
35626         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35627             listeners : config.listeners || {},
35628             events : config.events || {}
35629         });
35630     }
35631     
35632     if(skipConfig !== true){
35633         this.applyConfig(config);
35634     }
35635 };
35636
35637 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35638 {
35639     getPanelId : function(p){
35640         return p.getId();
35641     },
35642     
35643     applyConfig : function(config){
35644         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35645         this.config = config;
35646         
35647     },
35648     
35649     /**
35650      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35651      * the width, for horizontal (north, south) the height.
35652      * @param {Number} newSize The new width or height
35653      */
35654     resizeTo : function(newSize){
35655         var el = this.el ? this.el :
35656                  (this.activePanel ? this.activePanel.getEl() : null);
35657         if(el){
35658             switch(this.position){
35659                 case "east":
35660                 case "west":
35661                     el.setWidth(newSize);
35662                     this.fireEvent("resized", this, newSize);
35663                 break;
35664                 case "north":
35665                 case "south":
35666                     el.setHeight(newSize);
35667                     this.fireEvent("resized", this, newSize);
35668                 break;                
35669             }
35670         }
35671     },
35672     
35673     getBox : function(){
35674         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35675     },
35676     
35677     getMargins : function(){
35678         return this.margins;
35679     },
35680     
35681     updateBox : function(box){
35682         this.box = box;
35683         var el = this.activePanel.getEl();
35684         el.dom.style.left = box.x + "px";
35685         el.dom.style.top = box.y + "px";
35686         this.activePanel.setSize(box.width, box.height);
35687     },
35688     
35689     /**
35690      * Returns the container element for this region.
35691      * @return {Roo.Element}
35692      */
35693     getEl : function(){
35694         return this.activePanel;
35695     },
35696     
35697     /**
35698      * Returns true if this region is currently visible.
35699      * @return {Boolean}
35700      */
35701     isVisible : function(){
35702         return this.activePanel ? true : false;
35703     },
35704     
35705     setActivePanel : function(panel){
35706         panel = this.getPanel(panel);
35707         if(this.activePanel && this.activePanel != panel){
35708             this.activePanel.setActiveState(false);
35709             this.activePanel.getEl().setLeftTop(-10000,-10000);
35710         }
35711         this.activePanel = panel;
35712         panel.setActiveState(true);
35713         if(this.box){
35714             panel.setSize(this.box.width, this.box.height);
35715         }
35716         this.fireEvent("panelactivated", this, panel);
35717         this.fireEvent("invalidated");
35718     },
35719     
35720     /**
35721      * Show the specified panel.
35722      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35723      * @return {Roo.ContentPanel} The shown panel or null
35724      */
35725     showPanel : function(panel){
35726         panel = this.getPanel(panel);
35727         if(panel){
35728             this.setActivePanel(panel);
35729         }
35730         return panel;
35731     },
35732     
35733     /**
35734      * Get the active panel for this region.
35735      * @return {Roo.ContentPanel} The active panel or null
35736      */
35737     getActivePanel : function(){
35738         return this.activePanel;
35739     },
35740     
35741     /**
35742      * Add the passed ContentPanel(s)
35743      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35744      * @return {Roo.ContentPanel} The panel added (if only one was added)
35745      */
35746     add : function(panel){
35747         if(arguments.length > 1){
35748             for(var i = 0, len = arguments.length; i < len; i++) {
35749                 this.add(arguments[i]);
35750             }
35751             return null;
35752         }
35753         if(this.hasPanel(panel)){
35754             this.showPanel(panel);
35755             return panel;
35756         }
35757         var el = panel.getEl();
35758         if(el.dom.parentNode != this.mgr.el.dom){
35759             this.mgr.el.dom.appendChild(el.dom);
35760         }
35761         if(panel.setRegion){
35762             panel.setRegion(this);
35763         }
35764         this.panels.add(panel);
35765         el.setStyle("position", "absolute");
35766         if(!panel.background){
35767             this.setActivePanel(panel);
35768             if(this.config.initialSize && this.panels.getCount()==1){
35769                 this.resizeTo(this.config.initialSize);
35770             }
35771         }
35772         this.fireEvent("paneladded", this, panel);
35773         return panel;
35774     },
35775     
35776     /**
35777      * Returns true if the panel is in this region.
35778      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35779      * @return {Boolean}
35780      */
35781     hasPanel : function(panel){
35782         if(typeof panel == "object"){ // must be panel obj
35783             panel = panel.getId();
35784         }
35785         return this.getPanel(panel) ? true : false;
35786     },
35787     
35788     /**
35789      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35790      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35791      * @param {Boolean} preservePanel Overrides the config preservePanel option
35792      * @return {Roo.ContentPanel} The panel that was removed
35793      */
35794     remove : function(panel, preservePanel){
35795         panel = this.getPanel(panel);
35796         if(!panel){
35797             return null;
35798         }
35799         var e = {};
35800         this.fireEvent("beforeremove", this, panel, e);
35801         if(e.cancel === true){
35802             return null;
35803         }
35804         var panelId = panel.getId();
35805         this.panels.removeKey(panelId);
35806         return panel;
35807     },
35808     
35809     /**
35810      * Returns the panel specified or null if it's not in this region.
35811      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35812      * @return {Roo.ContentPanel}
35813      */
35814     getPanel : function(id){
35815         if(typeof id == "object"){ // must be panel obj
35816             return id;
35817         }
35818         return this.panels.get(id);
35819     },
35820     
35821     /**
35822      * Returns this regions position (north/south/east/west/center).
35823      * @return {String} 
35824      */
35825     getPosition: function(){
35826         return this.position;    
35827     }
35828 });/*
35829  * Based on:
35830  * Ext JS Library 1.1.1
35831  * Copyright(c) 2006-2007, Ext JS, LLC.
35832  *
35833  * Originally Released Under LGPL - original licence link has changed is not relivant.
35834  *
35835  * Fork - LGPL
35836  * <script type="text/javascript">
35837  */
35838  
35839 /**
35840  * @class Roo.bootstrap.layout.Region
35841  * @extends Roo.bootstrap.layout.Basic
35842  * This class represents a region in a layout manager.
35843  
35844  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35845  * @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})
35846  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35847  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35848  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35849  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35850  * @cfg {String}    title           The title for the region (overrides panel titles)
35851  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35852  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35853  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35854  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35855  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35856  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35857  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35858  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35859  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35860  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35861
35862  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35863  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35864  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35865  * @cfg {Number}    width           For East/West panels
35866  * @cfg {Number}    height          For North/South panels
35867  * @cfg {Boolean}   split           To show the splitter
35868  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35869  * 
35870  * @cfg {string}   cls             Extra CSS classes to add to region
35871  * 
35872  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35873  * @cfg {string}   region  the region that it inhabits..
35874  *
35875
35876  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35877  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35878
35879  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35880  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35881  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35882  */
35883 Roo.bootstrap.layout.Region = function(config)
35884 {
35885     this.applyConfig(config);
35886
35887     var mgr = config.mgr;
35888     var pos = config.region;
35889     config.skipConfig = true;
35890     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35891     
35892     if (mgr.el) {
35893         this.onRender(mgr.el);   
35894     }
35895      
35896     this.visible = true;
35897     this.collapsed = false;
35898     this.unrendered_panels = [];
35899 };
35900
35901 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35902
35903     position: '', // set by wrapper (eg. north/south etc..)
35904     unrendered_panels : null,  // unrendered panels.
35905     createBody : function(){
35906         /** This region's body element 
35907         * @type Roo.Element */
35908         this.bodyEl = this.el.createChild({
35909                 tag: "div",
35910                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35911         });
35912     },
35913
35914     onRender: function(ctr, pos)
35915     {
35916         var dh = Roo.DomHelper;
35917         /** This region's container element 
35918         * @type Roo.Element */
35919         this.el = dh.append(ctr.dom, {
35920                 tag: "div",
35921                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35922             }, true);
35923         /** This region's title element 
35924         * @type Roo.Element */
35925     
35926         this.titleEl = dh.append(this.el.dom,
35927             {
35928                     tag: "div",
35929                     unselectable: "on",
35930                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35931                     children:[
35932                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35933                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35934                     ]}, true);
35935         
35936         this.titleEl.enableDisplayMode();
35937         /** This region's title text element 
35938         * @type HTMLElement */
35939         this.titleTextEl = this.titleEl.dom.firstChild;
35940         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35941         /*
35942         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35943         this.closeBtn.enableDisplayMode();
35944         this.closeBtn.on("click", this.closeClicked, this);
35945         this.closeBtn.hide();
35946     */
35947         this.createBody(this.config);
35948         if(this.config.hideWhenEmpty){
35949             this.hide();
35950             this.on("paneladded", this.validateVisibility, this);
35951             this.on("panelremoved", this.validateVisibility, this);
35952         }
35953         if(this.autoScroll){
35954             this.bodyEl.setStyle("overflow", "auto");
35955         }else{
35956             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35957         }
35958         //if(c.titlebar !== false){
35959             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35960                 this.titleEl.hide();
35961             }else{
35962                 this.titleEl.show();
35963                 if(this.config.title){
35964                     this.titleTextEl.innerHTML = this.config.title;
35965                 }
35966             }
35967         //}
35968         if(this.config.collapsed){
35969             this.collapse(true);
35970         }
35971         if(this.config.hidden){
35972             this.hide();
35973         }
35974         
35975         if (this.unrendered_panels && this.unrendered_panels.length) {
35976             for (var i =0;i< this.unrendered_panels.length; i++) {
35977                 this.add(this.unrendered_panels[i]);
35978             }
35979             this.unrendered_panels = null;
35980             
35981         }
35982         
35983     },
35984     
35985     applyConfig : function(c)
35986     {
35987         /*
35988          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35989             var dh = Roo.DomHelper;
35990             if(c.titlebar !== false){
35991                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35992                 this.collapseBtn.on("click", this.collapse, this);
35993                 this.collapseBtn.enableDisplayMode();
35994                 /*
35995                 if(c.showPin === true || this.showPin){
35996                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35997                     this.stickBtn.enableDisplayMode();
35998                     this.stickBtn.on("click", this.expand, this);
35999                     this.stickBtn.hide();
36000                 }
36001                 
36002             }
36003             */
36004             /** This region's collapsed element
36005             * @type Roo.Element */
36006             /*
36007              *
36008             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36009                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36010             ]}, true);
36011             
36012             if(c.floatable !== false){
36013                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36014                this.collapsedEl.on("click", this.collapseClick, this);
36015             }
36016
36017             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36018                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36019                    id: "message", unselectable: "on", style:{"float":"left"}});
36020                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36021              }
36022             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36023             this.expandBtn.on("click", this.expand, this);
36024             
36025         }
36026         
36027         if(this.collapseBtn){
36028             this.collapseBtn.setVisible(c.collapsible == true);
36029         }
36030         
36031         this.cmargins = c.cmargins || this.cmargins ||
36032                          (this.position == "west" || this.position == "east" ?
36033                              {top: 0, left: 2, right:2, bottom: 0} :
36034                              {top: 2, left: 0, right:0, bottom: 2});
36035         */
36036         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36037         
36038         
36039         this.bottomTabs = c.tabPosition != "top";
36040         
36041         this.autoScroll = c.autoScroll || false;
36042         
36043         
36044        
36045         
36046         this.duration = c.duration || .30;
36047         this.slideDuration = c.slideDuration || .45;
36048         this.config = c;
36049        
36050     },
36051     /**
36052      * Returns true if this region is currently visible.
36053      * @return {Boolean}
36054      */
36055     isVisible : function(){
36056         return this.visible;
36057     },
36058
36059     /**
36060      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36061      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36062      */
36063     //setCollapsedTitle : function(title){
36064     //    title = title || "&#160;";
36065      //   if(this.collapsedTitleTextEl){
36066       //      this.collapsedTitleTextEl.innerHTML = title;
36067        // }
36068     //},
36069
36070     getBox : function(){
36071         var b;
36072       //  if(!this.collapsed){
36073             b = this.el.getBox(false, true);
36074        // }else{
36075           //  b = this.collapsedEl.getBox(false, true);
36076         //}
36077         return b;
36078     },
36079
36080     getMargins : function(){
36081         return this.margins;
36082         //return this.collapsed ? this.cmargins : this.margins;
36083     },
36084 /*
36085     highlight : function(){
36086         this.el.addClass("x-layout-panel-dragover");
36087     },
36088
36089     unhighlight : function(){
36090         this.el.removeClass("x-layout-panel-dragover");
36091     },
36092 */
36093     updateBox : function(box)
36094     {
36095         if (!this.bodyEl) {
36096             return; // not rendered yet..
36097         }
36098         
36099         this.box = box;
36100         if(!this.collapsed){
36101             this.el.dom.style.left = box.x + "px";
36102             this.el.dom.style.top = box.y + "px";
36103             this.updateBody(box.width, box.height);
36104         }else{
36105             this.collapsedEl.dom.style.left = box.x + "px";
36106             this.collapsedEl.dom.style.top = box.y + "px";
36107             this.collapsedEl.setSize(box.width, box.height);
36108         }
36109         if(this.tabs){
36110             this.tabs.autoSizeTabs();
36111         }
36112     },
36113
36114     updateBody : function(w, h)
36115     {
36116         if(w !== null){
36117             this.el.setWidth(w);
36118             w -= this.el.getBorderWidth("rl");
36119             if(this.config.adjustments){
36120                 w += this.config.adjustments[0];
36121             }
36122         }
36123         if(h !== null && h > 0){
36124             this.el.setHeight(h);
36125             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36126             h -= this.el.getBorderWidth("tb");
36127             if(this.config.adjustments){
36128                 h += this.config.adjustments[1];
36129             }
36130             this.bodyEl.setHeight(h);
36131             if(this.tabs){
36132                 h = this.tabs.syncHeight(h);
36133             }
36134         }
36135         if(this.panelSize){
36136             w = w !== null ? w : this.panelSize.width;
36137             h = h !== null ? h : this.panelSize.height;
36138         }
36139         if(this.activePanel){
36140             var el = this.activePanel.getEl();
36141             w = w !== null ? w : el.getWidth();
36142             h = h !== null ? h : el.getHeight();
36143             this.panelSize = {width: w, height: h};
36144             this.activePanel.setSize(w, h);
36145         }
36146         if(Roo.isIE && this.tabs){
36147             this.tabs.el.repaint();
36148         }
36149     },
36150
36151     /**
36152      * Returns the container element for this region.
36153      * @return {Roo.Element}
36154      */
36155     getEl : function(){
36156         return this.el;
36157     },
36158
36159     /**
36160      * Hides this region.
36161      */
36162     hide : function(){
36163         //if(!this.collapsed){
36164             this.el.dom.style.left = "-2000px";
36165             this.el.hide();
36166         //}else{
36167          //   this.collapsedEl.dom.style.left = "-2000px";
36168          //   this.collapsedEl.hide();
36169        // }
36170         this.visible = false;
36171         this.fireEvent("visibilitychange", this, false);
36172     },
36173
36174     /**
36175      * Shows this region if it was previously hidden.
36176      */
36177     show : function(){
36178         //if(!this.collapsed){
36179             this.el.show();
36180         //}else{
36181         //    this.collapsedEl.show();
36182        // }
36183         this.visible = true;
36184         this.fireEvent("visibilitychange", this, true);
36185     },
36186 /*
36187     closeClicked : function(){
36188         if(this.activePanel){
36189             this.remove(this.activePanel);
36190         }
36191     },
36192
36193     collapseClick : function(e){
36194         if(this.isSlid){
36195            e.stopPropagation();
36196            this.slideIn();
36197         }else{
36198            e.stopPropagation();
36199            this.slideOut();
36200         }
36201     },
36202 */
36203     /**
36204      * Collapses this region.
36205      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36206      */
36207     /*
36208     collapse : function(skipAnim, skipCheck = false){
36209         if(this.collapsed) {
36210             return;
36211         }
36212         
36213         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36214             
36215             this.collapsed = true;
36216             if(this.split){
36217                 this.split.el.hide();
36218             }
36219             if(this.config.animate && skipAnim !== true){
36220                 this.fireEvent("invalidated", this);
36221                 this.animateCollapse();
36222             }else{
36223                 this.el.setLocation(-20000,-20000);
36224                 this.el.hide();
36225                 this.collapsedEl.show();
36226                 this.fireEvent("collapsed", this);
36227                 this.fireEvent("invalidated", this);
36228             }
36229         }
36230         
36231     },
36232 */
36233     animateCollapse : function(){
36234         // overridden
36235     },
36236
36237     /**
36238      * Expands this region if it was previously collapsed.
36239      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36240      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36241      */
36242     /*
36243     expand : function(e, skipAnim){
36244         if(e) {
36245             e.stopPropagation();
36246         }
36247         if(!this.collapsed || this.el.hasActiveFx()) {
36248             return;
36249         }
36250         if(this.isSlid){
36251             this.afterSlideIn();
36252             skipAnim = true;
36253         }
36254         this.collapsed = false;
36255         if(this.config.animate && skipAnim !== true){
36256             this.animateExpand();
36257         }else{
36258             this.el.show();
36259             if(this.split){
36260                 this.split.el.show();
36261             }
36262             this.collapsedEl.setLocation(-2000,-2000);
36263             this.collapsedEl.hide();
36264             this.fireEvent("invalidated", this);
36265             this.fireEvent("expanded", this);
36266         }
36267     },
36268 */
36269     animateExpand : function(){
36270         // overridden
36271     },
36272
36273     initTabs : function()
36274     {
36275         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36276         
36277         var ts = new Roo.bootstrap.panel.Tabs({
36278                 el: this.bodyEl.dom,
36279                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36280                 disableTooltips: this.config.disableTabTips,
36281                 toolbar : this.config.toolbar
36282             });
36283         
36284         if(this.config.hideTabs){
36285             ts.stripWrap.setDisplayed(false);
36286         }
36287         this.tabs = ts;
36288         ts.resizeTabs = this.config.resizeTabs === true;
36289         ts.minTabWidth = this.config.minTabWidth || 40;
36290         ts.maxTabWidth = this.config.maxTabWidth || 250;
36291         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36292         ts.monitorResize = false;
36293         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36294         ts.bodyEl.addClass('roo-layout-tabs-body');
36295         this.panels.each(this.initPanelAsTab, this);
36296     },
36297
36298     initPanelAsTab : function(panel){
36299         var ti = this.tabs.addTab(
36300             panel.getEl().id,
36301             panel.getTitle(),
36302             null,
36303             this.config.closeOnTab && panel.isClosable(),
36304             panel.tpl
36305         );
36306         if(panel.tabTip !== undefined){
36307             ti.setTooltip(panel.tabTip);
36308         }
36309         ti.on("activate", function(){
36310               this.setActivePanel(panel);
36311         }, this);
36312         
36313         if(this.config.closeOnTab){
36314             ti.on("beforeclose", function(t, e){
36315                 e.cancel = true;
36316                 this.remove(panel);
36317             }, this);
36318         }
36319         
36320         panel.tabItem = ti;
36321         
36322         return ti;
36323     },
36324
36325     updatePanelTitle : function(panel, title)
36326     {
36327         if(this.activePanel == panel){
36328             this.updateTitle(title);
36329         }
36330         if(this.tabs){
36331             var ti = this.tabs.getTab(panel.getEl().id);
36332             ti.setText(title);
36333             if(panel.tabTip !== undefined){
36334                 ti.setTooltip(panel.tabTip);
36335             }
36336         }
36337     },
36338
36339     updateTitle : function(title){
36340         if(this.titleTextEl && !this.config.title){
36341             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36342         }
36343     },
36344
36345     setActivePanel : function(panel)
36346     {
36347         panel = this.getPanel(panel);
36348         if(this.activePanel && this.activePanel != panel){
36349             if(this.activePanel.setActiveState(false) === false){
36350                 return;
36351             }
36352         }
36353         this.activePanel = panel;
36354         panel.setActiveState(true);
36355         if(this.panelSize){
36356             panel.setSize(this.panelSize.width, this.panelSize.height);
36357         }
36358         if(this.closeBtn){
36359             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36360         }
36361         this.updateTitle(panel.getTitle());
36362         if(this.tabs){
36363             this.fireEvent("invalidated", this);
36364         }
36365         this.fireEvent("panelactivated", this, panel);
36366     },
36367
36368     /**
36369      * Shows the specified panel.
36370      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36371      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36372      */
36373     showPanel : function(panel)
36374     {
36375         panel = this.getPanel(panel);
36376         if(panel){
36377             if(this.tabs){
36378                 var tab = this.tabs.getTab(panel.getEl().id);
36379                 if(tab.isHidden()){
36380                     this.tabs.unhideTab(tab.id);
36381                 }
36382                 tab.activate();
36383             }else{
36384                 this.setActivePanel(panel);
36385             }
36386         }
36387         return panel;
36388     },
36389
36390     /**
36391      * Get the active panel for this region.
36392      * @return {Roo.ContentPanel} The active panel or null
36393      */
36394     getActivePanel : function(){
36395         return this.activePanel;
36396     },
36397
36398     validateVisibility : function(){
36399         if(this.panels.getCount() < 1){
36400             this.updateTitle("&#160;");
36401             this.closeBtn.hide();
36402             this.hide();
36403         }else{
36404             if(!this.isVisible()){
36405                 this.show();
36406             }
36407         }
36408     },
36409
36410     /**
36411      * Adds the passed ContentPanel(s) to this region.
36412      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36413      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36414      */
36415     add : function(panel)
36416     {
36417         if(arguments.length > 1){
36418             for(var i = 0, len = arguments.length; i < len; i++) {
36419                 this.add(arguments[i]);
36420             }
36421             return null;
36422         }
36423         
36424         // if we have not been rendered yet, then we can not really do much of this..
36425         if (!this.bodyEl) {
36426             this.unrendered_panels.push(panel);
36427             return panel;
36428         }
36429         
36430         
36431         
36432         
36433         if(this.hasPanel(panel)){
36434             this.showPanel(panel);
36435             return panel;
36436         }
36437         panel.setRegion(this);
36438         this.panels.add(panel);
36439        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36440             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36441             // and hide them... ???
36442             this.bodyEl.dom.appendChild(panel.getEl().dom);
36443             if(panel.background !== true){
36444                 this.setActivePanel(panel);
36445             }
36446             this.fireEvent("paneladded", this, panel);
36447             return panel;
36448         }
36449         */
36450         if(!this.tabs){
36451             this.initTabs();
36452         }else{
36453             this.initPanelAsTab(panel);
36454         }
36455         
36456         
36457         if(panel.background !== true){
36458             this.tabs.activate(panel.getEl().id);
36459         }
36460         this.fireEvent("paneladded", this, panel);
36461         return panel;
36462     },
36463
36464     /**
36465      * Hides the tab for the specified panel.
36466      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36467      */
36468     hidePanel : function(panel){
36469         if(this.tabs && (panel = this.getPanel(panel))){
36470             this.tabs.hideTab(panel.getEl().id);
36471         }
36472     },
36473
36474     /**
36475      * Unhides the tab for a previously hidden panel.
36476      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36477      */
36478     unhidePanel : function(panel){
36479         if(this.tabs && (panel = this.getPanel(panel))){
36480             this.tabs.unhideTab(panel.getEl().id);
36481         }
36482     },
36483
36484     clearPanels : function(){
36485         while(this.panels.getCount() > 0){
36486              this.remove(this.panels.first());
36487         }
36488     },
36489
36490     /**
36491      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36492      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36493      * @param {Boolean} preservePanel Overrides the config preservePanel option
36494      * @return {Roo.ContentPanel} The panel that was removed
36495      */
36496     remove : function(panel, preservePanel)
36497     {
36498         panel = this.getPanel(panel);
36499         if(!panel){
36500             return null;
36501         }
36502         var e = {};
36503         this.fireEvent("beforeremove", this, panel, e);
36504         if(e.cancel === true){
36505             return null;
36506         }
36507         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36508         var panelId = panel.getId();
36509         this.panels.removeKey(panelId);
36510         if(preservePanel){
36511             document.body.appendChild(panel.getEl().dom);
36512         }
36513         if(this.tabs){
36514             this.tabs.removeTab(panel.getEl().id);
36515         }else if (!preservePanel){
36516             this.bodyEl.dom.removeChild(panel.getEl().dom);
36517         }
36518         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36519             var p = this.panels.first();
36520             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36521             tempEl.appendChild(p.getEl().dom);
36522             this.bodyEl.update("");
36523             this.bodyEl.dom.appendChild(p.getEl().dom);
36524             tempEl = null;
36525             this.updateTitle(p.getTitle());
36526             this.tabs = null;
36527             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36528             this.setActivePanel(p);
36529         }
36530         panel.setRegion(null);
36531         if(this.activePanel == panel){
36532             this.activePanel = null;
36533         }
36534         if(this.config.autoDestroy !== false && preservePanel !== true){
36535             try{panel.destroy();}catch(e){}
36536         }
36537         this.fireEvent("panelremoved", this, panel);
36538         return panel;
36539     },
36540
36541     /**
36542      * Returns the TabPanel component used by this region
36543      * @return {Roo.TabPanel}
36544      */
36545     getTabs : function(){
36546         return this.tabs;
36547     },
36548
36549     createTool : function(parentEl, className){
36550         var btn = Roo.DomHelper.append(parentEl, {
36551             tag: "div",
36552             cls: "x-layout-tools-button",
36553             children: [ {
36554                 tag: "div",
36555                 cls: "roo-layout-tools-button-inner " + className,
36556                 html: "&#160;"
36557             }]
36558         }, true);
36559         btn.addClassOnOver("roo-layout-tools-button-over");
36560         return btn;
36561     }
36562 });/*
36563  * Based on:
36564  * Ext JS Library 1.1.1
36565  * Copyright(c) 2006-2007, Ext JS, LLC.
36566  *
36567  * Originally Released Under LGPL - original licence link has changed is not relivant.
36568  *
36569  * Fork - LGPL
36570  * <script type="text/javascript">
36571  */
36572  
36573
36574
36575 /**
36576  * @class Roo.SplitLayoutRegion
36577  * @extends Roo.LayoutRegion
36578  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36579  */
36580 Roo.bootstrap.layout.Split = function(config){
36581     this.cursor = config.cursor;
36582     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36583 };
36584
36585 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36586 {
36587     splitTip : "Drag to resize.",
36588     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36589     useSplitTips : false,
36590
36591     applyConfig : function(config){
36592         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36593     },
36594     
36595     onRender : function(ctr,pos) {
36596         
36597         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36598         if(!this.config.split){
36599             return;
36600         }
36601         if(!this.split){
36602             
36603             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36604                             tag: "div",
36605                             id: this.el.id + "-split",
36606                             cls: "roo-layout-split roo-layout-split-"+this.position,
36607                             html: "&#160;"
36608             });
36609             /** The SplitBar for this region 
36610             * @type Roo.SplitBar */
36611             // does not exist yet...
36612             Roo.log([this.position, this.orientation]);
36613             
36614             this.split = new Roo.bootstrap.SplitBar({
36615                 dragElement : splitEl,
36616                 resizingElement: this.el,
36617                 orientation : this.orientation
36618             });
36619             
36620             this.split.on("moved", this.onSplitMove, this);
36621             this.split.useShim = this.config.useShim === true;
36622             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36623             if(this.useSplitTips){
36624                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36625             }
36626             //if(config.collapsible){
36627             //    this.split.el.on("dblclick", this.collapse,  this);
36628             //}
36629         }
36630         if(typeof this.config.minSize != "undefined"){
36631             this.split.minSize = this.config.minSize;
36632         }
36633         if(typeof this.config.maxSize != "undefined"){
36634             this.split.maxSize = this.config.maxSize;
36635         }
36636         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36637             this.hideSplitter();
36638         }
36639         
36640     },
36641
36642     getHMaxSize : function(){
36643          var cmax = this.config.maxSize || 10000;
36644          var center = this.mgr.getRegion("center");
36645          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36646     },
36647
36648     getVMaxSize : function(){
36649          var cmax = this.config.maxSize || 10000;
36650          var center = this.mgr.getRegion("center");
36651          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36652     },
36653
36654     onSplitMove : function(split, newSize){
36655         this.fireEvent("resized", this, newSize);
36656     },
36657     
36658     /** 
36659      * Returns the {@link Roo.SplitBar} for this region.
36660      * @return {Roo.SplitBar}
36661      */
36662     getSplitBar : function(){
36663         return this.split;
36664     },
36665     
36666     hide : function(){
36667         this.hideSplitter();
36668         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36669     },
36670
36671     hideSplitter : function(){
36672         if(this.split){
36673             this.split.el.setLocation(-2000,-2000);
36674             this.split.el.hide();
36675         }
36676     },
36677
36678     show : function(){
36679         if(this.split){
36680             this.split.el.show();
36681         }
36682         Roo.bootstrap.layout.Split.superclass.show.call(this);
36683     },
36684     
36685     beforeSlide: function(){
36686         if(Roo.isGecko){// firefox overflow auto bug workaround
36687             this.bodyEl.clip();
36688             if(this.tabs) {
36689                 this.tabs.bodyEl.clip();
36690             }
36691             if(this.activePanel){
36692                 this.activePanel.getEl().clip();
36693                 
36694                 if(this.activePanel.beforeSlide){
36695                     this.activePanel.beforeSlide();
36696                 }
36697             }
36698         }
36699     },
36700     
36701     afterSlide : function(){
36702         if(Roo.isGecko){// firefox overflow auto bug workaround
36703             this.bodyEl.unclip();
36704             if(this.tabs) {
36705                 this.tabs.bodyEl.unclip();
36706             }
36707             if(this.activePanel){
36708                 this.activePanel.getEl().unclip();
36709                 if(this.activePanel.afterSlide){
36710                     this.activePanel.afterSlide();
36711                 }
36712             }
36713         }
36714     },
36715
36716     initAutoHide : function(){
36717         if(this.autoHide !== false){
36718             if(!this.autoHideHd){
36719                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36720                 this.autoHideHd = {
36721                     "mouseout": function(e){
36722                         if(!e.within(this.el, true)){
36723                             st.delay(500);
36724                         }
36725                     },
36726                     "mouseover" : function(e){
36727                         st.cancel();
36728                     },
36729                     scope : this
36730                 };
36731             }
36732             this.el.on(this.autoHideHd);
36733         }
36734     },
36735
36736     clearAutoHide : function(){
36737         if(this.autoHide !== false){
36738             this.el.un("mouseout", this.autoHideHd.mouseout);
36739             this.el.un("mouseover", this.autoHideHd.mouseover);
36740         }
36741     },
36742
36743     clearMonitor : function(){
36744         Roo.get(document).un("click", this.slideInIf, this);
36745     },
36746
36747     // these names are backwards but not changed for compat
36748     slideOut : function(){
36749         if(this.isSlid || this.el.hasActiveFx()){
36750             return;
36751         }
36752         this.isSlid = true;
36753         if(this.collapseBtn){
36754             this.collapseBtn.hide();
36755         }
36756         this.closeBtnState = this.closeBtn.getStyle('display');
36757         this.closeBtn.hide();
36758         if(this.stickBtn){
36759             this.stickBtn.show();
36760         }
36761         this.el.show();
36762         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36763         this.beforeSlide();
36764         this.el.setStyle("z-index", 10001);
36765         this.el.slideIn(this.getSlideAnchor(), {
36766             callback: function(){
36767                 this.afterSlide();
36768                 this.initAutoHide();
36769                 Roo.get(document).on("click", this.slideInIf, this);
36770                 this.fireEvent("slideshow", this);
36771             },
36772             scope: this,
36773             block: true
36774         });
36775     },
36776
36777     afterSlideIn : function(){
36778         this.clearAutoHide();
36779         this.isSlid = false;
36780         this.clearMonitor();
36781         this.el.setStyle("z-index", "");
36782         if(this.collapseBtn){
36783             this.collapseBtn.show();
36784         }
36785         this.closeBtn.setStyle('display', this.closeBtnState);
36786         if(this.stickBtn){
36787             this.stickBtn.hide();
36788         }
36789         this.fireEvent("slidehide", this);
36790     },
36791
36792     slideIn : function(cb){
36793         if(!this.isSlid || this.el.hasActiveFx()){
36794             Roo.callback(cb);
36795             return;
36796         }
36797         this.isSlid = false;
36798         this.beforeSlide();
36799         this.el.slideOut(this.getSlideAnchor(), {
36800             callback: function(){
36801                 this.el.setLeftTop(-10000, -10000);
36802                 this.afterSlide();
36803                 this.afterSlideIn();
36804                 Roo.callback(cb);
36805             },
36806             scope: this,
36807             block: true
36808         });
36809     },
36810     
36811     slideInIf : function(e){
36812         if(!e.within(this.el)){
36813             this.slideIn();
36814         }
36815     },
36816
36817     animateCollapse : function(){
36818         this.beforeSlide();
36819         this.el.setStyle("z-index", 20000);
36820         var anchor = this.getSlideAnchor();
36821         this.el.slideOut(anchor, {
36822             callback : function(){
36823                 this.el.setStyle("z-index", "");
36824                 this.collapsedEl.slideIn(anchor, {duration:.3});
36825                 this.afterSlide();
36826                 this.el.setLocation(-10000,-10000);
36827                 this.el.hide();
36828                 this.fireEvent("collapsed", this);
36829             },
36830             scope: this,
36831             block: true
36832         });
36833     },
36834
36835     animateExpand : function(){
36836         this.beforeSlide();
36837         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36838         this.el.setStyle("z-index", 20000);
36839         this.collapsedEl.hide({
36840             duration:.1
36841         });
36842         this.el.slideIn(this.getSlideAnchor(), {
36843             callback : function(){
36844                 this.el.setStyle("z-index", "");
36845                 this.afterSlide();
36846                 if(this.split){
36847                     this.split.el.show();
36848                 }
36849                 this.fireEvent("invalidated", this);
36850                 this.fireEvent("expanded", this);
36851             },
36852             scope: this,
36853             block: true
36854         });
36855     },
36856
36857     anchors : {
36858         "west" : "left",
36859         "east" : "right",
36860         "north" : "top",
36861         "south" : "bottom"
36862     },
36863
36864     sanchors : {
36865         "west" : "l",
36866         "east" : "r",
36867         "north" : "t",
36868         "south" : "b"
36869     },
36870
36871     canchors : {
36872         "west" : "tl-tr",
36873         "east" : "tr-tl",
36874         "north" : "tl-bl",
36875         "south" : "bl-tl"
36876     },
36877
36878     getAnchor : function(){
36879         return this.anchors[this.position];
36880     },
36881
36882     getCollapseAnchor : function(){
36883         return this.canchors[this.position];
36884     },
36885
36886     getSlideAnchor : function(){
36887         return this.sanchors[this.position];
36888     },
36889
36890     getAlignAdj : function(){
36891         var cm = this.cmargins;
36892         switch(this.position){
36893             case "west":
36894                 return [0, 0];
36895             break;
36896             case "east":
36897                 return [0, 0];
36898             break;
36899             case "north":
36900                 return [0, 0];
36901             break;
36902             case "south":
36903                 return [0, 0];
36904             break;
36905         }
36906     },
36907
36908     getExpandAdj : function(){
36909         var c = this.collapsedEl, cm = this.cmargins;
36910         switch(this.position){
36911             case "west":
36912                 return [-(cm.right+c.getWidth()+cm.left), 0];
36913             break;
36914             case "east":
36915                 return [cm.right+c.getWidth()+cm.left, 0];
36916             break;
36917             case "north":
36918                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36919             break;
36920             case "south":
36921                 return [0, cm.top+cm.bottom+c.getHeight()];
36922             break;
36923         }
36924     }
36925 });/*
36926  * Based on:
36927  * Ext JS Library 1.1.1
36928  * Copyright(c) 2006-2007, Ext JS, LLC.
36929  *
36930  * Originally Released Under LGPL - original licence link has changed is not relivant.
36931  *
36932  * Fork - LGPL
36933  * <script type="text/javascript">
36934  */
36935 /*
36936  * These classes are private internal classes
36937  */
36938 Roo.bootstrap.layout.Center = function(config){
36939     config.region = "center";
36940     Roo.bootstrap.layout.Region.call(this, config);
36941     this.visible = true;
36942     this.minWidth = config.minWidth || 20;
36943     this.minHeight = config.minHeight || 20;
36944 };
36945
36946 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36947     hide : function(){
36948         // center panel can't be hidden
36949     },
36950     
36951     show : function(){
36952         // center panel can't be hidden
36953     },
36954     
36955     getMinWidth: function(){
36956         return this.minWidth;
36957     },
36958     
36959     getMinHeight: function(){
36960         return this.minHeight;
36961     }
36962 });
36963
36964
36965
36966
36967  
36968
36969
36970
36971
36972
36973 Roo.bootstrap.layout.North = function(config)
36974 {
36975     config.region = 'north';
36976     config.cursor = 'n-resize';
36977     
36978     Roo.bootstrap.layout.Split.call(this, config);
36979     
36980     
36981     if(this.split){
36982         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36983         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36984         this.split.el.addClass("roo-layout-split-v");
36985     }
36986     var size = config.initialSize || config.height;
36987     if(typeof size != "undefined"){
36988         this.el.setHeight(size);
36989     }
36990 };
36991 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36992 {
36993     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36994     
36995     
36996     
36997     getBox : function(){
36998         if(this.collapsed){
36999             return this.collapsedEl.getBox();
37000         }
37001         var box = this.el.getBox();
37002         if(this.split){
37003             box.height += this.split.el.getHeight();
37004         }
37005         return box;
37006     },
37007     
37008     updateBox : function(box){
37009         if(this.split && !this.collapsed){
37010             box.height -= this.split.el.getHeight();
37011             this.split.el.setLeft(box.x);
37012             this.split.el.setTop(box.y+box.height);
37013             this.split.el.setWidth(box.width);
37014         }
37015         if(this.collapsed){
37016             this.updateBody(box.width, null);
37017         }
37018         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37019     }
37020 });
37021
37022
37023
37024
37025
37026 Roo.bootstrap.layout.South = function(config){
37027     config.region = 'south';
37028     config.cursor = 's-resize';
37029     Roo.bootstrap.layout.Split.call(this, config);
37030     if(this.split){
37031         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37032         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37033         this.split.el.addClass("roo-layout-split-v");
37034     }
37035     var size = config.initialSize || config.height;
37036     if(typeof size != "undefined"){
37037         this.el.setHeight(size);
37038     }
37039 };
37040
37041 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37042     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37043     getBox : function(){
37044         if(this.collapsed){
37045             return this.collapsedEl.getBox();
37046         }
37047         var box = this.el.getBox();
37048         if(this.split){
37049             var sh = this.split.el.getHeight();
37050             box.height += sh;
37051             box.y -= sh;
37052         }
37053         return box;
37054     },
37055     
37056     updateBox : function(box){
37057         if(this.split && !this.collapsed){
37058             var sh = this.split.el.getHeight();
37059             box.height -= sh;
37060             box.y += sh;
37061             this.split.el.setLeft(box.x);
37062             this.split.el.setTop(box.y-sh);
37063             this.split.el.setWidth(box.width);
37064         }
37065         if(this.collapsed){
37066             this.updateBody(box.width, null);
37067         }
37068         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37069     }
37070 });
37071
37072 Roo.bootstrap.layout.East = function(config){
37073     config.region = "east";
37074     config.cursor = "e-resize";
37075     Roo.bootstrap.layout.Split.call(this, config);
37076     if(this.split){
37077         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37078         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37079         this.split.el.addClass("roo-layout-split-h");
37080     }
37081     var size = config.initialSize || config.width;
37082     if(typeof size != "undefined"){
37083         this.el.setWidth(size);
37084     }
37085 };
37086 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37087     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37088     getBox : function(){
37089         if(this.collapsed){
37090             return this.collapsedEl.getBox();
37091         }
37092         var box = this.el.getBox();
37093         if(this.split){
37094             var sw = this.split.el.getWidth();
37095             box.width += sw;
37096             box.x -= sw;
37097         }
37098         return box;
37099     },
37100
37101     updateBox : function(box){
37102         if(this.split && !this.collapsed){
37103             var sw = this.split.el.getWidth();
37104             box.width -= sw;
37105             this.split.el.setLeft(box.x);
37106             this.split.el.setTop(box.y);
37107             this.split.el.setHeight(box.height);
37108             box.x += sw;
37109         }
37110         if(this.collapsed){
37111             this.updateBody(null, box.height);
37112         }
37113         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37114     }
37115 });
37116
37117 Roo.bootstrap.layout.West = function(config){
37118     config.region = "west";
37119     config.cursor = "w-resize";
37120     
37121     Roo.bootstrap.layout.Split.call(this, config);
37122     if(this.split){
37123         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37124         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37125         this.split.el.addClass("roo-layout-split-h");
37126     }
37127     
37128 };
37129 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37130     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37131     
37132     onRender: function(ctr, pos)
37133     {
37134         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37135         var size = this.config.initialSize || this.config.width;
37136         if(typeof size != "undefined"){
37137             this.el.setWidth(size);
37138         }
37139     },
37140     
37141     getBox : function(){
37142         if(this.collapsed){
37143             return this.collapsedEl.getBox();
37144         }
37145         var box = this.el.getBox();
37146         if(this.split){
37147             box.width += this.split.el.getWidth();
37148         }
37149         return box;
37150     },
37151     
37152     updateBox : function(box){
37153         if(this.split && !this.collapsed){
37154             var sw = this.split.el.getWidth();
37155             box.width -= sw;
37156             this.split.el.setLeft(box.x+box.width);
37157             this.split.el.setTop(box.y);
37158             this.split.el.setHeight(box.height);
37159         }
37160         if(this.collapsed){
37161             this.updateBody(null, box.height);
37162         }
37163         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37164     }
37165 });
37166 Roo.namespace("Roo.bootstrap.panel");/*
37167  * Based on:
37168  * Ext JS Library 1.1.1
37169  * Copyright(c) 2006-2007, Ext JS, LLC.
37170  *
37171  * Originally Released Under LGPL - original licence link has changed is not relivant.
37172  *
37173  * Fork - LGPL
37174  * <script type="text/javascript">
37175  */
37176 /**
37177  * @class Roo.ContentPanel
37178  * @extends Roo.util.Observable
37179  * A basic ContentPanel element.
37180  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37181  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37182  * @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
37183  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37184  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37185  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37186  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37187  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37188  * @cfg {String} title          The title for this panel
37189  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37190  * @cfg {String} url            Calls {@link #setUrl} with this value
37191  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37192  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37193  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37194  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37195  * @cfg {Boolean} badges render the badges
37196
37197  * @constructor
37198  * Create a new ContentPanel.
37199  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37200  * @param {String/Object} config A string to set only the title or a config object
37201  * @param {String} content (optional) Set the HTML content for this panel
37202  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37203  */
37204 Roo.bootstrap.panel.Content = function( config){
37205     
37206     this.tpl = config.tpl || false;
37207     
37208     var el = config.el;
37209     var content = config.content;
37210
37211     if(config.autoCreate){ // xtype is available if this is called from factory
37212         el = Roo.id();
37213     }
37214     this.el = Roo.get(el);
37215     if(!this.el && config && config.autoCreate){
37216         if(typeof config.autoCreate == "object"){
37217             if(!config.autoCreate.id){
37218                 config.autoCreate.id = config.id||el;
37219             }
37220             this.el = Roo.DomHelper.append(document.body,
37221                         config.autoCreate, true);
37222         }else{
37223             var elcfg =  {   tag: "div",
37224                             cls: "roo-layout-inactive-content",
37225                             id: config.id||el
37226                             };
37227             if (config.html) {
37228                 elcfg.html = config.html;
37229                 
37230             }
37231                         
37232             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37233         }
37234     } 
37235     this.closable = false;
37236     this.loaded = false;
37237     this.active = false;
37238    
37239       
37240     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37241         
37242         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37243         
37244         this.wrapEl = this.el; //this.el.wrap();
37245         var ti = [];
37246         if (config.toolbar.items) {
37247             ti = config.toolbar.items ;
37248             delete config.toolbar.items ;
37249         }
37250         
37251         var nitems = [];
37252         this.toolbar.render(this.wrapEl, 'before');
37253         for(var i =0;i < ti.length;i++) {
37254           //  Roo.log(['add child', items[i]]);
37255             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37256         }
37257         this.toolbar.items = nitems;
37258         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37259         delete config.toolbar;
37260         
37261     }
37262     /*
37263     // xtype created footer. - not sure if will work as we normally have to render first..
37264     if (this.footer && !this.footer.el && this.footer.xtype) {
37265         if (!this.wrapEl) {
37266             this.wrapEl = this.el.wrap();
37267         }
37268     
37269         this.footer.container = this.wrapEl.createChild();
37270          
37271         this.footer = Roo.factory(this.footer, Roo);
37272         
37273     }
37274     */
37275     
37276      if(typeof config == "string"){
37277         this.title = config;
37278     }else{
37279         Roo.apply(this, config);
37280     }
37281     
37282     if(this.resizeEl){
37283         this.resizeEl = Roo.get(this.resizeEl, true);
37284     }else{
37285         this.resizeEl = this.el;
37286     }
37287     // handle view.xtype
37288     
37289  
37290     
37291     
37292     this.addEvents({
37293         /**
37294          * @event activate
37295          * Fires when this panel is activated. 
37296          * @param {Roo.ContentPanel} this
37297          */
37298         "activate" : true,
37299         /**
37300          * @event deactivate
37301          * Fires when this panel is activated. 
37302          * @param {Roo.ContentPanel} this
37303          */
37304         "deactivate" : true,
37305
37306         /**
37307          * @event resize
37308          * Fires when this panel is resized if fitToFrame is true.
37309          * @param {Roo.ContentPanel} this
37310          * @param {Number} width The width after any component adjustments
37311          * @param {Number} height The height after any component adjustments
37312          */
37313         "resize" : true,
37314         
37315          /**
37316          * @event render
37317          * Fires when this tab is created
37318          * @param {Roo.ContentPanel} this
37319          */
37320         "render" : true
37321         
37322         
37323         
37324     });
37325     
37326
37327     
37328     
37329     if(this.autoScroll){
37330         this.resizeEl.setStyle("overflow", "auto");
37331     } else {
37332         // fix randome scrolling
37333         //this.el.on('scroll', function() {
37334         //    Roo.log('fix random scolling');
37335         //    this.scrollTo('top',0); 
37336         //});
37337     }
37338     content = content || this.content;
37339     if(content){
37340         this.setContent(content);
37341     }
37342     if(config && config.url){
37343         this.setUrl(this.url, this.params, this.loadOnce);
37344     }
37345     
37346     
37347     
37348     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37349     
37350     if (this.view && typeof(this.view.xtype) != 'undefined') {
37351         this.view.el = this.el.appendChild(document.createElement("div"));
37352         this.view = Roo.factory(this.view); 
37353         this.view.render  &&  this.view.render(false, '');  
37354     }
37355     
37356     
37357     this.fireEvent('render', this);
37358 };
37359
37360 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37361     
37362     tabTip : '',
37363     
37364     setRegion : function(region){
37365         this.region = region;
37366         this.setActiveClass(region && !this.background);
37367     },
37368     
37369     
37370     setActiveClass: function(state)
37371     {
37372         if(state){
37373            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37374            this.el.setStyle('position','relative');
37375         }else{
37376            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37377            this.el.setStyle('position', 'absolute');
37378         } 
37379     },
37380     
37381     /**
37382      * Returns the toolbar for this Panel if one was configured. 
37383      * @return {Roo.Toolbar} 
37384      */
37385     getToolbar : function(){
37386         return this.toolbar;
37387     },
37388     
37389     setActiveState : function(active)
37390     {
37391         this.active = active;
37392         this.setActiveClass(active);
37393         if(!active){
37394             if(this.fireEvent("deactivate", this) === false){
37395                 return false;
37396             }
37397             return true;
37398         }
37399         this.fireEvent("activate", this);
37400         return true;
37401     },
37402     /**
37403      * Updates this panel's element
37404      * @param {String} content The new content
37405      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37406     */
37407     setContent : function(content, loadScripts){
37408         this.el.update(content, loadScripts);
37409     },
37410
37411     ignoreResize : function(w, h){
37412         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37413             return true;
37414         }else{
37415             this.lastSize = {width: w, height: h};
37416             return false;
37417         }
37418     },
37419     /**
37420      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37421      * @return {Roo.UpdateManager} The UpdateManager
37422      */
37423     getUpdateManager : function(){
37424         return this.el.getUpdateManager();
37425     },
37426      /**
37427      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37428      * @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:
37429 <pre><code>
37430 panel.load({
37431     url: "your-url.php",
37432     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37433     callback: yourFunction,
37434     scope: yourObject, //(optional scope)
37435     discardUrl: false,
37436     nocache: false,
37437     text: "Loading...",
37438     timeout: 30,
37439     scripts: false
37440 });
37441 </code></pre>
37442      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37443      * 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.
37444      * @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}
37445      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37446      * @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.
37447      * @return {Roo.ContentPanel} this
37448      */
37449     load : function(){
37450         var um = this.el.getUpdateManager();
37451         um.update.apply(um, arguments);
37452         return this;
37453     },
37454
37455
37456     /**
37457      * 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.
37458      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37459      * @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)
37460      * @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)
37461      * @return {Roo.UpdateManager} The UpdateManager
37462      */
37463     setUrl : function(url, params, loadOnce){
37464         if(this.refreshDelegate){
37465             this.removeListener("activate", this.refreshDelegate);
37466         }
37467         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37468         this.on("activate", this.refreshDelegate);
37469         return this.el.getUpdateManager();
37470     },
37471     
37472     _handleRefresh : function(url, params, loadOnce){
37473         if(!loadOnce || !this.loaded){
37474             var updater = this.el.getUpdateManager();
37475             updater.update(url, params, this._setLoaded.createDelegate(this));
37476         }
37477     },
37478     
37479     _setLoaded : function(){
37480         this.loaded = true;
37481     }, 
37482     
37483     /**
37484      * Returns this panel's id
37485      * @return {String} 
37486      */
37487     getId : function(){
37488         return this.el.id;
37489     },
37490     
37491     /** 
37492      * Returns this panel's element - used by regiosn to add.
37493      * @return {Roo.Element} 
37494      */
37495     getEl : function(){
37496         return this.wrapEl || this.el;
37497     },
37498     
37499    
37500     
37501     adjustForComponents : function(width, height)
37502     {
37503         //Roo.log('adjustForComponents ');
37504         if(this.resizeEl != this.el){
37505             width -= this.el.getFrameWidth('lr');
37506             height -= this.el.getFrameWidth('tb');
37507         }
37508         if(this.toolbar){
37509             var te = this.toolbar.getEl();
37510             te.setWidth(width);
37511             height -= te.getHeight();
37512         }
37513         if(this.footer){
37514             var te = this.footer.getEl();
37515             te.setWidth(width);
37516             height -= te.getHeight();
37517         }
37518         
37519         
37520         if(this.adjustments){
37521             width += this.adjustments[0];
37522             height += this.adjustments[1];
37523         }
37524         return {"width": width, "height": height};
37525     },
37526     
37527     setSize : function(width, height){
37528         if(this.fitToFrame && !this.ignoreResize(width, height)){
37529             if(this.fitContainer && this.resizeEl != this.el){
37530                 this.el.setSize(width, height);
37531             }
37532             var size = this.adjustForComponents(width, height);
37533             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37534             this.fireEvent('resize', this, size.width, size.height);
37535         }
37536     },
37537     
37538     /**
37539      * Returns this panel's title
37540      * @return {String} 
37541      */
37542     getTitle : function(){
37543         
37544         if (typeof(this.title) != 'object') {
37545             return this.title;
37546         }
37547         
37548         var t = '';
37549         for (var k in this.title) {
37550             if (!this.title.hasOwnProperty(k)) {
37551                 continue;
37552             }
37553             
37554             if (k.indexOf('-') >= 0) {
37555                 var s = k.split('-');
37556                 for (var i = 0; i<s.length; i++) {
37557                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37558                 }
37559             } else {
37560                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37561             }
37562         }
37563         return t;
37564     },
37565     
37566     /**
37567      * Set this panel's title
37568      * @param {String} title
37569      */
37570     setTitle : function(title){
37571         this.title = title;
37572         if(this.region){
37573             this.region.updatePanelTitle(this, title);
37574         }
37575     },
37576     
37577     /**
37578      * Returns true is this panel was configured to be closable
37579      * @return {Boolean} 
37580      */
37581     isClosable : function(){
37582         return this.closable;
37583     },
37584     
37585     beforeSlide : function(){
37586         this.el.clip();
37587         this.resizeEl.clip();
37588     },
37589     
37590     afterSlide : function(){
37591         this.el.unclip();
37592         this.resizeEl.unclip();
37593     },
37594     
37595     /**
37596      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37597      *   Will fail silently if the {@link #setUrl} method has not been called.
37598      *   This does not activate the panel, just updates its content.
37599      */
37600     refresh : function(){
37601         if(this.refreshDelegate){
37602            this.loaded = false;
37603            this.refreshDelegate();
37604         }
37605     },
37606     
37607     /**
37608      * Destroys this panel
37609      */
37610     destroy : function(){
37611         this.el.removeAllListeners();
37612         var tempEl = document.createElement("span");
37613         tempEl.appendChild(this.el.dom);
37614         tempEl.innerHTML = "";
37615         this.el.remove();
37616         this.el = null;
37617     },
37618     
37619     /**
37620      * form - if the content panel contains a form - this is a reference to it.
37621      * @type {Roo.form.Form}
37622      */
37623     form : false,
37624     /**
37625      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37626      *    This contains a reference to it.
37627      * @type {Roo.View}
37628      */
37629     view : false,
37630     
37631       /**
37632      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37633      * <pre><code>
37634
37635 layout.addxtype({
37636        xtype : 'Form',
37637        items: [ .... ]
37638    }
37639 );
37640
37641 </code></pre>
37642      * @param {Object} cfg Xtype definition of item to add.
37643      */
37644     
37645     
37646     getChildContainer: function () {
37647         return this.getEl();
37648     }
37649     
37650     
37651     /*
37652         var  ret = new Roo.factory(cfg);
37653         return ret;
37654         
37655         
37656         // add form..
37657         if (cfg.xtype.match(/^Form$/)) {
37658             
37659             var el;
37660             //if (this.footer) {
37661             //    el = this.footer.container.insertSibling(false, 'before');
37662             //} else {
37663                 el = this.el.createChild();
37664             //}
37665
37666             this.form = new  Roo.form.Form(cfg);
37667             
37668             
37669             if ( this.form.allItems.length) {
37670                 this.form.render(el.dom);
37671             }
37672             return this.form;
37673         }
37674         // should only have one of theses..
37675         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37676             // views.. should not be just added - used named prop 'view''
37677             
37678             cfg.el = this.el.appendChild(document.createElement("div"));
37679             // factory?
37680             
37681             var ret = new Roo.factory(cfg);
37682              
37683              ret.render && ret.render(false, ''); // render blank..
37684             this.view = ret;
37685             return ret;
37686         }
37687         return false;
37688     }
37689     \*/
37690 });
37691  
37692 /**
37693  * @class Roo.bootstrap.panel.Grid
37694  * @extends Roo.bootstrap.panel.Content
37695  * @constructor
37696  * Create a new GridPanel.
37697  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37698  * @param {Object} config A the config object
37699   
37700  */
37701
37702
37703
37704 Roo.bootstrap.panel.Grid = function(config)
37705 {
37706     
37707       
37708     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37709         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37710
37711     config.el = this.wrapper;
37712     //this.el = this.wrapper;
37713     
37714       if (config.container) {
37715         // ctor'ed from a Border/panel.grid
37716         
37717         
37718         this.wrapper.setStyle("overflow", "hidden");
37719         this.wrapper.addClass('roo-grid-container');
37720
37721     }
37722     
37723     
37724     if(config.toolbar){
37725         var tool_el = this.wrapper.createChild();    
37726         this.toolbar = Roo.factory(config.toolbar);
37727         var ti = [];
37728         if (config.toolbar.items) {
37729             ti = config.toolbar.items ;
37730             delete config.toolbar.items ;
37731         }
37732         
37733         var nitems = [];
37734         this.toolbar.render(tool_el);
37735         for(var i =0;i < ti.length;i++) {
37736           //  Roo.log(['add child', items[i]]);
37737             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37738         }
37739         this.toolbar.items = nitems;
37740         
37741         delete config.toolbar;
37742     }
37743     
37744     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37745     config.grid.scrollBody = true;;
37746     config.grid.monitorWindowResize = false; // turn off autosizing
37747     config.grid.autoHeight = false;
37748     config.grid.autoWidth = false;
37749     
37750     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37751     
37752     if (config.background) {
37753         // render grid on panel activation (if panel background)
37754         this.on('activate', function(gp) {
37755             if (!gp.grid.rendered) {
37756                 gp.grid.render(this.wrapper);
37757                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37758             }
37759         });
37760             
37761     } else {
37762         this.grid.render(this.wrapper);
37763         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37764
37765     }
37766     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37767     // ??? needed ??? config.el = this.wrapper;
37768     
37769     
37770     
37771   
37772     // xtype created footer. - not sure if will work as we normally have to render first..
37773     if (this.footer && !this.footer.el && this.footer.xtype) {
37774         
37775         var ctr = this.grid.getView().getFooterPanel(true);
37776         this.footer.dataSource = this.grid.dataSource;
37777         this.footer = Roo.factory(this.footer, Roo);
37778         this.footer.render(ctr);
37779         
37780     }
37781     
37782     
37783     
37784     
37785      
37786 };
37787
37788 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37789     getId : function(){
37790         return this.grid.id;
37791     },
37792     
37793     /**
37794      * Returns the grid for this panel
37795      * @return {Roo.bootstrap.Table} 
37796      */
37797     getGrid : function(){
37798         return this.grid;    
37799     },
37800     
37801     setSize : function(width, height){
37802         if(!this.ignoreResize(width, height)){
37803             var grid = this.grid;
37804             var size = this.adjustForComponents(width, height);
37805             var gridel = grid.getGridEl();
37806             gridel.setSize(size.width, size.height);
37807             /*
37808             var thd = grid.getGridEl().select('thead',true).first();
37809             var tbd = grid.getGridEl().select('tbody', true).first();
37810             if (tbd) {
37811                 tbd.setSize(width, height - thd.getHeight());
37812             }
37813             */
37814             grid.autoSize();
37815         }
37816     },
37817      
37818     
37819     
37820     beforeSlide : function(){
37821         this.grid.getView().scroller.clip();
37822     },
37823     
37824     afterSlide : function(){
37825         this.grid.getView().scroller.unclip();
37826     },
37827     
37828     destroy : function(){
37829         this.grid.destroy();
37830         delete this.grid;
37831         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37832     }
37833 });
37834
37835 /**
37836  * @class Roo.bootstrap.panel.Nest
37837  * @extends Roo.bootstrap.panel.Content
37838  * @constructor
37839  * Create a new Panel, that can contain a layout.Border.
37840  * 
37841  * 
37842  * @param {Roo.BorderLayout} layout The layout for this panel
37843  * @param {String/Object} config A string to set only the title or a config object
37844  */
37845 Roo.bootstrap.panel.Nest = function(config)
37846 {
37847     // construct with only one argument..
37848     /* FIXME - implement nicer consturctors
37849     if (layout.layout) {
37850         config = layout;
37851         layout = config.layout;
37852         delete config.layout;
37853     }
37854     if (layout.xtype && !layout.getEl) {
37855         // then layout needs constructing..
37856         layout = Roo.factory(layout, Roo);
37857     }
37858     */
37859     
37860     config.el =  config.layout.getEl();
37861     
37862     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37863     
37864     config.layout.monitorWindowResize = false; // turn off autosizing
37865     this.layout = config.layout;
37866     this.layout.getEl().addClass("roo-layout-nested-layout");
37867     
37868     
37869     
37870     
37871 };
37872
37873 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37874
37875     setSize : function(width, height){
37876         if(!this.ignoreResize(width, height)){
37877             var size = this.adjustForComponents(width, height);
37878             var el = this.layout.getEl();
37879             if (size.height < 1) {
37880                 el.setWidth(size.width);   
37881             } else {
37882                 el.setSize(size.width, size.height);
37883             }
37884             var touch = el.dom.offsetWidth;
37885             this.layout.layout();
37886             // ie requires a double layout on the first pass
37887             if(Roo.isIE && !this.initialized){
37888                 this.initialized = true;
37889                 this.layout.layout();
37890             }
37891         }
37892     },
37893     
37894     // activate all subpanels if not currently active..
37895     
37896     setActiveState : function(active){
37897         this.active = active;
37898         this.setActiveClass(active);
37899         
37900         if(!active){
37901             this.fireEvent("deactivate", this);
37902             return;
37903         }
37904         
37905         this.fireEvent("activate", this);
37906         // not sure if this should happen before or after..
37907         if (!this.layout) {
37908             return; // should not happen..
37909         }
37910         var reg = false;
37911         for (var r in this.layout.regions) {
37912             reg = this.layout.getRegion(r);
37913             if (reg.getActivePanel()) {
37914                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37915                 reg.setActivePanel(reg.getActivePanel());
37916                 continue;
37917             }
37918             if (!reg.panels.length) {
37919                 continue;
37920             }
37921             reg.showPanel(reg.getPanel(0));
37922         }
37923         
37924         
37925         
37926         
37927     },
37928     
37929     /**
37930      * Returns the nested BorderLayout for this panel
37931      * @return {Roo.BorderLayout} 
37932      */
37933     getLayout : function(){
37934         return this.layout;
37935     },
37936     
37937      /**
37938      * Adds a xtype elements to the layout of the nested panel
37939      * <pre><code>
37940
37941 panel.addxtype({
37942        xtype : 'ContentPanel',
37943        region: 'west',
37944        items: [ .... ]
37945    }
37946 );
37947
37948 panel.addxtype({
37949         xtype : 'NestedLayoutPanel',
37950         region: 'west',
37951         layout: {
37952            center: { },
37953            west: { }   
37954         },
37955         items : [ ... list of content panels or nested layout panels.. ]
37956    }
37957 );
37958 </code></pre>
37959      * @param {Object} cfg Xtype definition of item to add.
37960      */
37961     addxtype : function(cfg) {
37962         return this.layout.addxtype(cfg);
37963     
37964     }
37965 });        /*
37966  * Based on:
37967  * Ext JS Library 1.1.1
37968  * Copyright(c) 2006-2007, Ext JS, LLC.
37969  *
37970  * Originally Released Under LGPL - original licence link has changed is not relivant.
37971  *
37972  * Fork - LGPL
37973  * <script type="text/javascript">
37974  */
37975 /**
37976  * @class Roo.TabPanel
37977  * @extends Roo.util.Observable
37978  * A lightweight tab container.
37979  * <br><br>
37980  * Usage:
37981  * <pre><code>
37982 // basic tabs 1, built from existing content
37983 var tabs = new Roo.TabPanel("tabs1");
37984 tabs.addTab("script", "View Script");
37985 tabs.addTab("markup", "View Markup");
37986 tabs.activate("script");
37987
37988 // more advanced tabs, built from javascript
37989 var jtabs = new Roo.TabPanel("jtabs");
37990 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37991
37992 // set up the UpdateManager
37993 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37994 var updater = tab2.getUpdateManager();
37995 updater.setDefaultUrl("ajax1.htm");
37996 tab2.on('activate', updater.refresh, updater, true);
37997
37998 // Use setUrl for Ajax loading
37999 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38000 tab3.setUrl("ajax2.htm", null, true);
38001
38002 // Disabled tab
38003 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38004 tab4.disable();
38005
38006 jtabs.activate("jtabs-1");
38007  * </code></pre>
38008  * @constructor
38009  * Create a new TabPanel.
38010  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38011  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38012  */
38013 Roo.bootstrap.panel.Tabs = function(config){
38014     /**
38015     * The container element for this TabPanel.
38016     * @type Roo.Element
38017     */
38018     this.el = Roo.get(config.el);
38019     delete config.el;
38020     if(config){
38021         if(typeof config == "boolean"){
38022             this.tabPosition = config ? "bottom" : "top";
38023         }else{
38024             Roo.apply(this, config);
38025         }
38026     }
38027     
38028     if(this.tabPosition == "bottom"){
38029         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38030         this.el.addClass("roo-tabs-bottom");
38031     }
38032     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38033     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38034     this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38035     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38036     if(Roo.isIE){
38037         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38038     }
38039     if(this.tabPosition != "bottom"){
38040         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38041          * @type Roo.Element
38042          */
38043         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38044         this.el.addClass("roo-tabs-top");
38045     }
38046     this.items = [];
38047
38048     this.bodyEl.setStyle("position", "relative");
38049
38050     this.active = null;
38051     this.activateDelegate = this.activate.createDelegate(this);
38052
38053     this.addEvents({
38054         /**
38055          * @event tabchange
38056          * Fires when the active tab changes
38057          * @param {Roo.TabPanel} this
38058          * @param {Roo.TabPanelItem} activePanel The new active tab
38059          */
38060         "tabchange": true,
38061         /**
38062          * @event beforetabchange
38063          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38064          * @param {Roo.TabPanel} this
38065          * @param {Object} e Set cancel to true on this object to cancel the tab change
38066          * @param {Roo.TabPanelItem} tab The tab being changed to
38067          */
38068         "beforetabchange" : true
38069     });
38070
38071     Roo.EventManager.onWindowResize(this.onResize, this);
38072     this.cpad = this.el.getPadding("lr");
38073     this.hiddenCount = 0;
38074
38075
38076     // toolbar on the tabbar support...
38077     if (this.toolbar) {
38078         alert("no toolbar support yet");
38079         this.toolbar  = false;
38080         /*
38081         var tcfg = this.toolbar;
38082         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38083         this.toolbar = new Roo.Toolbar(tcfg);
38084         if (Roo.isSafari) {
38085             var tbl = tcfg.container.child('table', true);
38086             tbl.setAttribute('width', '100%');
38087         }
38088         */
38089         
38090     }
38091    
38092
38093
38094     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38095 };
38096
38097 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38098     /*
38099      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38100      */
38101     tabPosition : "top",
38102     /*
38103      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38104      */
38105     currentTabWidth : 0,
38106     /*
38107      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38108      */
38109     minTabWidth : 40,
38110     /*
38111      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38112      */
38113     maxTabWidth : 250,
38114     /*
38115      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38116      */
38117     preferredTabWidth : 175,
38118     /*
38119      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38120      */
38121     resizeTabs : false,
38122     /*
38123      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38124      */
38125     monitorResize : true,
38126     /*
38127      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38128      */
38129     toolbar : false,
38130
38131     /**
38132      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38133      * @param {String} id The id of the div to use <b>or create</b>
38134      * @param {String} text The text for the tab
38135      * @param {String} content (optional) Content to put in the TabPanelItem body
38136      * @param {Boolean} closable (optional) True to create a close icon on the tab
38137      * @return {Roo.TabPanelItem} The created TabPanelItem
38138      */
38139     addTab : function(id, text, content, closable, tpl)
38140     {
38141         var item = new Roo.bootstrap.panel.TabItem({
38142             panel: this,
38143             id : id,
38144             text : text,
38145             closable : closable,
38146             tpl : tpl
38147         });
38148         this.addTabItem(item);
38149         if(content){
38150             item.setContent(content);
38151         }
38152         return item;
38153     },
38154
38155     /**
38156      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38157      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38158      * @return {Roo.TabPanelItem}
38159      */
38160     getTab : function(id){
38161         return this.items[id];
38162     },
38163
38164     /**
38165      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38166      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38167      */
38168     hideTab : function(id){
38169         var t = this.items[id];
38170         if(!t.isHidden()){
38171            t.setHidden(true);
38172            this.hiddenCount++;
38173            this.autoSizeTabs();
38174         }
38175     },
38176
38177     /**
38178      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38179      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38180      */
38181     unhideTab : function(id){
38182         var t = this.items[id];
38183         if(t.isHidden()){
38184            t.setHidden(false);
38185            this.hiddenCount--;
38186            this.autoSizeTabs();
38187         }
38188     },
38189
38190     /**
38191      * Adds an existing {@link Roo.TabPanelItem}.
38192      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38193      */
38194     addTabItem : function(item)
38195     {
38196         this.items[item.id] = item;
38197         this.items.push(item);
38198         this.autoSizeTabs();
38199       //  if(this.resizeTabs){
38200     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38201   //         this.autoSizeTabs();
38202 //        }else{
38203 //            item.autoSize();
38204        // }
38205     },
38206
38207     /**
38208      * Removes a {@link Roo.TabPanelItem}.
38209      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38210      */
38211     removeTab : function(id){
38212         var items = this.items;
38213         var tab = items[id];
38214         if(!tab) { return; }
38215         var index = items.indexOf(tab);
38216         if(this.active == tab && items.length > 1){
38217             var newTab = this.getNextAvailable(index);
38218             if(newTab) {
38219                 newTab.activate();
38220             }
38221         }
38222         this.stripEl.dom.removeChild(tab.pnode.dom);
38223         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38224             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38225         }
38226         items.splice(index, 1);
38227         delete this.items[tab.id];
38228         tab.fireEvent("close", tab);
38229         tab.purgeListeners();
38230         this.autoSizeTabs();
38231     },
38232
38233     getNextAvailable : function(start){
38234         var items = this.items;
38235         var index = start;
38236         // look for a next tab that will slide over to
38237         // replace the one being removed
38238         while(index < items.length){
38239             var item = items[++index];
38240             if(item && !item.isHidden()){
38241                 return item;
38242             }
38243         }
38244         // if one isn't found select the previous tab (on the left)
38245         index = start;
38246         while(index >= 0){
38247             var item = items[--index];
38248             if(item && !item.isHidden()){
38249                 return item;
38250             }
38251         }
38252         return null;
38253     },
38254
38255     /**
38256      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38257      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38258      */
38259     disableTab : function(id){
38260         var tab = this.items[id];
38261         if(tab && this.active != tab){
38262             tab.disable();
38263         }
38264     },
38265
38266     /**
38267      * Enables a {@link Roo.TabPanelItem} that is disabled.
38268      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38269      */
38270     enableTab : function(id){
38271         var tab = this.items[id];
38272         tab.enable();
38273     },
38274
38275     /**
38276      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38277      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38278      * @return {Roo.TabPanelItem} The TabPanelItem.
38279      */
38280     activate : function(id)
38281     {
38282         var tab = this.items[id];
38283         if(!tab){
38284             return null;
38285         }
38286         if(tab == this.active || tab.disabled){
38287             return tab;
38288         }
38289         var e = {};
38290         this.fireEvent("beforetabchange", this, e, tab);
38291         if(e.cancel !== true && !tab.disabled){
38292             if(this.active){
38293                 this.active.hide();
38294             }
38295             this.active = this.items[id];
38296             this.active.show();
38297             this.fireEvent("tabchange", this, this.active);
38298         }
38299         return tab;
38300     },
38301
38302     /**
38303      * Gets the active {@link Roo.TabPanelItem}.
38304      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38305      */
38306     getActiveTab : function(){
38307         return this.active;
38308     },
38309
38310     /**
38311      * Updates the tab body element to fit the height of the container element
38312      * for overflow scrolling
38313      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38314      */
38315     syncHeight : function(targetHeight){
38316         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38317         var bm = this.bodyEl.getMargins();
38318         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38319         this.bodyEl.setHeight(newHeight);
38320         return newHeight;
38321     },
38322
38323     onResize : function(){
38324         if(this.monitorResize){
38325             this.autoSizeTabs();
38326         }
38327     },
38328
38329     /**
38330      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38331      */
38332     beginUpdate : function(){
38333         this.updating = true;
38334     },
38335
38336     /**
38337      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38338      */
38339     endUpdate : function(){
38340         this.updating = false;
38341         this.autoSizeTabs();
38342     },
38343
38344     /**
38345      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38346      */
38347     autoSizeTabs : function()
38348     {
38349         var count = this.items.length;
38350         var vcount = count - this.hiddenCount;
38351         
38352         if (vcount < 2) {
38353             this.stripEl.hide();
38354         } else {
38355             this.stripEl.show();
38356         }
38357         
38358         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38359             return;
38360         }
38361         
38362         
38363         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38364         var availWidth = Math.floor(w / vcount);
38365         var b = this.stripBody;
38366         if(b.getWidth() > w){
38367             var tabs = this.items;
38368             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38369             if(availWidth < this.minTabWidth){
38370                 /*if(!this.sleft){    // incomplete scrolling code
38371                     this.createScrollButtons();
38372                 }
38373                 this.showScroll();
38374                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38375             }
38376         }else{
38377             if(this.currentTabWidth < this.preferredTabWidth){
38378                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38379             }
38380         }
38381     },
38382
38383     /**
38384      * Returns the number of tabs in this TabPanel.
38385      * @return {Number}
38386      */
38387      getCount : function(){
38388          return this.items.length;
38389      },
38390
38391     /**
38392      * Resizes all the tabs to the passed width
38393      * @param {Number} The new width
38394      */
38395     setTabWidth : function(width){
38396         this.currentTabWidth = width;
38397         for(var i = 0, len = this.items.length; i < len; i++) {
38398                 if(!this.items[i].isHidden()) {
38399                 this.items[i].setWidth(width);
38400             }
38401         }
38402     },
38403
38404     /**
38405      * Destroys this TabPanel
38406      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38407      */
38408     destroy : function(removeEl){
38409         Roo.EventManager.removeResizeListener(this.onResize, this);
38410         for(var i = 0, len = this.items.length; i < len; i++){
38411             this.items[i].purgeListeners();
38412         }
38413         if(removeEl === true){
38414             this.el.update("");
38415             this.el.remove();
38416         }
38417     },
38418     
38419     createStrip : function(container)
38420     {
38421         var strip = document.createElement("nav");
38422         strip.className = Roo.bootstrap.version == 4 ?
38423             "navbar-light bg-light" : 
38424             "navbar navbar-default"; //"x-tabs-wrap";
38425         container.appendChild(strip);
38426         return strip;
38427     },
38428     
38429     createStripList : function(strip)
38430     {
38431         // div wrapper for retard IE
38432         // returns the "tr" element.
38433         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38434         //'<div class="x-tabs-strip-wrap">'+
38435           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38436           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38437         return strip.firstChild; //.firstChild.firstChild.firstChild;
38438     },
38439     createBody : function(container)
38440     {
38441         var body = document.createElement("div");
38442         Roo.id(body, "tab-body");
38443         //Roo.fly(body).addClass("x-tabs-body");
38444         Roo.fly(body).addClass("tab-content");
38445         container.appendChild(body);
38446         return body;
38447     },
38448     createItemBody :function(bodyEl, id){
38449         var body = Roo.getDom(id);
38450         if(!body){
38451             body = document.createElement("div");
38452             body.id = id;
38453         }
38454         //Roo.fly(body).addClass("x-tabs-item-body");
38455         Roo.fly(body).addClass("tab-pane");
38456          bodyEl.insertBefore(body, bodyEl.firstChild);
38457         return body;
38458     },
38459     /** @private */
38460     createStripElements :  function(stripEl, text, closable, tpl)
38461     {
38462         var td = document.createElement("li"); // was td..
38463         td.className = 'nav-item';
38464         
38465         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38466         
38467         
38468         stripEl.appendChild(td);
38469         /*if(closable){
38470             td.className = "x-tabs-closable";
38471             if(!this.closeTpl){
38472                 this.closeTpl = new Roo.Template(
38473                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38474                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38475                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38476                 );
38477             }
38478             var el = this.closeTpl.overwrite(td, {"text": text});
38479             var close = el.getElementsByTagName("div")[0];
38480             var inner = el.getElementsByTagName("em")[0];
38481             return {"el": el, "close": close, "inner": inner};
38482         } else {
38483         */
38484         // not sure what this is..
38485 //            if(!this.tabTpl){
38486                 //this.tabTpl = 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></em></span></a>'
38489                 //);
38490 //                this.tabTpl = new Roo.Template(
38491 //                   '<a href="#">' +
38492 //                   '<span unselectable="on"' +
38493 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38494 //                            ' >{text}</span></a>'
38495 //                );
38496 //                
38497 //            }
38498
38499
38500             var template = tpl || this.tabTpl || false;
38501             
38502             if(!template){
38503                 template =  new Roo.Template(
38504                         Roo.bootstrap.version == 4 ? 
38505                             (
38506                                 '<a class="nav-link" href="#" unselectable="on"' +
38507                                      (this.disableTooltips ? '' : ' title="{text}"') +
38508                                      ' >{text}</a>'
38509                             ) : (
38510                                 '<a class="nav-link" href="#">' +
38511                                 '<span unselectable="on"' +
38512                                          (this.disableTooltips ? '' : ' title="{text}"') +
38513                                     ' >{text}</span></a>'
38514                             )
38515                 );
38516             }
38517             
38518             switch (typeof(template)) {
38519                 case 'object' :
38520                     break;
38521                 case 'string' :
38522                     template = new Roo.Template(template);
38523                     break;
38524                 default :
38525                     break;
38526             }
38527             
38528             var el = template.overwrite(td, {"text": text});
38529             
38530             var inner = el.getElementsByTagName("span")[0];
38531             
38532             return {"el": el, "inner": inner};
38533             
38534     }
38535         
38536     
38537 });
38538
38539 /**
38540  * @class Roo.TabPanelItem
38541  * @extends Roo.util.Observable
38542  * Represents an individual item (tab plus body) in a TabPanel.
38543  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38544  * @param {String} id The id of this TabPanelItem
38545  * @param {String} text The text for the tab of this TabPanelItem
38546  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38547  */
38548 Roo.bootstrap.panel.TabItem = function(config){
38549     /**
38550      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38551      * @type Roo.TabPanel
38552      */
38553     this.tabPanel = config.panel;
38554     /**
38555      * The id for this TabPanelItem
38556      * @type String
38557      */
38558     this.id = config.id;
38559     /** @private */
38560     this.disabled = false;
38561     /** @private */
38562     this.text = config.text;
38563     /** @private */
38564     this.loaded = false;
38565     this.closable = config.closable;
38566
38567     /**
38568      * The body element for this TabPanelItem.
38569      * @type Roo.Element
38570      */
38571     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38572     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38573     this.bodyEl.setStyle("display", "block");
38574     this.bodyEl.setStyle("zoom", "1");
38575     //this.hideAction();
38576
38577     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38578     /** @private */
38579     this.el = Roo.get(els.el);
38580     this.inner = Roo.get(els.inner, true);
38581      this.textEl = Roo.bootstrap.version == 4 ?
38582         this.el : Roo.get(this.el.dom.firstChild, true);
38583
38584     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38585     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38586
38587     
38588 //    this.el.on("mousedown", this.onTabMouseDown, this);
38589     this.el.on("click", this.onTabClick, this);
38590     /** @private */
38591     if(config.closable){
38592         var c = Roo.get(els.close, true);
38593         c.dom.title = this.closeText;
38594         c.addClassOnOver("close-over");
38595         c.on("click", this.closeClick, this);
38596      }
38597
38598     this.addEvents({
38599          /**
38600          * @event activate
38601          * Fires when this tab becomes the active tab.
38602          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38603          * @param {Roo.TabPanelItem} this
38604          */
38605         "activate": true,
38606         /**
38607          * @event beforeclose
38608          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38609          * @param {Roo.TabPanelItem} this
38610          * @param {Object} e Set cancel to true on this object to cancel the close.
38611          */
38612         "beforeclose": true,
38613         /**
38614          * @event close
38615          * Fires when this tab is closed.
38616          * @param {Roo.TabPanelItem} this
38617          */
38618          "close": true,
38619         /**
38620          * @event deactivate
38621          * Fires when this tab is no longer the active tab.
38622          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38623          * @param {Roo.TabPanelItem} this
38624          */
38625          "deactivate" : true
38626     });
38627     this.hidden = false;
38628
38629     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38630 };
38631
38632 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38633            {
38634     purgeListeners : function(){
38635        Roo.util.Observable.prototype.purgeListeners.call(this);
38636        this.el.removeAllListeners();
38637     },
38638     /**
38639      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38640      */
38641     show : function(){
38642         this.status_node.addClass("active");
38643         this.showAction();
38644         if(Roo.isOpera){
38645             this.tabPanel.stripWrap.repaint();
38646         }
38647         this.fireEvent("activate", this.tabPanel, this);
38648     },
38649
38650     /**
38651      * Returns true if this tab is the active tab.
38652      * @return {Boolean}
38653      */
38654     isActive : function(){
38655         return this.tabPanel.getActiveTab() == this;
38656     },
38657
38658     /**
38659      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38660      */
38661     hide : function(){
38662         this.status_node.removeClass("active");
38663         this.hideAction();
38664         this.fireEvent("deactivate", this.tabPanel, this);
38665     },
38666
38667     hideAction : function(){
38668         this.bodyEl.hide();
38669         this.bodyEl.setStyle("position", "absolute");
38670         this.bodyEl.setLeft("-20000px");
38671         this.bodyEl.setTop("-20000px");
38672     },
38673
38674     showAction : function(){
38675         this.bodyEl.setStyle("position", "relative");
38676         this.bodyEl.setTop("");
38677         this.bodyEl.setLeft("");
38678         this.bodyEl.show();
38679     },
38680
38681     /**
38682      * Set the tooltip for the tab.
38683      * @param {String} tooltip The tab's tooltip
38684      */
38685     setTooltip : function(text){
38686         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38687             this.textEl.dom.qtip = text;
38688             this.textEl.dom.removeAttribute('title');
38689         }else{
38690             this.textEl.dom.title = text;
38691         }
38692     },
38693
38694     onTabClick : function(e){
38695         e.preventDefault();
38696         this.tabPanel.activate(this.id);
38697     },
38698
38699     onTabMouseDown : function(e){
38700         e.preventDefault();
38701         this.tabPanel.activate(this.id);
38702     },
38703 /*
38704     getWidth : function(){
38705         return this.inner.getWidth();
38706     },
38707
38708     setWidth : function(width){
38709         var iwidth = width - this.linode.getPadding("lr");
38710         this.inner.setWidth(iwidth);
38711         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38712         this.linode.setWidth(width);
38713     },
38714 */
38715     /**
38716      * Show or hide the tab
38717      * @param {Boolean} hidden True to hide or false to show.
38718      */
38719     setHidden : function(hidden){
38720         this.hidden = hidden;
38721         this.linode.setStyle("display", hidden ? "none" : "");
38722     },
38723
38724     /**
38725      * Returns true if this tab is "hidden"
38726      * @return {Boolean}
38727      */
38728     isHidden : function(){
38729         return this.hidden;
38730     },
38731
38732     /**
38733      * Returns the text for this tab
38734      * @return {String}
38735      */
38736     getText : function(){
38737         return this.text;
38738     },
38739     /*
38740     autoSize : function(){
38741         //this.el.beginMeasure();
38742         this.textEl.setWidth(1);
38743         /*
38744          *  #2804 [new] Tabs in Roojs
38745          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38746          */
38747         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38748         //this.el.endMeasure();
38749     //},
38750
38751     /**
38752      * Sets the text for the tab (Note: this also sets the tooltip text)
38753      * @param {String} text The tab's text and tooltip
38754      */
38755     setText : function(text){
38756         this.text = text;
38757         this.textEl.update(text);
38758         this.setTooltip(text);
38759         //if(!this.tabPanel.resizeTabs){
38760         //    this.autoSize();
38761         //}
38762     },
38763     /**
38764      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38765      */
38766     activate : function(){
38767         this.tabPanel.activate(this.id);
38768     },
38769
38770     /**
38771      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38772      */
38773     disable : function(){
38774         if(this.tabPanel.active != this){
38775             this.disabled = true;
38776             this.status_node.addClass("disabled");
38777         }
38778     },
38779
38780     /**
38781      * Enables this TabPanelItem if it was previously disabled.
38782      */
38783     enable : function(){
38784         this.disabled = false;
38785         this.status_node.removeClass("disabled");
38786     },
38787
38788     /**
38789      * Sets the content for this TabPanelItem.
38790      * @param {String} content The content
38791      * @param {Boolean} loadScripts true to look for and load scripts
38792      */
38793     setContent : function(content, loadScripts){
38794         this.bodyEl.update(content, loadScripts);
38795     },
38796
38797     /**
38798      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38799      * @return {Roo.UpdateManager} The UpdateManager
38800      */
38801     getUpdateManager : function(){
38802         return this.bodyEl.getUpdateManager();
38803     },
38804
38805     /**
38806      * Set a URL to be used to load the content for this TabPanelItem.
38807      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38808      * @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)
38809      * @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)
38810      * @return {Roo.UpdateManager} The UpdateManager
38811      */
38812     setUrl : function(url, params, loadOnce){
38813         if(this.refreshDelegate){
38814             this.un('activate', this.refreshDelegate);
38815         }
38816         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38817         this.on("activate", this.refreshDelegate);
38818         return this.bodyEl.getUpdateManager();
38819     },
38820
38821     /** @private */
38822     _handleRefresh : function(url, params, loadOnce){
38823         if(!loadOnce || !this.loaded){
38824             var updater = this.bodyEl.getUpdateManager();
38825             updater.update(url, params, this._setLoaded.createDelegate(this));
38826         }
38827     },
38828
38829     /**
38830      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38831      *   Will fail silently if the setUrl method has not been called.
38832      *   This does not activate the panel, just updates its content.
38833      */
38834     refresh : function(){
38835         if(this.refreshDelegate){
38836            this.loaded = false;
38837            this.refreshDelegate();
38838         }
38839     },
38840
38841     /** @private */
38842     _setLoaded : function(){
38843         this.loaded = true;
38844     },
38845
38846     /** @private */
38847     closeClick : function(e){
38848         var o = {};
38849         e.stopEvent();
38850         this.fireEvent("beforeclose", this, o);
38851         if(o.cancel !== true){
38852             this.tabPanel.removeTab(this.id);
38853         }
38854     },
38855     /**
38856      * The text displayed in the tooltip for the close icon.
38857      * @type String
38858      */
38859     closeText : "Close this tab"
38860 });
38861 /**
38862 *    This script refer to:
38863 *    Title: International Telephone Input
38864 *    Author: Jack O'Connor
38865 *    Code version:  v12.1.12
38866 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38867 **/
38868
38869 Roo.bootstrap.PhoneInputData = function() {
38870     var d = [
38871       [
38872         "Afghanistan (‫افغانستان‬‎)",
38873         "af",
38874         "93"
38875       ],
38876       [
38877         "Albania (Shqipëri)",
38878         "al",
38879         "355"
38880       ],
38881       [
38882         "Algeria (‫الجزائر‬‎)",
38883         "dz",
38884         "213"
38885       ],
38886       [
38887         "American Samoa",
38888         "as",
38889         "1684"
38890       ],
38891       [
38892         "Andorra",
38893         "ad",
38894         "376"
38895       ],
38896       [
38897         "Angola",
38898         "ao",
38899         "244"
38900       ],
38901       [
38902         "Anguilla",
38903         "ai",
38904         "1264"
38905       ],
38906       [
38907         "Antigua and Barbuda",
38908         "ag",
38909         "1268"
38910       ],
38911       [
38912         "Argentina",
38913         "ar",
38914         "54"
38915       ],
38916       [
38917         "Armenia (Հայաստան)",
38918         "am",
38919         "374"
38920       ],
38921       [
38922         "Aruba",
38923         "aw",
38924         "297"
38925       ],
38926       [
38927         "Australia",
38928         "au",
38929         "61",
38930         0
38931       ],
38932       [
38933         "Austria (Österreich)",
38934         "at",
38935         "43"
38936       ],
38937       [
38938         "Azerbaijan (Azərbaycan)",
38939         "az",
38940         "994"
38941       ],
38942       [
38943         "Bahamas",
38944         "bs",
38945         "1242"
38946       ],
38947       [
38948         "Bahrain (‫البحرين‬‎)",
38949         "bh",
38950         "973"
38951       ],
38952       [
38953         "Bangladesh (বাংলাদেশ)",
38954         "bd",
38955         "880"
38956       ],
38957       [
38958         "Barbados",
38959         "bb",
38960         "1246"
38961       ],
38962       [
38963         "Belarus (Беларусь)",
38964         "by",
38965         "375"
38966       ],
38967       [
38968         "Belgium (België)",
38969         "be",
38970         "32"
38971       ],
38972       [
38973         "Belize",
38974         "bz",
38975         "501"
38976       ],
38977       [
38978         "Benin (Bénin)",
38979         "bj",
38980         "229"
38981       ],
38982       [
38983         "Bermuda",
38984         "bm",
38985         "1441"
38986       ],
38987       [
38988         "Bhutan (འབྲུག)",
38989         "bt",
38990         "975"
38991       ],
38992       [
38993         "Bolivia",
38994         "bo",
38995         "591"
38996       ],
38997       [
38998         "Bosnia and Herzegovina (Босна и Херцеговина)",
38999         "ba",
39000         "387"
39001       ],
39002       [
39003         "Botswana",
39004         "bw",
39005         "267"
39006       ],
39007       [
39008         "Brazil (Brasil)",
39009         "br",
39010         "55"
39011       ],
39012       [
39013         "British Indian Ocean Territory",
39014         "io",
39015         "246"
39016       ],
39017       [
39018         "British Virgin Islands",
39019         "vg",
39020         "1284"
39021       ],
39022       [
39023         "Brunei",
39024         "bn",
39025         "673"
39026       ],
39027       [
39028         "Bulgaria (България)",
39029         "bg",
39030         "359"
39031       ],
39032       [
39033         "Burkina Faso",
39034         "bf",
39035         "226"
39036       ],
39037       [
39038         "Burundi (Uburundi)",
39039         "bi",
39040         "257"
39041       ],
39042       [
39043         "Cambodia (កម្ពុជា)",
39044         "kh",
39045         "855"
39046       ],
39047       [
39048         "Cameroon (Cameroun)",
39049         "cm",
39050         "237"
39051       ],
39052       [
39053         "Canada",
39054         "ca",
39055         "1",
39056         1,
39057         ["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"]
39058       ],
39059       [
39060         "Cape Verde (Kabu Verdi)",
39061         "cv",
39062         "238"
39063       ],
39064       [
39065         "Caribbean Netherlands",
39066         "bq",
39067         "599",
39068         1
39069       ],
39070       [
39071         "Cayman Islands",
39072         "ky",
39073         "1345"
39074       ],
39075       [
39076         "Central African Republic (République centrafricaine)",
39077         "cf",
39078         "236"
39079       ],
39080       [
39081         "Chad (Tchad)",
39082         "td",
39083         "235"
39084       ],
39085       [
39086         "Chile",
39087         "cl",
39088         "56"
39089       ],
39090       [
39091         "China (中国)",
39092         "cn",
39093         "86"
39094       ],
39095       [
39096         "Christmas Island",
39097         "cx",
39098         "61",
39099         2
39100       ],
39101       [
39102         "Cocos (Keeling) Islands",
39103         "cc",
39104         "61",
39105         1
39106       ],
39107       [
39108         "Colombia",
39109         "co",
39110         "57"
39111       ],
39112       [
39113         "Comoros (‫جزر القمر‬‎)",
39114         "km",
39115         "269"
39116       ],
39117       [
39118         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39119         "cd",
39120         "243"
39121       ],
39122       [
39123         "Congo (Republic) (Congo-Brazzaville)",
39124         "cg",
39125         "242"
39126       ],
39127       [
39128         "Cook Islands",
39129         "ck",
39130         "682"
39131       ],
39132       [
39133         "Costa Rica",
39134         "cr",
39135         "506"
39136       ],
39137       [
39138         "Côte d’Ivoire",
39139         "ci",
39140         "225"
39141       ],
39142       [
39143         "Croatia (Hrvatska)",
39144         "hr",
39145         "385"
39146       ],
39147       [
39148         "Cuba",
39149         "cu",
39150         "53"
39151       ],
39152       [
39153         "Curaçao",
39154         "cw",
39155         "599",
39156         0
39157       ],
39158       [
39159         "Cyprus (Κύπρος)",
39160         "cy",
39161         "357"
39162       ],
39163       [
39164         "Czech Republic (Česká republika)",
39165         "cz",
39166         "420"
39167       ],
39168       [
39169         "Denmark (Danmark)",
39170         "dk",
39171         "45"
39172       ],
39173       [
39174         "Djibouti",
39175         "dj",
39176         "253"
39177       ],
39178       [
39179         "Dominica",
39180         "dm",
39181         "1767"
39182       ],
39183       [
39184         "Dominican Republic (República Dominicana)",
39185         "do",
39186         "1",
39187         2,
39188         ["809", "829", "849"]
39189       ],
39190       [
39191         "Ecuador",
39192         "ec",
39193         "593"
39194       ],
39195       [
39196         "Egypt (‫مصر‬‎)",
39197         "eg",
39198         "20"
39199       ],
39200       [
39201         "El Salvador",
39202         "sv",
39203         "503"
39204       ],
39205       [
39206         "Equatorial Guinea (Guinea Ecuatorial)",
39207         "gq",
39208         "240"
39209       ],
39210       [
39211         "Eritrea",
39212         "er",
39213         "291"
39214       ],
39215       [
39216         "Estonia (Eesti)",
39217         "ee",
39218         "372"
39219       ],
39220       [
39221         "Ethiopia",
39222         "et",
39223         "251"
39224       ],
39225       [
39226         "Falkland Islands (Islas Malvinas)",
39227         "fk",
39228         "500"
39229       ],
39230       [
39231         "Faroe Islands (Føroyar)",
39232         "fo",
39233         "298"
39234       ],
39235       [
39236         "Fiji",
39237         "fj",
39238         "679"
39239       ],
39240       [
39241         "Finland (Suomi)",
39242         "fi",
39243         "358",
39244         0
39245       ],
39246       [
39247         "France",
39248         "fr",
39249         "33"
39250       ],
39251       [
39252         "French Guiana (Guyane française)",
39253         "gf",
39254         "594"
39255       ],
39256       [
39257         "French Polynesia (Polynésie française)",
39258         "pf",
39259         "689"
39260       ],
39261       [
39262         "Gabon",
39263         "ga",
39264         "241"
39265       ],
39266       [
39267         "Gambia",
39268         "gm",
39269         "220"
39270       ],
39271       [
39272         "Georgia (საქართველო)",
39273         "ge",
39274         "995"
39275       ],
39276       [
39277         "Germany (Deutschland)",
39278         "de",
39279         "49"
39280       ],
39281       [
39282         "Ghana (Gaana)",
39283         "gh",
39284         "233"
39285       ],
39286       [
39287         "Gibraltar",
39288         "gi",
39289         "350"
39290       ],
39291       [
39292         "Greece (Ελλάδα)",
39293         "gr",
39294         "30"
39295       ],
39296       [
39297         "Greenland (Kalaallit Nunaat)",
39298         "gl",
39299         "299"
39300       ],
39301       [
39302         "Grenada",
39303         "gd",
39304         "1473"
39305       ],
39306       [
39307         "Guadeloupe",
39308         "gp",
39309         "590",
39310         0
39311       ],
39312       [
39313         "Guam",
39314         "gu",
39315         "1671"
39316       ],
39317       [
39318         "Guatemala",
39319         "gt",
39320         "502"
39321       ],
39322       [
39323         "Guernsey",
39324         "gg",
39325         "44",
39326         1
39327       ],
39328       [
39329         "Guinea (Guinée)",
39330         "gn",
39331         "224"
39332       ],
39333       [
39334         "Guinea-Bissau (Guiné Bissau)",
39335         "gw",
39336         "245"
39337       ],
39338       [
39339         "Guyana",
39340         "gy",
39341         "592"
39342       ],
39343       [
39344         "Haiti",
39345         "ht",
39346         "509"
39347       ],
39348       [
39349         "Honduras",
39350         "hn",
39351         "504"
39352       ],
39353       [
39354         "Hong Kong (香港)",
39355         "hk",
39356         "852"
39357       ],
39358       [
39359         "Hungary (Magyarország)",
39360         "hu",
39361         "36"
39362       ],
39363       [
39364         "Iceland (Ísland)",
39365         "is",
39366         "354"
39367       ],
39368       [
39369         "India (भारत)",
39370         "in",
39371         "91"
39372       ],
39373       [
39374         "Indonesia",
39375         "id",
39376         "62"
39377       ],
39378       [
39379         "Iran (‫ایران‬‎)",
39380         "ir",
39381         "98"
39382       ],
39383       [
39384         "Iraq (‫العراق‬‎)",
39385         "iq",
39386         "964"
39387       ],
39388       [
39389         "Ireland",
39390         "ie",
39391         "353"
39392       ],
39393       [
39394         "Isle of Man",
39395         "im",
39396         "44",
39397         2
39398       ],
39399       [
39400         "Israel (‫ישראל‬‎)",
39401         "il",
39402         "972"
39403       ],
39404       [
39405         "Italy (Italia)",
39406         "it",
39407         "39",
39408         0
39409       ],
39410       [
39411         "Jamaica",
39412         "jm",
39413         "1876"
39414       ],
39415       [
39416         "Japan (日本)",
39417         "jp",
39418         "81"
39419       ],
39420       [
39421         "Jersey",
39422         "je",
39423         "44",
39424         3
39425       ],
39426       [
39427         "Jordan (‫الأردن‬‎)",
39428         "jo",
39429         "962"
39430       ],
39431       [
39432         "Kazakhstan (Казахстан)",
39433         "kz",
39434         "7",
39435         1
39436       ],
39437       [
39438         "Kenya",
39439         "ke",
39440         "254"
39441       ],
39442       [
39443         "Kiribati",
39444         "ki",
39445         "686"
39446       ],
39447       [
39448         "Kosovo",
39449         "xk",
39450         "383"
39451       ],
39452       [
39453         "Kuwait (‫الكويت‬‎)",
39454         "kw",
39455         "965"
39456       ],
39457       [
39458         "Kyrgyzstan (Кыргызстан)",
39459         "kg",
39460         "996"
39461       ],
39462       [
39463         "Laos (ລາວ)",
39464         "la",
39465         "856"
39466       ],
39467       [
39468         "Latvia (Latvija)",
39469         "lv",
39470         "371"
39471       ],
39472       [
39473         "Lebanon (‫لبنان‬‎)",
39474         "lb",
39475         "961"
39476       ],
39477       [
39478         "Lesotho",
39479         "ls",
39480         "266"
39481       ],
39482       [
39483         "Liberia",
39484         "lr",
39485         "231"
39486       ],
39487       [
39488         "Libya (‫ليبيا‬‎)",
39489         "ly",
39490         "218"
39491       ],
39492       [
39493         "Liechtenstein",
39494         "li",
39495         "423"
39496       ],
39497       [
39498         "Lithuania (Lietuva)",
39499         "lt",
39500         "370"
39501       ],
39502       [
39503         "Luxembourg",
39504         "lu",
39505         "352"
39506       ],
39507       [
39508         "Macau (澳門)",
39509         "mo",
39510         "853"
39511       ],
39512       [
39513         "Macedonia (FYROM) (Македонија)",
39514         "mk",
39515         "389"
39516       ],
39517       [
39518         "Madagascar (Madagasikara)",
39519         "mg",
39520         "261"
39521       ],
39522       [
39523         "Malawi",
39524         "mw",
39525         "265"
39526       ],
39527       [
39528         "Malaysia",
39529         "my",
39530         "60"
39531       ],
39532       [
39533         "Maldives",
39534         "mv",
39535         "960"
39536       ],
39537       [
39538         "Mali",
39539         "ml",
39540         "223"
39541       ],
39542       [
39543         "Malta",
39544         "mt",
39545         "356"
39546       ],
39547       [
39548         "Marshall Islands",
39549         "mh",
39550         "692"
39551       ],
39552       [
39553         "Martinique",
39554         "mq",
39555         "596"
39556       ],
39557       [
39558         "Mauritania (‫موريتانيا‬‎)",
39559         "mr",
39560         "222"
39561       ],
39562       [
39563         "Mauritius (Moris)",
39564         "mu",
39565         "230"
39566       ],
39567       [
39568         "Mayotte",
39569         "yt",
39570         "262",
39571         1
39572       ],
39573       [
39574         "Mexico (México)",
39575         "mx",
39576         "52"
39577       ],
39578       [
39579         "Micronesia",
39580         "fm",
39581         "691"
39582       ],
39583       [
39584         "Moldova (Republica Moldova)",
39585         "md",
39586         "373"
39587       ],
39588       [
39589         "Monaco",
39590         "mc",
39591         "377"
39592       ],
39593       [
39594         "Mongolia (Монгол)",
39595         "mn",
39596         "976"
39597       ],
39598       [
39599         "Montenegro (Crna Gora)",
39600         "me",
39601         "382"
39602       ],
39603       [
39604         "Montserrat",
39605         "ms",
39606         "1664"
39607       ],
39608       [
39609         "Morocco (‫المغرب‬‎)",
39610         "ma",
39611         "212",
39612         0
39613       ],
39614       [
39615         "Mozambique (Moçambique)",
39616         "mz",
39617         "258"
39618       ],
39619       [
39620         "Myanmar (Burma) (မြန်မာ)",
39621         "mm",
39622         "95"
39623       ],
39624       [
39625         "Namibia (Namibië)",
39626         "na",
39627         "264"
39628       ],
39629       [
39630         "Nauru",
39631         "nr",
39632         "674"
39633       ],
39634       [
39635         "Nepal (नेपाल)",
39636         "np",
39637         "977"
39638       ],
39639       [
39640         "Netherlands (Nederland)",
39641         "nl",
39642         "31"
39643       ],
39644       [
39645         "New Caledonia (Nouvelle-Calédonie)",
39646         "nc",
39647         "687"
39648       ],
39649       [
39650         "New Zealand",
39651         "nz",
39652         "64"
39653       ],
39654       [
39655         "Nicaragua",
39656         "ni",
39657         "505"
39658       ],
39659       [
39660         "Niger (Nijar)",
39661         "ne",
39662         "227"
39663       ],
39664       [
39665         "Nigeria",
39666         "ng",
39667         "234"
39668       ],
39669       [
39670         "Niue",
39671         "nu",
39672         "683"
39673       ],
39674       [
39675         "Norfolk Island",
39676         "nf",
39677         "672"
39678       ],
39679       [
39680         "North Korea (조선 민주주의 인민 공화국)",
39681         "kp",
39682         "850"
39683       ],
39684       [
39685         "Northern Mariana Islands",
39686         "mp",
39687         "1670"
39688       ],
39689       [
39690         "Norway (Norge)",
39691         "no",
39692         "47",
39693         0
39694       ],
39695       [
39696         "Oman (‫عُمان‬‎)",
39697         "om",
39698         "968"
39699       ],
39700       [
39701         "Pakistan (‫پاکستان‬‎)",
39702         "pk",
39703         "92"
39704       ],
39705       [
39706         "Palau",
39707         "pw",
39708         "680"
39709       ],
39710       [
39711         "Palestine (‫فلسطين‬‎)",
39712         "ps",
39713         "970"
39714       ],
39715       [
39716         "Panama (Panamá)",
39717         "pa",
39718         "507"
39719       ],
39720       [
39721         "Papua New Guinea",
39722         "pg",
39723         "675"
39724       ],
39725       [
39726         "Paraguay",
39727         "py",
39728         "595"
39729       ],
39730       [
39731         "Peru (Perú)",
39732         "pe",
39733         "51"
39734       ],
39735       [
39736         "Philippines",
39737         "ph",
39738         "63"
39739       ],
39740       [
39741         "Poland (Polska)",
39742         "pl",
39743         "48"
39744       ],
39745       [
39746         "Portugal",
39747         "pt",
39748         "351"
39749       ],
39750       [
39751         "Puerto Rico",
39752         "pr",
39753         "1",
39754         3,
39755         ["787", "939"]
39756       ],
39757       [
39758         "Qatar (‫قطر‬‎)",
39759         "qa",
39760         "974"
39761       ],
39762       [
39763         "Réunion (La Réunion)",
39764         "re",
39765         "262",
39766         0
39767       ],
39768       [
39769         "Romania (România)",
39770         "ro",
39771         "40"
39772       ],
39773       [
39774         "Russia (Россия)",
39775         "ru",
39776         "7",
39777         0
39778       ],
39779       [
39780         "Rwanda",
39781         "rw",
39782         "250"
39783       ],
39784       [
39785         "Saint Barthélemy",
39786         "bl",
39787         "590",
39788         1
39789       ],
39790       [
39791         "Saint Helena",
39792         "sh",
39793         "290"
39794       ],
39795       [
39796         "Saint Kitts and Nevis",
39797         "kn",
39798         "1869"
39799       ],
39800       [
39801         "Saint Lucia",
39802         "lc",
39803         "1758"
39804       ],
39805       [
39806         "Saint Martin (Saint-Martin (partie française))",
39807         "mf",
39808         "590",
39809         2
39810       ],
39811       [
39812         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39813         "pm",
39814         "508"
39815       ],
39816       [
39817         "Saint Vincent and the Grenadines",
39818         "vc",
39819         "1784"
39820       ],
39821       [
39822         "Samoa",
39823         "ws",
39824         "685"
39825       ],
39826       [
39827         "San Marino",
39828         "sm",
39829         "378"
39830       ],
39831       [
39832         "São Tomé and Príncipe (São Tomé e Príncipe)",
39833         "st",
39834         "239"
39835       ],
39836       [
39837         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39838         "sa",
39839         "966"
39840       ],
39841       [
39842         "Senegal (Sénégal)",
39843         "sn",
39844         "221"
39845       ],
39846       [
39847         "Serbia (Србија)",
39848         "rs",
39849         "381"
39850       ],
39851       [
39852         "Seychelles",
39853         "sc",
39854         "248"
39855       ],
39856       [
39857         "Sierra Leone",
39858         "sl",
39859         "232"
39860       ],
39861       [
39862         "Singapore",
39863         "sg",
39864         "65"
39865       ],
39866       [
39867         "Sint Maarten",
39868         "sx",
39869         "1721"
39870       ],
39871       [
39872         "Slovakia (Slovensko)",
39873         "sk",
39874         "421"
39875       ],
39876       [
39877         "Slovenia (Slovenija)",
39878         "si",
39879         "386"
39880       ],
39881       [
39882         "Solomon Islands",
39883         "sb",
39884         "677"
39885       ],
39886       [
39887         "Somalia (Soomaaliya)",
39888         "so",
39889         "252"
39890       ],
39891       [
39892         "South Africa",
39893         "za",
39894         "27"
39895       ],
39896       [
39897         "South Korea (대한민국)",
39898         "kr",
39899         "82"
39900       ],
39901       [
39902         "South Sudan (‫جنوب السودان‬‎)",
39903         "ss",
39904         "211"
39905       ],
39906       [
39907         "Spain (España)",
39908         "es",
39909         "34"
39910       ],
39911       [
39912         "Sri Lanka (ශ්‍රී ලංකාව)",
39913         "lk",
39914         "94"
39915       ],
39916       [
39917         "Sudan (‫السودان‬‎)",
39918         "sd",
39919         "249"
39920       ],
39921       [
39922         "Suriname",
39923         "sr",
39924         "597"
39925       ],
39926       [
39927         "Svalbard and Jan Mayen",
39928         "sj",
39929         "47",
39930         1
39931       ],
39932       [
39933         "Swaziland",
39934         "sz",
39935         "268"
39936       ],
39937       [
39938         "Sweden (Sverige)",
39939         "se",
39940         "46"
39941       ],
39942       [
39943         "Switzerland (Schweiz)",
39944         "ch",
39945         "41"
39946       ],
39947       [
39948         "Syria (‫سوريا‬‎)",
39949         "sy",
39950         "963"
39951       ],
39952       [
39953         "Taiwan (台灣)",
39954         "tw",
39955         "886"
39956       ],
39957       [
39958         "Tajikistan",
39959         "tj",
39960         "992"
39961       ],
39962       [
39963         "Tanzania",
39964         "tz",
39965         "255"
39966       ],
39967       [
39968         "Thailand (ไทย)",
39969         "th",
39970         "66"
39971       ],
39972       [
39973         "Timor-Leste",
39974         "tl",
39975         "670"
39976       ],
39977       [
39978         "Togo",
39979         "tg",
39980         "228"
39981       ],
39982       [
39983         "Tokelau",
39984         "tk",
39985         "690"
39986       ],
39987       [
39988         "Tonga",
39989         "to",
39990         "676"
39991       ],
39992       [
39993         "Trinidad and Tobago",
39994         "tt",
39995         "1868"
39996       ],
39997       [
39998         "Tunisia (‫تونس‬‎)",
39999         "tn",
40000         "216"
40001       ],
40002       [
40003         "Turkey (Türkiye)",
40004         "tr",
40005         "90"
40006       ],
40007       [
40008         "Turkmenistan",
40009         "tm",
40010         "993"
40011       ],
40012       [
40013         "Turks and Caicos Islands",
40014         "tc",
40015         "1649"
40016       ],
40017       [
40018         "Tuvalu",
40019         "tv",
40020         "688"
40021       ],
40022       [
40023         "U.S. Virgin Islands",
40024         "vi",
40025         "1340"
40026       ],
40027       [
40028         "Uganda",
40029         "ug",
40030         "256"
40031       ],
40032       [
40033         "Ukraine (Україна)",
40034         "ua",
40035         "380"
40036       ],
40037       [
40038         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
40039         "ae",
40040         "971"
40041       ],
40042       [
40043         "United Kingdom",
40044         "gb",
40045         "44",
40046         0
40047       ],
40048       [
40049         "United States",
40050         "us",
40051         "1",
40052         0
40053       ],
40054       [
40055         "Uruguay",
40056         "uy",
40057         "598"
40058       ],
40059       [
40060         "Uzbekistan (Oʻzbekiston)",
40061         "uz",
40062         "998"
40063       ],
40064       [
40065         "Vanuatu",
40066         "vu",
40067         "678"
40068       ],
40069       [
40070         "Vatican City (Città del Vaticano)",
40071         "va",
40072         "39",
40073         1
40074       ],
40075       [
40076         "Venezuela",
40077         "ve",
40078         "58"
40079       ],
40080       [
40081         "Vietnam (Việt Nam)",
40082         "vn",
40083         "84"
40084       ],
40085       [
40086         "Wallis and Futuna (Wallis-et-Futuna)",
40087         "wf",
40088         "681"
40089       ],
40090       [
40091         "Western Sahara (‫الصحراء الغربية‬‎)",
40092         "eh",
40093         "212",
40094         1
40095       ],
40096       [
40097         "Yemen (‫اليمن‬‎)",
40098         "ye",
40099         "967"
40100       ],
40101       [
40102         "Zambia",
40103         "zm",
40104         "260"
40105       ],
40106       [
40107         "Zimbabwe",
40108         "zw",
40109         "263"
40110       ],
40111       [
40112         "Åland Islands",
40113         "ax",
40114         "358",
40115         1
40116       ]
40117   ];
40118   
40119   return d;
40120 }/**
40121 *    This script refer to:
40122 *    Title: International Telephone Input
40123 *    Author: Jack O'Connor
40124 *    Code version:  v12.1.12
40125 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40126 **/
40127
40128 /**
40129  * @class Roo.bootstrap.PhoneInput
40130  * @extends Roo.bootstrap.TriggerField
40131  * An input with International dial-code selection
40132  
40133  * @cfg {String} defaultDialCode default '+852'
40134  * @cfg {Array} preferedCountries default []
40135   
40136  * @constructor
40137  * Create a new PhoneInput.
40138  * @param {Object} config Configuration options
40139  */
40140
40141 Roo.bootstrap.PhoneInput = function(config) {
40142     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40143 };
40144
40145 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40146         
40147         listWidth: undefined,
40148         
40149         selectedClass: 'active',
40150         
40151         invalidClass : "has-warning",
40152         
40153         validClass: 'has-success',
40154         
40155         allowed: '0123456789',
40156         
40157         max_length: 15,
40158         
40159         /**
40160          * @cfg {String} defaultDialCode The default dial code when initializing the input
40161          */
40162         defaultDialCode: '+852',
40163         
40164         /**
40165          * @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
40166          */
40167         preferedCountries: false,
40168         
40169         getAutoCreate : function()
40170         {
40171             var data = Roo.bootstrap.PhoneInputData();
40172             var align = this.labelAlign || this.parentLabelAlign();
40173             var id = Roo.id();
40174             
40175             this.allCountries = [];
40176             this.dialCodeMapping = [];
40177             
40178             for (var i = 0; i < data.length; i++) {
40179               var c = data[i];
40180               this.allCountries[i] = {
40181                 name: c[0],
40182                 iso2: c[1],
40183                 dialCode: c[2],
40184                 priority: c[3] || 0,
40185                 areaCodes: c[4] || null
40186               };
40187               this.dialCodeMapping[c[2]] = {
40188                   name: c[0],
40189                   iso2: c[1],
40190                   priority: c[3] || 0,
40191                   areaCodes: c[4] || null
40192               };
40193             }
40194             
40195             var cfg = {
40196                 cls: 'form-group',
40197                 cn: []
40198             };
40199             
40200             var input =  {
40201                 tag: 'input',
40202                 id : id,
40203                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40204                 maxlength: this.max_length,
40205                 cls : 'form-control tel-input',
40206                 autocomplete: 'new-password'
40207             };
40208             
40209             var hiddenInput = {
40210                 tag: 'input',
40211                 type: 'hidden',
40212                 cls: 'hidden-tel-input'
40213             };
40214             
40215             if (this.name) {
40216                 hiddenInput.name = this.name;
40217             }
40218             
40219             if (this.disabled) {
40220                 input.disabled = true;
40221             }
40222             
40223             var flag_container = {
40224                 tag: 'div',
40225                 cls: 'flag-box',
40226                 cn: [
40227                     {
40228                         tag: 'div',
40229                         cls: 'flag'
40230                     },
40231                     {
40232                         tag: 'div',
40233                         cls: 'caret'
40234                     }
40235                 ]
40236             };
40237             
40238             var box = {
40239                 tag: 'div',
40240                 cls: this.hasFeedback ? 'has-feedback' : '',
40241                 cn: [
40242                     hiddenInput,
40243                     input,
40244                     {
40245                         tag: 'input',
40246                         cls: 'dial-code-holder',
40247                         disabled: true
40248                     }
40249                 ]
40250             };
40251             
40252             var container = {
40253                 cls: 'roo-select2-container input-group',
40254                 cn: [
40255                     flag_container,
40256                     box
40257                 ]
40258             };
40259             
40260             if (this.fieldLabel.length) {
40261                 var indicator = {
40262                     tag: 'i',
40263                     tooltip: 'This field is required'
40264                 };
40265                 
40266                 var label = {
40267                     tag: 'label',
40268                     'for':  id,
40269                     cls: 'control-label',
40270                     cn: []
40271                 };
40272                 
40273                 var label_text = {
40274                     tag: 'span',
40275                     html: this.fieldLabel
40276                 };
40277                 
40278                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40279                 label.cn = [
40280                     indicator,
40281                     label_text
40282                 ];
40283                 
40284                 if(this.indicatorpos == 'right') {
40285                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40286                     label.cn = [
40287                         label_text,
40288                         indicator
40289                     ];
40290                 }
40291                 
40292                 if(align == 'left') {
40293                     container = {
40294                         tag: 'div',
40295                         cn: [
40296                             container
40297                         ]
40298                     };
40299                     
40300                     if(this.labelWidth > 12){
40301                         label.style = "width: " + this.labelWidth + 'px';
40302                     }
40303                     if(this.labelWidth < 13 && this.labelmd == 0){
40304                         this.labelmd = this.labelWidth;
40305                     }
40306                     if(this.labellg > 0){
40307                         label.cls += ' col-lg-' + this.labellg;
40308                         input.cls += ' col-lg-' + (12 - this.labellg);
40309                     }
40310                     if(this.labelmd > 0){
40311                         label.cls += ' col-md-' + this.labelmd;
40312                         container.cls += ' col-md-' + (12 - this.labelmd);
40313                     }
40314                     if(this.labelsm > 0){
40315                         label.cls += ' col-sm-' + this.labelsm;
40316                         container.cls += ' col-sm-' + (12 - this.labelsm);
40317                     }
40318                     if(this.labelxs > 0){
40319                         label.cls += ' col-xs-' + this.labelxs;
40320                         container.cls += ' col-xs-' + (12 - this.labelxs);
40321                     }
40322                 }
40323             }
40324             
40325             cfg.cn = [
40326                 label,
40327                 container
40328             ];
40329             
40330             var settings = this;
40331             
40332             ['xs','sm','md','lg'].map(function(size){
40333                 if (settings[size]) {
40334                     cfg.cls += ' col-' + size + '-' + settings[size];
40335                 }
40336             });
40337             
40338             this.store = new Roo.data.Store({
40339                 proxy : new Roo.data.MemoryProxy({}),
40340                 reader : new Roo.data.JsonReader({
40341                     fields : [
40342                         {
40343                             'name' : 'name',
40344                             'type' : 'string'
40345                         },
40346                         {
40347                             'name' : 'iso2',
40348                             'type' : 'string'
40349                         },
40350                         {
40351                             'name' : 'dialCode',
40352                             'type' : 'string'
40353                         },
40354                         {
40355                             'name' : 'priority',
40356                             'type' : 'string'
40357                         },
40358                         {
40359                             'name' : 'areaCodes',
40360                             'type' : 'string'
40361                         }
40362                     ]
40363                 })
40364             });
40365             
40366             if(!this.preferedCountries) {
40367                 this.preferedCountries = [
40368                     'hk',
40369                     'gb',
40370                     'us'
40371                 ];
40372             }
40373             
40374             var p = this.preferedCountries.reverse();
40375             
40376             if(p) {
40377                 for (var i = 0; i < p.length; i++) {
40378                     for (var j = 0; j < this.allCountries.length; j++) {
40379                         if(this.allCountries[j].iso2 == p[i]) {
40380                             var t = this.allCountries[j];
40381                             this.allCountries.splice(j,1);
40382                             this.allCountries.unshift(t);
40383                         }
40384                     } 
40385                 }
40386             }
40387             
40388             this.store.proxy.data = {
40389                 success: true,
40390                 data: this.allCountries
40391             };
40392             
40393             return cfg;
40394         },
40395         
40396         initEvents : function()
40397         {
40398             this.createList();
40399             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40400             
40401             this.indicator = this.indicatorEl();
40402             this.flag = this.flagEl();
40403             this.dialCodeHolder = this.dialCodeHolderEl();
40404             
40405             this.trigger = this.el.select('div.flag-box',true).first();
40406             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40407             
40408             var _this = this;
40409             
40410             (function(){
40411                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40412                 _this.list.setWidth(lw);
40413             }).defer(100);
40414             
40415             this.list.on('mouseover', this.onViewOver, this);
40416             this.list.on('mousemove', this.onViewMove, this);
40417             this.inputEl().on("keyup", this.onKeyUp, this);
40418             this.inputEl().on("keypress", this.onKeyPress, this);
40419             
40420             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40421
40422             this.view = new Roo.View(this.list, this.tpl, {
40423                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40424             });
40425             
40426             this.view.on('click', this.onViewClick, this);
40427             this.setValue(this.defaultDialCode);
40428         },
40429         
40430         onTriggerClick : function(e)
40431         {
40432             Roo.log('trigger click');
40433             if(this.disabled){
40434                 return;
40435             }
40436             
40437             if(this.isExpanded()){
40438                 this.collapse();
40439                 this.hasFocus = false;
40440             }else {
40441                 this.store.load({});
40442                 this.hasFocus = true;
40443                 this.expand();
40444             }
40445         },
40446         
40447         isExpanded : function()
40448         {
40449             return this.list.isVisible();
40450         },
40451         
40452         collapse : function()
40453         {
40454             if(!this.isExpanded()){
40455                 return;
40456             }
40457             this.list.hide();
40458             Roo.get(document).un('mousedown', this.collapseIf, this);
40459             Roo.get(document).un('mousewheel', this.collapseIf, this);
40460             this.fireEvent('collapse', this);
40461             this.validate();
40462         },
40463         
40464         expand : function()
40465         {
40466             Roo.log('expand');
40467
40468             if(this.isExpanded() || !this.hasFocus){
40469                 return;
40470             }
40471             
40472             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40473             this.list.setWidth(lw);
40474             
40475             this.list.show();
40476             this.restrictHeight();
40477             
40478             Roo.get(document).on('mousedown', this.collapseIf, this);
40479             Roo.get(document).on('mousewheel', this.collapseIf, this);
40480             
40481             this.fireEvent('expand', this);
40482         },
40483         
40484         restrictHeight : function()
40485         {
40486             this.list.alignTo(this.inputEl(), this.listAlign);
40487             this.list.alignTo(this.inputEl(), this.listAlign);
40488         },
40489         
40490         onViewOver : function(e, t)
40491         {
40492             if(this.inKeyMode){
40493                 return;
40494             }
40495             var item = this.view.findItemFromChild(t);
40496             
40497             if(item){
40498                 var index = this.view.indexOf(item);
40499                 this.select(index, false);
40500             }
40501         },
40502
40503         // private
40504         onViewClick : function(view, doFocus, el, e)
40505         {
40506             var index = this.view.getSelectedIndexes()[0];
40507             
40508             var r = this.store.getAt(index);
40509             
40510             if(r){
40511                 this.onSelect(r, index);
40512             }
40513             if(doFocus !== false && !this.blockFocus){
40514                 this.inputEl().focus();
40515             }
40516         },
40517         
40518         onViewMove : function(e, t)
40519         {
40520             this.inKeyMode = false;
40521         },
40522         
40523         select : function(index, scrollIntoView)
40524         {
40525             this.selectedIndex = index;
40526             this.view.select(index);
40527             if(scrollIntoView !== false){
40528                 var el = this.view.getNode(index);
40529                 if(el){
40530                     this.list.scrollChildIntoView(el, false);
40531                 }
40532             }
40533         },
40534         
40535         createList : function()
40536         {
40537             this.list = Roo.get(document.body).createChild({
40538                 tag: 'ul',
40539                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40540                 style: 'display:none'
40541             });
40542             
40543             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40544         },
40545         
40546         collapseIf : function(e)
40547         {
40548             var in_combo  = e.within(this.el);
40549             var in_list =  e.within(this.list);
40550             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40551             
40552             if (in_combo || in_list || is_list) {
40553                 return;
40554             }
40555             this.collapse();
40556         },
40557         
40558         onSelect : function(record, index)
40559         {
40560             if(this.fireEvent('beforeselect', this, record, index) !== false){
40561                 
40562                 this.setFlagClass(record.data.iso2);
40563                 this.setDialCode(record.data.dialCode);
40564                 this.hasFocus = false;
40565                 this.collapse();
40566                 this.fireEvent('select', this, record, index);
40567             }
40568         },
40569         
40570         flagEl : function()
40571         {
40572             var flag = this.el.select('div.flag',true).first();
40573             if(!flag){
40574                 return false;
40575             }
40576             return flag;
40577         },
40578         
40579         dialCodeHolderEl : function()
40580         {
40581             var d = this.el.select('input.dial-code-holder',true).first();
40582             if(!d){
40583                 return false;
40584             }
40585             return d;
40586         },
40587         
40588         setDialCode : function(v)
40589         {
40590             this.dialCodeHolder.dom.value = '+'+v;
40591         },
40592         
40593         setFlagClass : function(n)
40594         {
40595             this.flag.dom.className = 'flag '+n;
40596         },
40597         
40598         getValue : function()
40599         {
40600             var v = this.inputEl().getValue();
40601             if(this.dialCodeHolder) {
40602                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40603             }
40604             return v;
40605         },
40606         
40607         setValue : function(v)
40608         {
40609             var d = this.getDialCode(v);
40610             
40611             //invalid dial code
40612             if(v.length == 0 || !d || d.length == 0) {
40613                 if(this.rendered){
40614                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40615                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40616                 }
40617                 return;
40618             }
40619             
40620             //valid dial code
40621             this.setFlagClass(this.dialCodeMapping[d].iso2);
40622             this.setDialCode(d);
40623             this.inputEl().dom.value = v.replace('+'+d,'');
40624             this.hiddenEl().dom.value = this.getValue();
40625             
40626             this.validate();
40627         },
40628         
40629         getDialCode : function(v)
40630         {
40631             v = v ||  '';
40632             
40633             if (v.length == 0) {
40634                 return this.dialCodeHolder.dom.value;
40635             }
40636             
40637             var dialCode = "";
40638             if (v.charAt(0) != "+") {
40639                 return false;
40640             }
40641             var numericChars = "";
40642             for (var i = 1; i < v.length; i++) {
40643               var c = v.charAt(i);
40644               if (!isNaN(c)) {
40645                 numericChars += c;
40646                 if (this.dialCodeMapping[numericChars]) {
40647                   dialCode = v.substr(1, i);
40648                 }
40649                 if (numericChars.length == 4) {
40650                   break;
40651                 }
40652               }
40653             }
40654             return dialCode;
40655         },
40656         
40657         reset : function()
40658         {
40659             this.setValue(this.defaultDialCode);
40660             this.validate();
40661         },
40662         
40663         hiddenEl : function()
40664         {
40665             return this.el.select('input.hidden-tel-input',true).first();
40666         },
40667         
40668         // after setting val
40669         onKeyUp : function(e){
40670             this.setValue(this.getValue());
40671         },
40672         
40673         onKeyPress : function(e){
40674             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40675                 e.stopEvent();
40676             }
40677         }
40678         
40679 });
40680 /**
40681  * @class Roo.bootstrap.MoneyField
40682  * @extends Roo.bootstrap.ComboBox
40683  * Bootstrap MoneyField class
40684  * 
40685  * @constructor
40686  * Create a new MoneyField.
40687  * @param {Object} config Configuration options
40688  */
40689
40690 Roo.bootstrap.MoneyField = function(config) {
40691     
40692     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40693     
40694 };
40695
40696 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40697     
40698     /**
40699      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40700      */
40701     allowDecimals : true,
40702     /**
40703      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40704      */
40705     decimalSeparator : ".",
40706     /**
40707      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40708      */
40709     decimalPrecision : 0,
40710     /**
40711      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40712      */
40713     allowNegative : true,
40714     /**
40715      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40716      */
40717     allowZero: true,
40718     /**
40719      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40720      */
40721     minValue : Number.NEGATIVE_INFINITY,
40722     /**
40723      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40724      */
40725     maxValue : Number.MAX_VALUE,
40726     /**
40727      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40728      */
40729     minText : "The minimum value for this field is {0}",
40730     /**
40731      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40732      */
40733     maxText : "The maximum value for this field is {0}",
40734     /**
40735      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40736      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40737      */
40738     nanText : "{0} is not a valid number",
40739     /**
40740      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40741      */
40742     castInt : true,
40743     /**
40744      * @cfg {String} defaults currency of the MoneyField
40745      * value should be in lkey
40746      */
40747     defaultCurrency : false,
40748     /**
40749      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40750      */
40751     thousandsDelimiter : false,
40752     /**
40753      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40754      */
40755     max_length: false,
40756     
40757     inputlg : 9,
40758     inputmd : 9,
40759     inputsm : 9,
40760     inputxs : 6,
40761     
40762     store : false,
40763     
40764     getAutoCreate : function()
40765     {
40766         var align = this.labelAlign || this.parentLabelAlign();
40767         
40768         var id = Roo.id();
40769
40770         var cfg = {
40771             cls: 'form-group',
40772             cn: []
40773         };
40774
40775         var input =  {
40776             tag: 'input',
40777             id : id,
40778             cls : 'form-control roo-money-amount-input',
40779             autocomplete: 'new-password'
40780         };
40781         
40782         var hiddenInput = {
40783             tag: 'input',
40784             type: 'hidden',
40785             id: Roo.id(),
40786             cls: 'hidden-number-input'
40787         };
40788         
40789         if(this.max_length) {
40790             input.maxlength = this.max_length; 
40791         }
40792         
40793         if (this.name) {
40794             hiddenInput.name = this.name;
40795         }
40796
40797         if (this.disabled) {
40798             input.disabled = true;
40799         }
40800
40801         var clg = 12 - this.inputlg;
40802         var cmd = 12 - this.inputmd;
40803         var csm = 12 - this.inputsm;
40804         var cxs = 12 - this.inputxs;
40805         
40806         var container = {
40807             tag : 'div',
40808             cls : 'row roo-money-field',
40809             cn : [
40810                 {
40811                     tag : 'div',
40812                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40813                     cn : [
40814                         {
40815                             tag : 'div',
40816                             cls: 'roo-select2-container input-group',
40817                             cn: [
40818                                 {
40819                                     tag : 'input',
40820                                     cls : 'form-control roo-money-currency-input',
40821                                     autocomplete: 'new-password',
40822                                     readOnly : 1,
40823                                     name : this.currencyName
40824                                 },
40825                                 {
40826                                     tag :'span',
40827                                     cls : 'input-group-addon',
40828                                     cn : [
40829                                         {
40830                                             tag: 'span',
40831                                             cls: 'caret'
40832                                         }
40833                                     ]
40834                                 }
40835                             ]
40836                         }
40837                     ]
40838                 },
40839                 {
40840                     tag : 'div',
40841                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40842                     cn : [
40843                         {
40844                             tag: 'div',
40845                             cls: this.hasFeedback ? 'has-feedback' : '',
40846                             cn: [
40847                                 input
40848                             ]
40849                         }
40850                     ]
40851                 }
40852             ]
40853             
40854         };
40855         
40856         if (this.fieldLabel.length) {
40857             var indicator = {
40858                 tag: 'i',
40859                 tooltip: 'This field is required'
40860             };
40861
40862             var label = {
40863                 tag: 'label',
40864                 'for':  id,
40865                 cls: 'control-label',
40866                 cn: []
40867             };
40868
40869             var label_text = {
40870                 tag: 'span',
40871                 html: this.fieldLabel
40872             };
40873
40874             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40875             label.cn = [
40876                 indicator,
40877                 label_text
40878             ];
40879
40880             if(this.indicatorpos == 'right') {
40881                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40882                 label.cn = [
40883                     label_text,
40884                     indicator
40885                 ];
40886             }
40887
40888             if(align == 'left') {
40889                 container = {
40890                     tag: 'div',
40891                     cn: [
40892                         container
40893                     ]
40894                 };
40895
40896                 if(this.labelWidth > 12){
40897                     label.style = "width: " + this.labelWidth + 'px';
40898                 }
40899                 if(this.labelWidth < 13 && this.labelmd == 0){
40900                     this.labelmd = this.labelWidth;
40901                 }
40902                 if(this.labellg > 0){
40903                     label.cls += ' col-lg-' + this.labellg;
40904                     input.cls += ' col-lg-' + (12 - this.labellg);
40905                 }
40906                 if(this.labelmd > 0){
40907                     label.cls += ' col-md-' + this.labelmd;
40908                     container.cls += ' col-md-' + (12 - this.labelmd);
40909                 }
40910                 if(this.labelsm > 0){
40911                     label.cls += ' col-sm-' + this.labelsm;
40912                     container.cls += ' col-sm-' + (12 - this.labelsm);
40913                 }
40914                 if(this.labelxs > 0){
40915                     label.cls += ' col-xs-' + this.labelxs;
40916                     container.cls += ' col-xs-' + (12 - this.labelxs);
40917                 }
40918             }
40919         }
40920
40921         cfg.cn = [
40922             label,
40923             container,
40924             hiddenInput
40925         ];
40926         
40927         var settings = this;
40928
40929         ['xs','sm','md','lg'].map(function(size){
40930             if (settings[size]) {
40931                 cfg.cls += ' col-' + size + '-' + settings[size];
40932             }
40933         });
40934         
40935         return cfg;
40936     },
40937     
40938     initEvents : function()
40939     {
40940         this.indicator = this.indicatorEl();
40941         
40942         this.initCurrencyEvent();
40943         
40944         this.initNumberEvent();
40945     },
40946     
40947     initCurrencyEvent : function()
40948     {
40949         if (!this.store) {
40950             throw "can not find store for combo";
40951         }
40952         
40953         this.store = Roo.factory(this.store, Roo.data);
40954         this.store.parent = this;
40955         
40956         this.createList();
40957         
40958         this.triggerEl = this.el.select('.input-group-addon', true).first();
40959         
40960         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40961         
40962         var _this = this;
40963         
40964         (function(){
40965             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40966             _this.list.setWidth(lw);
40967         }).defer(100);
40968         
40969         this.list.on('mouseover', this.onViewOver, this);
40970         this.list.on('mousemove', this.onViewMove, this);
40971         this.list.on('scroll', this.onViewScroll, this);
40972         
40973         if(!this.tpl){
40974             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40975         }
40976         
40977         this.view = new Roo.View(this.list, this.tpl, {
40978             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40979         });
40980         
40981         this.view.on('click', this.onViewClick, this);
40982         
40983         this.store.on('beforeload', this.onBeforeLoad, this);
40984         this.store.on('load', this.onLoad, this);
40985         this.store.on('loadexception', this.onLoadException, this);
40986         
40987         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40988             "up" : function(e){
40989                 this.inKeyMode = true;
40990                 this.selectPrev();
40991             },
40992
40993             "down" : function(e){
40994                 if(!this.isExpanded()){
40995                     this.onTriggerClick();
40996                 }else{
40997                     this.inKeyMode = true;
40998                     this.selectNext();
40999                 }
41000             },
41001
41002             "enter" : function(e){
41003                 this.collapse();
41004                 
41005                 if(this.fireEvent("specialkey", this, e)){
41006                     this.onViewClick(false);
41007                 }
41008                 
41009                 return true;
41010             },
41011
41012             "esc" : function(e){
41013                 this.collapse();
41014             },
41015
41016             "tab" : 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             scope : this,
41027
41028             doRelay : function(foo, bar, hname){
41029                 if(hname == 'down' || this.scope.isExpanded()){
41030                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41031                 }
41032                 return true;
41033             },
41034
41035             forceKeyDown: true
41036         });
41037         
41038         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41039         
41040     },
41041     
41042     initNumberEvent : function(e)
41043     {
41044         this.inputEl().on("keydown" , this.fireKey,  this);
41045         this.inputEl().on("focus", this.onFocus,  this);
41046         this.inputEl().on("blur", this.onBlur,  this);
41047         
41048         this.inputEl().relayEvent('keyup', this);
41049         
41050         if(this.indicator){
41051             this.indicator.addClass('invisible');
41052         }
41053  
41054         this.originalValue = this.getValue();
41055         
41056         if(this.validationEvent == 'keyup'){
41057             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41058             this.inputEl().on('keyup', this.filterValidation, this);
41059         }
41060         else if(this.validationEvent !== false){
41061             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41062         }
41063         
41064         if(this.selectOnFocus){
41065             this.on("focus", this.preFocus, this);
41066             
41067         }
41068         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41069             this.inputEl().on("keypress", this.filterKeys, this);
41070         } else {
41071             this.inputEl().relayEvent('keypress', this);
41072         }
41073         
41074         var allowed = "0123456789";
41075         
41076         if(this.allowDecimals){
41077             allowed += this.decimalSeparator;
41078         }
41079         
41080         if(this.allowNegative){
41081             allowed += "-";
41082         }
41083         
41084         if(this.thousandsDelimiter) {
41085             allowed += ",";
41086         }
41087         
41088         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41089         
41090         var keyPress = function(e){
41091             
41092             var k = e.getKey();
41093             
41094             var c = e.getCharCode();
41095             
41096             if(
41097                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41098                     allowed.indexOf(String.fromCharCode(c)) === -1
41099             ){
41100                 e.stopEvent();
41101                 return;
41102             }
41103             
41104             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41105                 return;
41106             }
41107             
41108             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41109                 e.stopEvent();
41110             }
41111         };
41112         
41113         this.inputEl().on("keypress", keyPress, this);
41114         
41115     },
41116     
41117     onTriggerClick : function(e)
41118     {   
41119         if(this.disabled){
41120             return;
41121         }
41122         
41123         this.page = 0;
41124         this.loadNext = false;
41125         
41126         if(this.isExpanded()){
41127             this.collapse();
41128             return;
41129         }
41130         
41131         this.hasFocus = true;
41132         
41133         if(this.triggerAction == 'all') {
41134             this.doQuery(this.allQuery, true);
41135             return;
41136         }
41137         
41138         this.doQuery(this.getRawValue());
41139     },
41140     
41141     getCurrency : function()
41142     {   
41143         var v = this.currencyEl().getValue();
41144         
41145         return v;
41146     },
41147     
41148     restrictHeight : function()
41149     {
41150         this.list.alignTo(this.currencyEl(), this.listAlign);
41151         this.list.alignTo(this.currencyEl(), this.listAlign);
41152     },
41153     
41154     onViewClick : function(view, doFocus, el, e)
41155     {
41156         var index = this.view.getSelectedIndexes()[0];
41157         
41158         var r = this.store.getAt(index);
41159         
41160         if(r){
41161             this.onSelect(r, index);
41162         }
41163     },
41164     
41165     onSelect : function(record, index){
41166         
41167         if(this.fireEvent('beforeselect', this, record, index) !== false){
41168         
41169             this.setFromCurrencyData(index > -1 ? record.data : false);
41170             
41171             this.collapse();
41172             
41173             this.fireEvent('select', this, record, index);
41174         }
41175     },
41176     
41177     setFromCurrencyData : function(o)
41178     {
41179         var currency = '';
41180         
41181         this.lastCurrency = o;
41182         
41183         if (this.currencyField) {
41184             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41185         } else {
41186             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41187         }
41188         
41189         this.lastSelectionText = currency;
41190         
41191         //setting default currency
41192         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41193             this.setCurrency(this.defaultCurrency);
41194             return;
41195         }
41196         
41197         this.setCurrency(currency);
41198     },
41199     
41200     setFromData : function(o)
41201     {
41202         var c = {};
41203         
41204         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41205         
41206         this.setFromCurrencyData(c);
41207         
41208         var value = '';
41209         
41210         if (this.name) {
41211             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41212         } else {
41213             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41214         }
41215         
41216         this.setValue(value);
41217         
41218     },
41219     
41220     setCurrency : function(v)
41221     {   
41222         this.currencyValue = v;
41223         
41224         if(this.rendered){
41225             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41226             this.validate();
41227         }
41228     },
41229     
41230     setValue : function(v)
41231     {
41232         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41233         
41234         this.value = v;
41235         
41236         if(this.rendered){
41237             
41238             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41239             
41240             this.inputEl().dom.value = (v == '') ? '' :
41241                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41242             
41243             if(!this.allowZero && v === '0') {
41244                 this.hiddenEl().dom.value = '';
41245                 this.inputEl().dom.value = '';
41246             }
41247             
41248             this.validate();
41249         }
41250     },
41251     
41252     getRawValue : function()
41253     {
41254         var v = this.inputEl().getValue();
41255         
41256         return v;
41257     },
41258     
41259     getValue : function()
41260     {
41261         return this.fixPrecision(this.parseValue(this.getRawValue()));
41262     },
41263     
41264     parseValue : function(value)
41265     {
41266         if(this.thousandsDelimiter) {
41267             value += "";
41268             r = new RegExp(",", "g");
41269             value = value.replace(r, "");
41270         }
41271         
41272         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41273         return isNaN(value) ? '' : value;
41274         
41275     },
41276     
41277     fixPrecision : function(value)
41278     {
41279         if(this.thousandsDelimiter) {
41280             value += "";
41281             r = new RegExp(",", "g");
41282             value = value.replace(r, "");
41283         }
41284         
41285         var nan = isNaN(value);
41286         
41287         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41288             return nan ? '' : value;
41289         }
41290         return parseFloat(value).toFixed(this.decimalPrecision);
41291     },
41292     
41293     decimalPrecisionFcn : function(v)
41294     {
41295         return Math.floor(v);
41296     },
41297     
41298     validateValue : function(value)
41299     {
41300         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41301             return false;
41302         }
41303         
41304         var num = this.parseValue(value);
41305         
41306         if(isNaN(num)){
41307             this.markInvalid(String.format(this.nanText, value));
41308             return false;
41309         }
41310         
41311         if(num < this.minValue){
41312             this.markInvalid(String.format(this.minText, this.minValue));
41313             return false;
41314         }
41315         
41316         if(num > this.maxValue){
41317             this.markInvalid(String.format(this.maxText, this.maxValue));
41318             return false;
41319         }
41320         
41321         return true;
41322     },
41323     
41324     validate : function()
41325     {
41326         if(this.disabled || this.allowBlank){
41327             this.markValid();
41328             return true;
41329         }
41330         
41331         var currency = this.getCurrency();
41332         
41333         if(this.validateValue(this.getRawValue()) && currency.length){
41334             this.markValid();
41335             return true;
41336         }
41337         
41338         this.markInvalid();
41339         return false;
41340     },
41341     
41342     getName: function()
41343     {
41344         return this.name;
41345     },
41346     
41347     beforeBlur : function()
41348     {
41349         if(!this.castInt){
41350             return;
41351         }
41352         
41353         var v = this.parseValue(this.getRawValue());
41354         
41355         if(v || v == 0){
41356             this.setValue(v);
41357         }
41358     },
41359     
41360     onBlur : function()
41361     {
41362         this.beforeBlur();
41363         
41364         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41365             //this.el.removeClass(this.focusClass);
41366         }
41367         
41368         this.hasFocus = false;
41369         
41370         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41371             this.validate();
41372         }
41373         
41374         var v = this.getValue();
41375         
41376         if(String(v) !== String(this.startValue)){
41377             this.fireEvent('change', this, v, this.startValue);
41378         }
41379         
41380         this.fireEvent("blur", this);
41381     },
41382     
41383     inputEl : function()
41384     {
41385         return this.el.select('.roo-money-amount-input', true).first();
41386     },
41387     
41388     currencyEl : function()
41389     {
41390         return this.el.select('.roo-money-currency-input', true).first();
41391     },
41392     
41393     hiddenEl : function()
41394     {
41395         return this.el.select('input.hidden-number-input',true).first();
41396     }
41397     
41398 });