roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets[0], function(s) {
10                     if (s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })();/*
16  * - LGPL
17  *
18  * base class for bootstrap elements.
19  * 
20  */
21
22 Roo.bootstrap = Roo.bootstrap || {};
23 /**
24  * @class Roo.bootstrap.Component
25  * @extends Roo.Component
26  * Bootstrap Component base class
27  * @cfg {String} cls css class
28  * @cfg {String} style any extra css
29  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
31  * @cfg {string} dataId cutomer id
32  * @cfg {string} name Specifies name attribute
33  * @cfg {string} tooltip  Text for the tooltip
34  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
35  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
36  
37  * @constructor
38  * Do not use directly - it does not do anything..
39  * @param {Object} config The config object
40  */
41
42
43
44 Roo.bootstrap.Component = function(config){
45     Roo.bootstrap.Component.superclass.constructor.call(this, config);
46        
47     this.addEvents({
48         /**
49          * @event childrenrendered
50          * Fires when the children have been rendered..
51          * @param {Roo.bootstrap.Component} this
52          */
53         "childrenrendered" : true
54         
55         
56         
57     });
58     
59     
60 };
61
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
63     
64     
65     allowDomMove : false, // to stop relocations in parent onRender...
66     
67     cls : false,
68     
69     style : false,
70     
71     autoCreate : false,
72     
73     tooltip : null,
74     /**
75      * Initialize Events for the element
76      */
77     initEvents : function() { },
78     
79     xattr : false,
80     
81     parentId : false,
82     
83     can_build_overlaid : true,
84     
85     container_method : false,
86     
87     dataId : false,
88     
89     name : false,
90     
91     parent: function() {
92         // returns the parent component..
93         return Roo.ComponentMgr.get(this.parentId)
94         
95         
96     },
97     
98     // private
99     onRender : function(ct, position)
100     {
101        // Roo.log("Call onRender: " + this.xtype);
102         
103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
104         
105         if(this.el){
106             if (this.el.attr('xtype')) {
107                 this.el.attr('xtypex', this.el.attr('xtype'));
108                 this.el.dom.removeAttribute('xtype');
109                 
110                 this.initEvents();
111             }
112             
113             return;
114         }
115         
116          
117         
118         var cfg = Roo.apply({},  this.getAutoCreate());
119         
120         cfg.id = this.id || Roo.id();
121         
122         // fill in the extra attributes 
123         if (this.xattr && typeof(this.xattr) =='object') {
124             for (var i in this.xattr) {
125                 cfg[i] = this.xattr[i];
126             }
127         }
128         
129         if(this.dataId){
130             cfg.dataId = this.dataId;
131         }
132         
133         if (this.cls) {
134             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
135         }
136         
137         if (this.style) { // fixme needs to support more complex style data.
138             cfg.style = this.style;
139         }
140         
141         if(this.name){
142             cfg.name = this.name;
143         }
144         
145         this.el = ct.createChild(cfg, position);
146         
147         if (this.tooltip) {
148             this.tooltipEl().attr('tooltip', this.tooltip);
149         }
150         
151         if(this.tabIndex !== undefined){
152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
153         }
154         
155         this.initEvents();
156         
157     },
158     /**
159      * Fetch the element to add children to
160      * @return {Roo.Element} defaults to this.el
161      */
162     getChildContainer : function()
163     {
164         return this.el;
165     },
166     /**
167      * Fetch the element to display the tooltip on.
168      * @return {Roo.Element} defaults to this.el
169      */
170     tooltipEl : function()
171     {
172         return this.el;
173     },
174         
175     addxtype  : function(tree,cntr)
176     {
177         var cn = this;
178         
179         cn = Roo.factory(tree);
180         //Roo.log(['addxtype', cn]);
181            
182         cn.parentType = this.xtype; //??
183         cn.parentId = this.id;
184         
185         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186         if (typeof(cn.container_method) == 'string') {
187             cntr = cn.container_method;
188         }
189         
190         
191         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
192         
193         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
194         
195         var build_from_html =  Roo.XComponent.build_from_html;
196           
197         var is_body  = (tree.xtype == 'Body') ;
198           
199         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
200           
201         var self_cntr_el = Roo.get(this[cntr](false));
202         
203         // do not try and build conditional elements 
204         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
205             return false;
206         }
207         
208         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210                 return this.addxtypeChild(tree,cntr, is_body);
211             }
212             
213             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
214                 
215             if(echild){
216                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
217             }
218             
219             Roo.log('skipping render');
220             return cn;
221             
222         }
223         
224         var ret = false;
225         if (!build_from_html) {
226             return false;
227         }
228         
229         // this i think handles overlaying multiple children of the same type
230         // with the sam eelement.. - which might be buggy..
231         while (true) {
232             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
233             
234             if (!echild) {
235                 break;
236             }
237             
238             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
239                 break;
240             }
241             
242             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
243         }
244        
245         return ret;
246     },
247     
248     
249     addxtypeChild : function (tree, cntr, is_body)
250     {
251         Roo.debug && Roo.log('addxtypeChild:' + cntr);
252         var cn = this;
253         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
254         
255         
256         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257                     (typeof(tree['flexy:foreach']) != 'undefined');
258           
259     
260         
261         skip_children = false;
262         // render the element if it's not BODY.
263         if (!is_body) {
264             
265             // if parent was disabled, then do not try and create the children..
266             if(!this[cntr](true)){
267                 tree.items = [];
268                 return tree;
269             }
270            
271             cn = Roo.factory(tree);
272            
273             cn.parentType = this.xtype; //??
274             cn.parentId = this.id;
275             
276             var build_from_html =  Roo.XComponent.build_from_html;
277             
278             
279             // does the container contain child eleemnts with 'xtype' attributes.
280             // that match this xtype..
281             // note - when we render we create these as well..
282             // so we should check to see if body has xtype set.
283             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
284                
285                 var self_cntr_el = Roo.get(this[cntr](false));
286                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
287                 if (echild) { 
288                     //Roo.log(Roo.XComponent.build_from_html);
289                     //Roo.log("got echild:");
290                     //Roo.log(echild);
291                 }
292                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293                 // and are not displayed -this causes this to use up the wrong element when matching.
294                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
295                 
296                 
297                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
299                   
300                   
301                   
302                     cn.el = echild;
303                   //  Roo.log("GOT");
304                     //echild.dom.removeAttribute('xtype');
305                 } else {
306                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307                     Roo.debug && Roo.log(self_cntr_el);
308                     Roo.debug && Roo.log(echild);
309                     Roo.debug && Roo.log(cn);
310                 }
311             }
312            
313             
314            
315             // if object has flexy:if - then it may or may not be rendered.
316             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
317                 // skip a flexy if element.
318                 Roo.debug && Roo.log('skipping render');
319                 Roo.debug && Roo.log(tree);
320                 if (!cn.el) {
321                     Roo.debug && Roo.log('skipping all children');
322                     skip_children = true;
323                 }
324                 
325              } else {
326                  
327                 // actually if flexy:foreach is found, we really want to create 
328                 // multiple copies here...
329                 //Roo.log('render');
330                 //Roo.log(this[cntr]());
331                 // some elements do not have render methods.. like the layouts...
332                 /*
333                 if(this[cntr](true) === false){
334                     cn.items = [];
335                     return cn;
336                 }
337                 */
338                 cn.render && cn.render(this[cntr](true));
339                 
340              }
341             // then add the element..
342         }
343          
344         // handle the kids..
345         
346         var nitems = [];
347         /*
348         if (typeof (tree.menu) != 'undefined') {
349             tree.menu.parentType = cn.xtype;
350             tree.menu.triggerEl = cn.el;
351             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
352             
353         }
354         */
355         if (!tree.items || !tree.items.length) {
356             cn.items = nitems;
357             //Roo.log(["no children", this]);
358             
359             return cn;
360         }
361          
362         var items = tree.items;
363         delete tree.items;
364         
365         //Roo.log(items.length);
366             // add the items..
367         if (!skip_children) {    
368             for(var i =0;i < items.length;i++) {
369               //  Roo.log(['add child', items[i]]);
370                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
371             }
372         }
373         
374         cn.items = nitems;
375         
376         //Roo.log("fire childrenrendered");
377         
378         cn.fireEvent('childrenrendered', this);
379         
380         return cn;
381     },
382     
383     /**
384      * Set the element that will be used to show or hide
385      */
386     setVisibilityEl : function(el)
387     {
388         this.visibilityEl = el;
389     },
390     
391      /**
392      * Get the element that will be used to show or hide
393      */
394     getVisibilityEl : function()
395     {
396         if (typeof(this.visibilityEl) == 'object') {
397             return this.visibilityEl;
398         }
399         
400         if (typeof(this.visibilityEl) == 'string') {
401             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
402         }
403         
404         return this.getEl();
405     },
406     
407     /**
408      * Show a component - removes 'hidden' class
409      */
410     show : function()
411     {
412         if(!this.getVisibilityEl()){
413             return;
414         }
415          
416         this.getVisibilityEl().removeClass(['hidden','d-none']);
417         
418         this.fireEvent('show', this);
419         
420         
421     },
422     /**
423      * Hide a component - adds 'hidden' class
424      */
425     hide: function()
426     {
427         if(!this.getVisibilityEl()){
428             return;
429         }
430         
431         this.getVisibilityEl().addClass(['hidden','d-none']);
432         
433         this.fireEvent('hide', this);
434         
435     }
436 });
437
438  /*
439  * - LGPL
440  *
441  * Body
442  *
443  */
444
445 /**
446  * @class Roo.bootstrap.Body
447  * @extends Roo.bootstrap.Component
448  * Bootstrap Body class
449  *
450  * @constructor
451  * Create a new body
452  * @param {Object} config The config object
453  */
454
455 Roo.bootstrap.Body = function(config){
456
457     config = config || {};
458
459     Roo.bootstrap.Body.superclass.constructor.call(this, config);
460     this.el = Roo.get(config.el ? config.el : document.body );
461     if (this.cls && this.cls.length) {
462         Roo.get(document.body).addClass(this.cls);
463     }
464 };
465
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
467
468     is_body : true,// just to make sure it's constructed?
469
470         autoCreate : {
471         cls: 'container'
472     },
473     onRender : function(ct, position)
474     {
475        /* Roo.log("Roo.bootstrap.Body - onRender");
476         if (this.cls && this.cls.length) {
477             Roo.get(document.body).addClass(this.cls);
478         }
479         // style??? xttr???
480         */
481     }
482
483
484
485
486 });
487 /*
488  * - LGPL
489  *
490  * button group
491  * 
492  */
493
494
495 /**
496  * @class Roo.bootstrap.ButtonGroup
497  * @extends Roo.bootstrap.Component
498  * Bootstrap ButtonGroup class
499  * @cfg {String} size lg | sm | xs (default empty normal)
500  * @cfg {String} align vertical | justified  (default none)
501  * @cfg {String} direction up | down (default down)
502  * @cfg {Boolean} toolbar false | true
503  * @cfg {Boolean} btn true | false
504  * 
505  * 
506  * @constructor
507  * Create a new Input
508  * @param {Object} config The config object
509  */
510
511 Roo.bootstrap.ButtonGroup = function(config){
512     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
513 };
514
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
516     
517     size: '',
518     align: '',
519     direction: '',
520     toolbar: false,
521     btn: true,
522
523     getAutoCreate : function(){
524         var cfg = {
525             cls: 'btn-group',
526             html : null
527         };
528         
529         cfg.html = this.html || cfg.html;
530         
531         if (this.toolbar) {
532             cfg = {
533                 cls: 'btn-toolbar',
534                 html: null
535             };
536             
537             return cfg;
538         }
539         
540         if (['vertical','justified'].indexOf(this.align)!==-1) {
541             cfg.cls = 'btn-group-' + this.align;
542             
543             if (this.align == 'justified') {
544                 console.log(this.items);
545             }
546         }
547         
548         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549             cfg.cls += ' btn-group-' + this.size;
550         }
551         
552         if (this.direction == 'up') {
553             cfg.cls += ' dropup' ;
554         }
555         
556         return cfg;
557     },
558     /**
559      * Add a button to the group (similar to NavItem API.)
560      */
561     addItem : function(cfg)
562     {
563         var cn = new Roo.bootstrap.Button(cfg);
564         //this.register(cn);
565         cn.parentId = this.id;
566         cn.onRender(this.el, null);
567         return cn;
568     }
569    
570 });
571
572  /*
573  * - LGPL
574  *
575  * button
576  * 
577  */
578
579 /**
580  * @class Roo.bootstrap.Button
581  * @extends Roo.bootstrap.Component
582  * Bootstrap Button class
583  * @cfg {String} html The button content
584  * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
585  * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
586  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
587  * @cfg {String} size ( lg | sm | xs)
588  * @cfg {String} tag ( a | input | submit)
589  * @cfg {String} href empty or href
590  * @cfg {Boolean} disabled default false;
591  * @cfg {Boolean} isClose default false;
592  * @cfg {String} glyphicon depricated - use fa
593  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
594  * @cfg {String} badge text for badge
595  * @cfg {String} theme (default|glow)  
596  * @cfg {Boolean} inverse dark themed version
597  * @cfg {Boolean} toggle is it a slidy toggle button
598  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
599  * @cfg {String} ontext text for on slidy toggle state
600  * @cfg {String} offtext text for off slidy toggle state
601  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
602  * @cfg {Boolean} removeClass remove the standard class..
603  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
604  * 
605  * @constructor
606  * Create a new button
607  * @param {Object} config The config object
608  */
609
610
611 Roo.bootstrap.Button = function(config){
612     Roo.bootstrap.Button.superclass.constructor.call(this, config);
613     this.weightClass = ["btn-default btn-outline-secondary", 
614                        "btn-primary", 
615                        "btn-success", 
616                        "btn-info", 
617                        "btn-warning",
618                        "btn-danger",
619                        "btn-link"
620                       ],  
621     this.addEvents({
622         // raw events
623         /**
624          * @event click
625          * When a butotn is pressed
626          * @param {Roo.bootstrap.Button} btn
627          * @param {Roo.EventObject} e
628          */
629         "click" : true,
630          /**
631          * @event toggle
632          * After the button has been toggles
633          * @param {Roo.bootstrap.Button} btn
634          * @param {Roo.EventObject} e
635          * @param {boolean} pressed (also available as button.pressed)
636          */
637         "toggle" : true
638     });
639 };
640
641 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
642     html: false,
643     active: false,
644     weight: '',
645     badge_weight: '',
646     outline : false,
647     size: '',
648     tag: 'button',
649     href: '',
650     disabled: false,
651     isClose: false,
652     glyphicon: '',
653     fa: '',
654     badge: '',
655     theme: 'default',
656     inverse: false,
657     
658     toggle: false,
659     ontext: 'ON',
660     offtext: 'OFF',
661     defaulton: true,
662     preventDefault: true,
663     removeClass: false,
664     name: false,
665     target: false,
666      
667     pressed : null,
668      
669     
670     getAutoCreate : function(){
671         
672         var cfg = {
673             tag : 'button',
674             cls : 'roo-button',
675             html: ''
676         };
677         
678         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
679             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
680             this.tag = 'button';
681         } else {
682             cfg.tag = this.tag;
683         }
684         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
685         
686         if (this.toggle == true) {
687             cfg={
688                 tag: 'div',
689                 cls: 'slider-frame roo-button',
690                 cn: [
691                     {
692                         tag: 'span',
693                         'data-on-text':'ON',
694                         'data-off-text':'OFF',
695                         cls: 'slider-button',
696                         html: this.offtext
697                     }
698                 ]
699             };
700             
701             if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
702                 cfg.cls += ' '+this.weight;
703             }
704             
705             return cfg;
706         }
707         
708         if (this.isClose) {
709             cfg.cls += ' close';
710             
711             cfg["aria-hidden"] = true;
712             
713             cfg.html = "&times;";
714             
715             return cfg;
716         }
717         
718          
719         if (this.theme==='default') {
720             cfg.cls = 'btn roo-button';
721             
722             //if (this.parentType != 'Navbar') {
723             this.weight = this.weight.length ?  this.weight : 'default';
724             //}
725             if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
726                 
727                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
728                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
729                 cfg.cls += ' btn-' + outline + weight;
730                 if (this.weight == 'default') {
731                     // BC
732                     cfg.cls += ' btn-' + this.weight;
733                 }
734             }
735         } else if (this.theme==='glow') {
736             
737             cfg.tag = 'a';
738             cfg.cls = 'btn-glow roo-button';
739             
740             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
741                 
742                 cfg.cls += ' ' + this.weight;
743             }
744         }
745    
746         
747         if (this.inverse) {
748             this.cls += ' inverse';
749         }
750         
751         
752         if (this.active || this.pressed === true) {
753             cfg.cls += ' active';
754         }
755         
756         if (this.disabled) {
757             cfg.disabled = 'disabled';
758         }
759         
760         if (this.items) {
761             Roo.log('changing to ul' );
762             cfg.tag = 'ul';
763             this.glyphicon = 'caret';
764             if (Roo.bootstrap.version == 4) {
765                 this.fa = 'caret-down';
766             }
767             
768         }
769         
770         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
771          
772         //gsRoo.log(this.parentType);
773         if (this.parentType === 'Navbar' && !this.parent().bar) {
774             Roo.log('changing to li?');
775             
776             cfg.tag = 'li';
777             
778             cfg.cls = '';
779             cfg.cn =  [{
780                 tag : 'a',
781                 cls : 'roo-button',
782                 html : this.html,
783                 href : this.href || '#'
784             }];
785             if (this.menu) {
786                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
787                 cfg.cls += ' dropdown';
788             }   
789             
790             delete cfg.html;
791             
792         }
793         
794        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
795         
796         if (this.glyphicon) {
797             cfg.html = ' ' + cfg.html;
798             
799             cfg.cn = [
800                 {
801                     tag: 'span',
802                     cls: 'glyphicon glyphicon-' + this.glyphicon
803                 }
804             ];
805         }
806         if (this.fa) {
807             cfg.html = ' ' + cfg.html;
808             
809             cfg.cn = [
810                 {
811                     tag: 'i',
812                     cls: 'fa fas fa-' + this.fa
813                 }
814             ];
815         }
816         
817         if (this.badge) {
818             cfg.html += ' ';
819             
820             cfg.tag = 'a';
821             
822 //            cfg.cls='btn roo-button';
823             
824             cfg.href=this.href;
825             
826             var value = cfg.html;
827             
828             if(this.glyphicon){
829                 value = {
830                     tag: 'span',
831                     cls: 'glyphicon glyphicon-' + this.glyphicon,
832                     html: this.html
833                 };
834             }
835             if(this.fa){
836                 value = {
837                     tag: 'i',
838                     cls: 'fa fas fa-' + this.fa,
839                     html: this.html
840                 };
841             }
842             
843             var bw = this.badge_weight.length ? this.badge_weight :
844                 (this.weight.length ? this.weight : 'secondary');
845             bw = bw == 'default' ? 'secondary' : bw;
846             
847             cfg.cn = [
848                 value,
849                 {
850                     tag: 'span',
851                     cls: 'badge badge-' + bw,
852                     html: this.badge
853                 }
854             ];
855             
856             cfg.html='';
857         }
858         
859         if (this.menu) {
860             cfg.cls += ' dropdown';
861             cfg.html = typeof(cfg.html) != 'undefined' ?
862                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
863         }
864         
865         if (cfg.tag !== 'a' && this.href !== '') {
866             throw "Tag must be a to set href.";
867         } else if (this.href.length > 0) {
868             cfg.href = this.href;
869         }
870         
871         if(this.removeClass){
872             cfg.cls = '';
873         }
874         
875         if(this.target){
876             cfg.target = this.target;
877         }
878         
879         return cfg;
880     },
881     initEvents: function() {
882        // Roo.log('init events?');
883 //        Roo.log(this.el.dom);
884         // add the menu...
885         
886         if (typeof (this.menu) != 'undefined') {
887             this.menu.parentType = this.xtype;
888             this.menu.triggerEl = this.el;
889             this.addxtype(Roo.apply({}, this.menu));
890         }
891
892
893        if (this.el.hasClass('roo-button')) {
894             this.el.on('click', this.onClick, this);
895        } else {
896             this.el.select('.roo-button').on('click', this.onClick, this);
897        }
898        
899        if(this.removeClass){
900            this.el.on('click', this.onClick, this);
901        }
902        
903        this.el.enableDisplayMode();
904         
905     },
906     onClick : function(e)
907     {
908         if (this.disabled) {
909             return;
910         }
911         
912         Roo.log('button on click ');
913         if(this.preventDefault){
914             e.preventDefault();
915         }
916         
917         if (this.pressed === true || this.pressed === false) {
918             this.toggleActive(e);
919         }
920         
921         
922         this.fireEvent('click', this, e);
923     },
924     
925     /**
926      * Enables this button
927      */
928     enable : function()
929     {
930         this.disabled = false;
931         this.el.removeClass('disabled');
932     },
933     
934     /**
935      * Disable this button
936      */
937     disable : function()
938     {
939         this.disabled = true;
940         this.el.addClass('disabled');
941     },
942      /**
943      * sets the active state on/off, 
944      * @param {Boolean} state (optional) Force a particular state
945      */
946     setActive : function(v) {
947         
948         this.el[v ? 'addClass' : 'removeClass']('active');
949         this.pressed = v;
950     },
951      /**
952      * toggles the current active state 
953      */
954     toggleActive : function(e)
955     {
956         this.setActive(!this.pressed);
957         this.fireEvent('toggle', this, e, !this.pressed);
958     },
959      /**
960      * get the current active state
961      * @return {boolean} true if it's active
962      */
963     isActive : function()
964     {
965         return this.el.hasClass('active');
966     },
967     /**
968      * set the text of the first selected button
969      */
970     setText : function(str)
971     {
972         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
973     },
974     /**
975      * get the text of the first selected button
976      */
977     getText : function()
978     {
979         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
980     },
981     
982     setWeight : function(str)
983     {
984         this.el.removeClass(this.weightClass);
985         this.weight = str;
986         var outline = this.outline ? 'outline-' : '';
987         if (str == 'default') {
988             this.el.addClass('btn-default btn-outline-secondary');        
989             return;
990         }
991         this.el.addClass('btn-' + outline + str);        
992     }
993     
994     
995 });
996
997  /*
998  * - LGPL
999  *
1000  * column
1001  * 
1002  */
1003
1004 /**
1005  * @class Roo.bootstrap.Column
1006  * @extends Roo.bootstrap.Component
1007  * Bootstrap Column class
1008  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1009  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1010  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1011  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1012  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1013  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1014  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1015  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1016  *
1017  * 
1018  * @cfg {Boolean} hidden (true|false) hide the element
1019  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1020  * @cfg {String} fa (ban|check|...) font awesome icon
1021  * @cfg {Number} fasize (1|2|....) font awsome size
1022
1023  * @cfg {String} icon (info-sign|check|...) glyphicon name
1024
1025  * @cfg {String} html content of column.
1026  * 
1027  * @constructor
1028  * Create a new Column
1029  * @param {Object} config The config object
1030  */
1031
1032 Roo.bootstrap.Column = function(config){
1033     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1034 };
1035
1036 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1037     
1038     xs: false,
1039     sm: false,
1040     md: false,
1041     lg: false,
1042     xsoff: false,
1043     smoff: false,
1044     mdoff: false,
1045     lgoff: false,
1046     html: '',
1047     offset: 0,
1048     alert: false,
1049     fa: false,
1050     icon : false,
1051     hidden : false,
1052     fasize : 1,
1053     
1054     getAutoCreate : function(){
1055         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1056         
1057         cfg = {
1058             tag: 'div',
1059             cls: 'column'
1060         };
1061         
1062         var settings=this;
1063         ['xs','sm','md','lg'].map(function(size){
1064             //Roo.log( size + ':' + settings[size]);
1065             
1066             if (settings[size+'off'] !== false) {
1067                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1068             }
1069             
1070             if (settings[size] === false) {
1071                 return;
1072             }
1073             
1074             if (!settings[size]) { // 0 = hidden
1075                 cfg.cls += ' hidden-' + size;
1076                 return;
1077             }
1078             cfg.cls += ' col-' + size + '-' + settings[size];
1079             
1080         });
1081         
1082         if (this.hidden) {
1083             cfg.cls += ' hidden';
1084         }
1085         
1086         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1087             cfg.cls +=' alert alert-' + this.alert;
1088         }
1089         
1090         
1091         if (this.html.length) {
1092             cfg.html = this.html;
1093         }
1094         if (this.fa) {
1095             var fasize = '';
1096             if (this.fasize > 1) {
1097                 fasize = ' fa-' + this.fasize + 'x';
1098             }
1099             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1100             
1101             
1102         }
1103         if (this.icon) {
1104             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1105         }
1106         
1107         return cfg;
1108     }
1109    
1110 });
1111
1112  
1113
1114  /*
1115  * - LGPL
1116  *
1117  * page container.
1118  * 
1119  */
1120
1121
1122 /**
1123  * @class Roo.bootstrap.Container
1124  * @extends Roo.bootstrap.Component
1125  * Bootstrap Container class
1126  * @cfg {Boolean} jumbotron is it a jumbotron element
1127  * @cfg {String} html content of element
1128  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1129  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1130  * @cfg {String} header content of header (for panel)
1131  * @cfg {String} footer content of footer (for panel)
1132  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1133  * @cfg {String} tag (header|aside|section) type of HTML tag.
1134  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1135  * @cfg {String} fa font awesome icon
1136  * @cfg {String} icon (info-sign|check|...) glyphicon name
1137  * @cfg {Boolean} hidden (true|false) hide the element
1138  * @cfg {Boolean} expandable (true|false) default false
1139  * @cfg {Boolean} expanded (true|false) default true
1140  * @cfg {String} rheader contet on the right of header
1141  * @cfg {Boolean} clickable (true|false) default false
1142
1143  *     
1144  * @constructor
1145  * Create a new Container
1146  * @param {Object} config The config object
1147  */
1148
1149 Roo.bootstrap.Container = function(config){
1150     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1151     
1152     this.addEvents({
1153         // raw events
1154          /**
1155          * @event expand
1156          * After the panel has been expand
1157          * 
1158          * @param {Roo.bootstrap.Container} this
1159          */
1160         "expand" : true,
1161         /**
1162          * @event collapse
1163          * After the panel has been collapsed
1164          * 
1165          * @param {Roo.bootstrap.Container} this
1166          */
1167         "collapse" : true,
1168         /**
1169          * @event click
1170          * When a element is chick
1171          * @param {Roo.bootstrap.Container} this
1172          * @param {Roo.EventObject} e
1173          */
1174         "click" : true
1175     });
1176 };
1177
1178 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1179     
1180     jumbotron : false,
1181     well: '',
1182     panel : '',
1183     header: '',
1184     footer : '',
1185     sticky: '',
1186     tag : false,
1187     alert : false,
1188     fa: false,
1189     icon : false,
1190     expandable : false,
1191     rheader : '',
1192     expanded : true,
1193     clickable: false,
1194   
1195      
1196     getChildContainer : function() {
1197         
1198         if(!this.el){
1199             return false;
1200         }
1201         
1202         if (this.panel.length) {
1203             return this.el.select('.panel-body',true).first();
1204         }
1205         
1206         return this.el;
1207     },
1208     
1209     
1210     getAutoCreate : function(){
1211         
1212         var cfg = {
1213             tag : this.tag || 'div',
1214             html : '',
1215             cls : ''
1216         };
1217         if (this.jumbotron) {
1218             cfg.cls = 'jumbotron';
1219         }
1220         
1221         
1222         
1223         // - this is applied by the parent..
1224         //if (this.cls) {
1225         //    cfg.cls = this.cls + '';
1226         //}
1227         
1228         if (this.sticky.length) {
1229             
1230             var bd = Roo.get(document.body);
1231             if (!bd.hasClass('bootstrap-sticky')) {
1232                 bd.addClass('bootstrap-sticky');
1233                 Roo.select('html',true).setStyle('height', '100%');
1234             }
1235              
1236             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1237         }
1238         
1239         
1240         if (this.well.length) {
1241             switch (this.well) {
1242                 case 'lg':
1243                 case 'sm':
1244                     cfg.cls +=' well well-' +this.well;
1245                     break;
1246                 default:
1247                     cfg.cls +=' well';
1248                     break;
1249             }
1250         }
1251         
1252         if (this.hidden) {
1253             cfg.cls += ' hidden';
1254         }
1255         
1256         
1257         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1258             cfg.cls +=' alert alert-' + this.alert;
1259         }
1260         
1261         var body = cfg;
1262         
1263         if (this.panel.length) {
1264             cfg.cls += ' panel panel-' + this.panel;
1265             cfg.cn = [];
1266             if (this.header.length) {
1267                 
1268                 var h = [];
1269                 
1270                 if(this.expandable){
1271                     
1272                     cfg.cls = cfg.cls + ' expandable';
1273                     
1274                     h.push({
1275                         tag: 'i',
1276                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1277                     });
1278                     
1279                 }
1280                 
1281                 h.push(
1282                     {
1283                         tag: 'span',
1284                         cls : 'panel-title',
1285                         html : (this.expandable ? '&nbsp;' : '') + this.header
1286                     },
1287                     {
1288                         tag: 'span',
1289                         cls: 'panel-header-right',
1290                         html: this.rheader
1291                     }
1292                 );
1293                 
1294                 cfg.cn.push({
1295                     cls : 'panel-heading',
1296                     style : this.expandable ? 'cursor: pointer' : '',
1297                     cn : h
1298                 });
1299                 
1300             }
1301             
1302             body = false;
1303             cfg.cn.push({
1304                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1305                 html : this.html
1306             });
1307             
1308             
1309             if (this.footer.length) {
1310                 cfg.cn.push({
1311                     cls : 'panel-footer',
1312                     html : this.footer
1313                     
1314                 });
1315             }
1316             
1317         }
1318         
1319         if (body) {
1320             body.html = this.html || cfg.html;
1321             // prefix with the icons..
1322             if (this.fa) {
1323                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1324             }
1325             if (this.icon) {
1326                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1327             }
1328             
1329             
1330         }
1331         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1332             cfg.cls =  'container';
1333         }
1334         
1335         return cfg;
1336     },
1337     
1338     initEvents: function() 
1339     {
1340         if(this.expandable){
1341             var headerEl = this.headerEl();
1342         
1343             if(headerEl){
1344                 headerEl.on('click', this.onToggleClick, this);
1345             }
1346         }
1347         
1348         if(this.clickable){
1349             this.el.on('click', this.onClick, this);
1350         }
1351         
1352     },
1353     
1354     onToggleClick : function()
1355     {
1356         var headerEl = this.headerEl();
1357         
1358         if(!headerEl){
1359             return;
1360         }
1361         
1362         if(this.expanded){
1363             this.collapse();
1364             return;
1365         }
1366         
1367         this.expand();
1368     },
1369     
1370     expand : function()
1371     {
1372         if(this.fireEvent('expand', this)) {
1373             
1374             this.expanded = true;
1375             
1376             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1377             
1378             this.el.select('.panel-body',true).first().removeClass('hide');
1379             
1380             var toggleEl = this.toggleEl();
1381
1382             if(!toggleEl){
1383                 return;
1384             }
1385
1386             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1387         }
1388         
1389     },
1390     
1391     collapse : function()
1392     {
1393         if(this.fireEvent('collapse', this)) {
1394             
1395             this.expanded = false;
1396             
1397             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1398             this.el.select('.panel-body',true).first().addClass('hide');
1399         
1400             var toggleEl = this.toggleEl();
1401
1402             if(!toggleEl){
1403                 return;
1404             }
1405
1406             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1407         }
1408     },
1409     
1410     toggleEl : function()
1411     {
1412         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1413             return;
1414         }
1415         
1416         return this.el.select('.panel-heading .fa',true).first();
1417     },
1418     
1419     headerEl : function()
1420     {
1421         if(!this.el || !this.panel.length || !this.header.length){
1422             return;
1423         }
1424         
1425         return this.el.select('.panel-heading',true).first()
1426     },
1427     
1428     bodyEl : function()
1429     {
1430         if(!this.el || !this.panel.length){
1431             return;
1432         }
1433         
1434         return this.el.select('.panel-body',true).first()
1435     },
1436     
1437     titleEl : function()
1438     {
1439         if(!this.el || !this.panel.length || !this.header.length){
1440             return;
1441         }
1442         
1443         return this.el.select('.panel-title',true).first();
1444     },
1445     
1446     setTitle : function(v)
1447     {
1448         var titleEl = this.titleEl();
1449         
1450         if(!titleEl){
1451             return;
1452         }
1453         
1454         titleEl.dom.innerHTML = v;
1455     },
1456     
1457     getTitle : function()
1458     {
1459         
1460         var titleEl = this.titleEl();
1461         
1462         if(!titleEl){
1463             return '';
1464         }
1465         
1466         return titleEl.dom.innerHTML;
1467     },
1468     
1469     setRightTitle : function(v)
1470     {
1471         var t = this.el.select('.panel-header-right',true).first();
1472         
1473         if(!t){
1474             return;
1475         }
1476         
1477         t.dom.innerHTML = v;
1478     },
1479     
1480     onClick : function(e)
1481     {
1482         e.preventDefault();
1483         
1484         this.fireEvent('click', this, e);
1485     }
1486 });
1487
1488  /*
1489  * - LGPL
1490  *
1491  * image
1492  * 
1493  */
1494
1495
1496 /**
1497  * @class Roo.bootstrap.Img
1498  * @extends Roo.bootstrap.Component
1499  * Bootstrap Img class
1500  * @cfg {Boolean} imgResponsive false | true
1501  * @cfg {String} border rounded | circle | thumbnail
1502  * @cfg {String} src image source
1503  * @cfg {String} alt image alternative text
1504  * @cfg {String} href a tag href
1505  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1506  * @cfg {String} xsUrl xs image source
1507  * @cfg {String} smUrl sm image source
1508  * @cfg {String} mdUrl md image source
1509  * @cfg {String} lgUrl lg image source
1510  * 
1511  * @constructor
1512  * Create a new Input
1513  * @param {Object} config The config object
1514  */
1515
1516 Roo.bootstrap.Img = function(config){
1517     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1518     
1519     this.addEvents({
1520         // img events
1521         /**
1522          * @event click
1523          * The img click event for the img.
1524          * @param {Roo.EventObject} e
1525          */
1526         "click" : true
1527     });
1528 };
1529
1530 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1531     
1532     imgResponsive: true,
1533     border: '',
1534     src: 'about:blank',
1535     href: false,
1536     target: false,
1537     xsUrl: '',
1538     smUrl: '',
1539     mdUrl: '',
1540     lgUrl: '',
1541
1542     getAutoCreate : function()
1543     {   
1544         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1545             return this.createSingleImg();
1546         }
1547         
1548         var cfg = {
1549             tag: 'div',
1550             cls: 'roo-image-responsive-group',
1551             cn: []
1552         };
1553         var _this = this;
1554         
1555         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1556             
1557             if(!_this[size + 'Url']){
1558                 return;
1559             }
1560             
1561             var img = {
1562                 tag: 'img',
1563                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1564                 html: _this.html || cfg.html,
1565                 src: _this[size + 'Url']
1566             };
1567             
1568             img.cls += ' roo-image-responsive-' + size;
1569             
1570             var s = ['xs', 'sm', 'md', 'lg'];
1571             
1572             s.splice(s.indexOf(size), 1);
1573             
1574             Roo.each(s, function(ss){
1575                 img.cls += ' hidden-' + ss;
1576             });
1577             
1578             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1579                 cfg.cls += ' img-' + _this.border;
1580             }
1581             
1582             if(_this.alt){
1583                 cfg.alt = _this.alt;
1584             }
1585             
1586             if(_this.href){
1587                 var a = {
1588                     tag: 'a',
1589                     href: _this.href,
1590                     cn: [
1591                         img
1592                     ]
1593                 };
1594
1595                 if(this.target){
1596                     a.target = _this.target;
1597                 }
1598             }
1599             
1600             cfg.cn.push((_this.href) ? a : img);
1601             
1602         });
1603         
1604         return cfg;
1605     },
1606     
1607     createSingleImg : function()
1608     {
1609         var cfg = {
1610             tag: 'img',
1611             cls: (this.imgResponsive) ? 'img-responsive' : '',
1612             html : null,
1613             src : 'about:blank'  // just incase src get's set to undefined?!?
1614         };
1615         
1616         cfg.html = this.html || cfg.html;
1617         
1618         cfg.src = this.src || cfg.src;
1619         
1620         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1621             cfg.cls += ' img-' + this.border;
1622         }
1623         
1624         if(this.alt){
1625             cfg.alt = this.alt;
1626         }
1627         
1628         if(this.href){
1629             var a = {
1630                 tag: 'a',
1631                 href: this.href,
1632                 cn: [
1633                     cfg
1634                 ]
1635             };
1636             
1637             if(this.target){
1638                 a.target = this.target;
1639             }
1640             
1641         }
1642         
1643         return (this.href) ? a : cfg;
1644     },
1645     
1646     initEvents: function() 
1647     {
1648         if(!this.href){
1649             this.el.on('click', this.onClick, this);
1650         }
1651         
1652     },
1653     
1654     onClick : function(e)
1655     {
1656         Roo.log('img onclick');
1657         this.fireEvent('click', this, e);
1658     },
1659     /**
1660      * Sets the url of the image - used to update it
1661      * @param {String} url the url of the image
1662      */
1663     
1664     setSrc : function(url)
1665     {
1666         this.src =  url;
1667         
1668         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1669             this.el.dom.src =  url;
1670             return;
1671         }
1672         
1673         this.el.select('img', true).first().dom.src =  url;
1674     }
1675     
1676     
1677    
1678 });
1679
1680  /*
1681  * - LGPL
1682  *
1683  * image
1684  * 
1685  */
1686
1687
1688 /**
1689  * @class Roo.bootstrap.Link
1690  * @extends Roo.bootstrap.Component
1691  * Bootstrap Link Class
1692  * @cfg {String} alt image alternative text
1693  * @cfg {String} href a tag href
1694  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1695  * @cfg {String} html the content of the link.
1696  * @cfg {String} anchor name for the anchor link
1697  * @cfg {String} fa - favicon
1698
1699  * @cfg {Boolean} preventDefault (true | false) default false
1700
1701  * 
1702  * @constructor
1703  * Create a new Input
1704  * @param {Object} config The config object
1705  */
1706
1707 Roo.bootstrap.Link = function(config){
1708     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1709     
1710     this.addEvents({
1711         // img events
1712         /**
1713          * @event click
1714          * The img click event for the img.
1715          * @param {Roo.EventObject} e
1716          */
1717         "click" : true
1718     });
1719 };
1720
1721 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1722     
1723     href: false,
1724     target: false,
1725     preventDefault: false,
1726     anchor : false,
1727     alt : false,
1728     fa: false,
1729
1730
1731     getAutoCreate : function()
1732     {
1733         var html = this.html || '';
1734         
1735         if (this.fa !== false) {
1736             html = '<i class="fa fa-' + this.fa + '"></i>';
1737         }
1738         var cfg = {
1739             tag: 'a'
1740         };
1741         // anchor's do not require html/href...
1742         if (this.anchor === false) {
1743             cfg.html = html;
1744             cfg.href = this.href || '#';
1745         } else {
1746             cfg.name = this.anchor;
1747             if (this.html !== false || this.fa !== false) {
1748                 cfg.html = html;
1749             }
1750             if (this.href !== false) {
1751                 cfg.href = this.href;
1752             }
1753         }
1754         
1755         if(this.alt !== false){
1756             cfg.alt = this.alt;
1757         }
1758         
1759         
1760         if(this.target !== false) {
1761             cfg.target = this.target;
1762         }
1763         
1764         return cfg;
1765     },
1766     
1767     initEvents: function() {
1768         
1769         if(!this.href || this.preventDefault){
1770             this.el.on('click', this.onClick, this);
1771         }
1772     },
1773     
1774     onClick : function(e)
1775     {
1776         if(this.preventDefault){
1777             e.preventDefault();
1778         }
1779         //Roo.log('img onclick');
1780         this.fireEvent('click', this, e);
1781     }
1782    
1783 });
1784
1785  /*
1786  * - LGPL
1787  *
1788  * header
1789  * 
1790  */
1791
1792 /**
1793  * @class Roo.bootstrap.Header
1794  * @extends Roo.bootstrap.Component
1795  * Bootstrap Header class
1796  * @cfg {String} html content of header
1797  * @cfg {Number} level (1|2|3|4|5|6) default 1
1798  * 
1799  * @constructor
1800  * Create a new Header
1801  * @param {Object} config The config object
1802  */
1803
1804
1805 Roo.bootstrap.Header  = function(config){
1806     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1807 };
1808
1809 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1810     
1811     //href : false,
1812     html : false,
1813     level : 1,
1814     
1815     
1816     
1817     getAutoCreate : function(){
1818         
1819         
1820         
1821         var cfg = {
1822             tag: 'h' + (1 *this.level),
1823             html: this.html || ''
1824         } ;
1825         
1826         return cfg;
1827     }
1828    
1829 });
1830
1831  
1832
1833  /*
1834  * Based on:
1835  * Ext JS Library 1.1.1
1836  * Copyright(c) 2006-2007, Ext JS, LLC.
1837  *
1838  * Originally Released Under LGPL - original licence link has changed is not relivant.
1839  *
1840  * Fork - LGPL
1841  * <script type="text/javascript">
1842  */
1843  
1844 /**
1845  * @class Roo.bootstrap.MenuMgr
1846  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1847  * @singleton
1848  */
1849 Roo.bootstrap.MenuMgr = function(){
1850    var menus, active, groups = {}, attached = false, lastShow = new Date();
1851
1852    // private - called when first menu is created
1853    function init(){
1854        menus = {};
1855        active = new Roo.util.MixedCollection();
1856        Roo.get(document).addKeyListener(27, function(){
1857            if(active.length > 0){
1858                hideAll();
1859            }
1860        });
1861    }
1862
1863    // private
1864    function hideAll(){
1865        if(active && active.length > 0){
1866            var c = active.clone();
1867            c.each(function(m){
1868                m.hide();
1869            });
1870        }
1871    }
1872
1873    // private
1874    function onHide(m){
1875        active.remove(m);
1876        if(active.length < 1){
1877            Roo.get(document).un("mouseup", onMouseDown);
1878             
1879            attached = false;
1880        }
1881    }
1882
1883    // private
1884    function onShow(m){
1885        var last = active.last();
1886        lastShow = new Date();
1887        active.add(m);
1888        if(!attached){
1889           Roo.get(document).on("mouseup", onMouseDown);
1890            
1891            attached = true;
1892        }
1893        if(m.parentMenu){
1894           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1895           m.parentMenu.activeChild = m;
1896        }else if(last && last.isVisible()){
1897           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1898        }
1899    }
1900
1901    // private
1902    function onBeforeHide(m){
1903        if(m.activeChild){
1904            m.activeChild.hide();
1905        }
1906        if(m.autoHideTimer){
1907            clearTimeout(m.autoHideTimer);
1908            delete m.autoHideTimer;
1909        }
1910    }
1911
1912    // private
1913    function onBeforeShow(m){
1914        var pm = m.parentMenu;
1915        if(!pm && !m.allowOtherMenus){
1916            hideAll();
1917        }else if(pm && pm.activeChild && active != m){
1918            pm.activeChild.hide();
1919        }
1920    }
1921
1922    // private this should really trigger on mouseup..
1923    function onMouseDown(e){
1924         Roo.log("on Mouse Up");
1925         
1926         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1927             Roo.log("MenuManager hideAll");
1928             hideAll();
1929             e.stopEvent();
1930         }
1931         
1932         
1933    }
1934
1935    // private
1936    function onBeforeCheck(mi, state){
1937        if(state){
1938            var g = groups[mi.group];
1939            for(var i = 0, l = g.length; i < l; i++){
1940                if(g[i] != mi){
1941                    g[i].setChecked(false);
1942                }
1943            }
1944        }
1945    }
1946
1947    return {
1948
1949        /**
1950         * Hides all menus that are currently visible
1951         */
1952        hideAll : function(){
1953             hideAll();  
1954        },
1955
1956        // private
1957        register : function(menu){
1958            if(!menus){
1959                init();
1960            }
1961            menus[menu.id] = menu;
1962            menu.on("beforehide", onBeforeHide);
1963            menu.on("hide", onHide);
1964            menu.on("beforeshow", onBeforeShow);
1965            menu.on("show", onShow);
1966            var g = menu.group;
1967            if(g && menu.events["checkchange"]){
1968                if(!groups[g]){
1969                    groups[g] = [];
1970                }
1971                groups[g].push(menu);
1972                menu.on("checkchange", onCheck);
1973            }
1974        },
1975
1976         /**
1977          * Returns a {@link Roo.menu.Menu} object
1978          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1979          * be used to generate and return a new Menu instance.
1980          */
1981        get : function(menu){
1982            if(typeof menu == "string"){ // menu id
1983                return menus[menu];
1984            }else if(menu.events){  // menu instance
1985                return menu;
1986            }
1987            /*else if(typeof menu.length == 'number'){ // array of menu items?
1988                return new Roo.bootstrap.Menu({items:menu});
1989            }else{ // otherwise, must be a config
1990                return new Roo.bootstrap.Menu(menu);
1991            }
1992            */
1993            return false;
1994        },
1995
1996        // private
1997        unregister : function(menu){
1998            delete menus[menu.id];
1999            menu.un("beforehide", onBeforeHide);
2000            menu.un("hide", onHide);
2001            menu.un("beforeshow", onBeforeShow);
2002            menu.un("show", onShow);
2003            var g = menu.group;
2004            if(g && menu.events["checkchange"]){
2005                groups[g].remove(menu);
2006                menu.un("checkchange", onCheck);
2007            }
2008        },
2009
2010        // private
2011        registerCheckable : function(menuItem){
2012            var g = menuItem.group;
2013            if(g){
2014                if(!groups[g]){
2015                    groups[g] = [];
2016                }
2017                groups[g].push(menuItem);
2018                menuItem.on("beforecheckchange", onBeforeCheck);
2019            }
2020        },
2021
2022        // private
2023        unregisterCheckable : function(menuItem){
2024            var g = menuItem.group;
2025            if(g){
2026                groups[g].remove(menuItem);
2027                menuItem.un("beforecheckchange", onBeforeCheck);
2028            }
2029        }
2030    };
2031 }();/*
2032  * - LGPL
2033  *
2034  * menu
2035  * 
2036  */
2037
2038 /**
2039  * @class Roo.bootstrap.Menu
2040  * @extends Roo.bootstrap.Component
2041  * Bootstrap Menu class - container for MenuItems
2042  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2043  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2044  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2045  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2046  * 
2047  * @constructor
2048  * Create a new Menu
2049  * @param {Object} config The config object
2050  */
2051
2052
2053 Roo.bootstrap.Menu = function(config){
2054     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2055     if (this.registerMenu && this.type != 'treeview')  {
2056         Roo.bootstrap.MenuMgr.register(this);
2057     }
2058     
2059     
2060     this.addEvents({
2061         /**
2062          * @event beforeshow
2063          * Fires before this menu is displayed
2064          * @param {Roo.menu.Menu} this
2065          */
2066         beforeshow : true,
2067         /**
2068          * @event beforehide
2069          * Fires before this menu is hidden
2070          * @param {Roo.menu.Menu} this
2071          */
2072         beforehide : true,
2073         /**
2074          * @event show
2075          * Fires after this menu is displayed
2076          * @param {Roo.menu.Menu} this
2077          */
2078         show : true,
2079         /**
2080          * @event hide
2081          * Fires after this menu is hidden
2082          * @param {Roo.menu.Menu} this
2083          */
2084         hide : true,
2085         /**
2086          * @event click
2087          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2088          * @param {Roo.menu.Menu} this
2089          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2090          * @param {Roo.EventObject} e
2091          */
2092         click : true,
2093         /**
2094          * @event mouseover
2095          * Fires when the mouse is hovering over this menu
2096          * @param {Roo.menu.Menu} this
2097          * @param {Roo.EventObject} e
2098          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2099          */
2100         mouseover : true,
2101         /**
2102          * @event mouseout
2103          * Fires when the mouse exits this menu
2104          * @param {Roo.menu.Menu} this
2105          * @param {Roo.EventObject} e
2106          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2107          */
2108         mouseout : true,
2109         /**
2110          * @event itemclick
2111          * Fires when a menu item contained in this menu is clicked
2112          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2113          * @param {Roo.EventObject} e
2114          */
2115         itemclick: true
2116     });
2117     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2118 };
2119
2120 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2121     
2122    /// html : false,
2123     //align : '',
2124     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2125     type: false,
2126     /**
2127      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2128      */
2129     registerMenu : true,
2130     
2131     menuItems :false, // stores the menu items..
2132     
2133     hidden:true,
2134         
2135     parentMenu : false,
2136     
2137     stopEvent : true,
2138     
2139     isLink : false,
2140     
2141     getChildContainer : function() {
2142         return this.el;  
2143     },
2144     
2145     getAutoCreate : function(){
2146          
2147         //if (['right'].indexOf(this.align)!==-1) {
2148         //    cfg.cn[1].cls += ' pull-right'
2149         //}
2150         
2151         
2152         var cfg = {
2153             tag : 'ul',
2154             cls : 'dropdown-menu' ,
2155             style : 'z-index:1000'
2156             
2157         };
2158         
2159         if (this.type === 'submenu') {
2160             cfg.cls = 'submenu active';
2161         }
2162         if (this.type === 'treeview') {
2163             cfg.cls = 'treeview-menu';
2164         }
2165         
2166         return cfg;
2167     },
2168     initEvents : function() {
2169         
2170        // Roo.log("ADD event");
2171        // Roo.log(this.triggerEl.dom);
2172         
2173         this.triggerEl.on('click', this.onTriggerClick, this);
2174         
2175         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2176         
2177         
2178         if (this.triggerEl.hasClass('nav-item')) {
2179             // dropdown toggle on the 'a' in BS4?
2180             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2181         } else {
2182             this.triggerEl.addClass('dropdown-toggle');
2183         }
2184         if (Roo.isTouch) {
2185             this.el.on('touchstart'  , this.onTouch, this);
2186         }
2187         this.el.on('click' , this.onClick, this);
2188
2189         this.el.on("mouseover", this.onMouseOver, this);
2190         this.el.on("mouseout", this.onMouseOut, this);
2191         
2192     },
2193     
2194     findTargetItem : function(e)
2195     {
2196         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2197         if(!t){
2198             return false;
2199         }
2200         //Roo.log(t);         Roo.log(t.id);
2201         if(t && t.id){
2202             //Roo.log(this.menuitems);
2203             return this.menuitems.get(t.id);
2204             
2205             //return this.items.get(t.menuItemId);
2206         }
2207         
2208         return false;
2209     },
2210     
2211     onTouch : function(e) 
2212     {
2213         Roo.log("menu.onTouch");
2214         //e.stopEvent(); this make the user popdown broken
2215         this.onClick(e);
2216     },
2217     
2218     onClick : function(e)
2219     {
2220         Roo.log("menu.onClick");
2221         
2222         var t = this.findTargetItem(e);
2223         if(!t || t.isContainer){
2224             return;
2225         }
2226         Roo.log(e);
2227         /*
2228         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2229             if(t == this.activeItem && t.shouldDeactivate(e)){
2230                 this.activeItem.deactivate();
2231                 delete this.activeItem;
2232                 return;
2233             }
2234             if(t.canActivate){
2235                 this.setActiveItem(t, true);
2236             }
2237             return;
2238             
2239             
2240         }
2241         */
2242        
2243         Roo.log('pass click event');
2244         
2245         t.onClick(e);
2246         
2247         this.fireEvent("click", this, t, e);
2248         
2249         var _this = this;
2250         
2251         if(!t.href.length || t.href == '#'){
2252             (function() { _this.hide(); }).defer(100);
2253         }
2254         
2255     },
2256     
2257     onMouseOver : function(e){
2258         var t  = this.findTargetItem(e);
2259         //Roo.log(t);
2260         //if(t){
2261         //    if(t.canActivate && !t.disabled){
2262         //        this.setActiveItem(t, true);
2263         //    }
2264         //}
2265         
2266         this.fireEvent("mouseover", this, e, t);
2267     },
2268     isVisible : function(){
2269         return !this.hidden;
2270     },
2271      onMouseOut : function(e){
2272         var t  = this.findTargetItem(e);
2273         
2274         //if(t ){
2275         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2276         //        this.activeItem.deactivate();
2277         //        delete this.activeItem;
2278         //    }
2279         //}
2280         this.fireEvent("mouseout", this, e, t);
2281     },
2282     
2283     
2284     /**
2285      * Displays this menu relative to another element
2286      * @param {String/HTMLElement/Roo.Element} element The element to align to
2287      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2288      * the element (defaults to this.defaultAlign)
2289      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2290      */
2291     show : function(el, pos, parentMenu){
2292         this.parentMenu = parentMenu;
2293         if(!this.el){
2294             this.render();
2295         }
2296         this.fireEvent("beforeshow", this);
2297         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2298     },
2299      /**
2300      * Displays this menu at a specific xy position
2301      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2302      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2303      */
2304     showAt : function(xy, parentMenu, /* private: */_e){
2305         this.parentMenu = parentMenu;
2306         if(!this.el){
2307             this.render();
2308         }
2309         if(_e !== false){
2310             this.fireEvent("beforeshow", this);
2311             //xy = this.el.adjustForConstraints(xy);
2312         }
2313         
2314         //this.el.show();
2315         this.hideMenuItems();
2316         this.hidden = false;
2317         this.triggerEl.addClass('open');
2318         this.el.addClass('show');
2319         
2320         // reassign x when hitting right
2321         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2322             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2323         }
2324         
2325         // reassign y when hitting bottom
2326         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2327             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2328         }
2329         
2330         // but the list may align on trigger left or trigger top... should it be a properity?
2331         
2332         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2333             this.el.setXY(xy);
2334         }
2335         
2336         this.focus();
2337         this.fireEvent("show", this);
2338     },
2339     
2340     focus : function(){
2341         return;
2342         if(!this.hidden){
2343             this.doFocus.defer(50, this);
2344         }
2345     },
2346
2347     doFocus : function(){
2348         if(!this.hidden){
2349             this.focusEl.focus();
2350         }
2351     },
2352
2353     /**
2354      * Hides this menu and optionally all parent menus
2355      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2356      */
2357     hide : function(deep)
2358     {
2359         
2360         this.hideMenuItems();
2361         if(this.el && this.isVisible()){
2362             this.fireEvent("beforehide", this);
2363             if(this.activeItem){
2364                 this.activeItem.deactivate();
2365                 this.activeItem = null;
2366             }
2367             this.triggerEl.removeClass('open');;
2368             this.el.removeClass('show');
2369             this.hidden = true;
2370             this.fireEvent("hide", this);
2371         }
2372         if(deep === true && this.parentMenu){
2373             this.parentMenu.hide(true);
2374         }
2375     },
2376     
2377     onTriggerClick : function(e)
2378     {
2379         Roo.log('trigger click');
2380         
2381         var target = e.getTarget();
2382         
2383         Roo.log(target.nodeName.toLowerCase());
2384         
2385         if(target.nodeName.toLowerCase() === 'i'){
2386             e.preventDefault();
2387         }
2388         
2389     },
2390     
2391     onTriggerPress  : function(e)
2392     {
2393         Roo.log('trigger press');
2394         //Roo.log(e.getTarget());
2395        // Roo.log(this.triggerEl.dom);
2396        
2397         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2398         var pel = Roo.get(e.getTarget());
2399         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2400             Roo.log('is treeview or dropdown?');
2401             return;
2402         }
2403         
2404         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2405             return;
2406         }
2407         
2408         if (this.isVisible()) {
2409             Roo.log('hide');
2410             this.hide();
2411         } else {
2412             Roo.log('show');
2413             this.show(this.triggerEl, '?', false);
2414         }
2415         
2416         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2417             e.stopEvent();
2418         }
2419         
2420     },
2421        
2422     
2423     hideMenuItems : function()
2424     {
2425         Roo.log("hide Menu Items");
2426         if (!this.el) { 
2427             return;
2428         }
2429         //$(backdrop).remove()
2430         this.el.select('.open',true).each(function(aa) {
2431             
2432             aa.removeClass('open');
2433           //var parent = getParent($(this))
2434           //var relatedTarget = { relatedTarget: this }
2435           
2436            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2437           //if (e.isDefaultPrevented()) return
2438            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2439         });
2440     },
2441     addxtypeChild : function (tree, cntr) {
2442         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2443           
2444         this.menuitems.add(comp);
2445         return comp;
2446
2447     },
2448     getEl : function()
2449     {
2450         Roo.log(this.el);
2451         return this.el;
2452     },
2453     
2454     clear : function()
2455     {
2456         this.getEl().dom.innerHTML = '';
2457         this.menuitems.clear();
2458     }
2459 });
2460
2461  
2462  /*
2463  * - LGPL
2464  *
2465  * menu item
2466  * 
2467  */
2468
2469
2470 /**
2471  * @class Roo.bootstrap.MenuItem
2472  * @extends Roo.bootstrap.Component
2473  * Bootstrap MenuItem class
2474  * @cfg {String} html the menu label
2475  * @cfg {String} href the link
2476  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2477  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2478  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2479  * @cfg {String} fa favicon to show on left of menu item.
2480  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2481  * 
2482  * 
2483  * @constructor
2484  * Create a new MenuItem
2485  * @param {Object} config The config object
2486  */
2487
2488
2489 Roo.bootstrap.MenuItem = function(config){
2490     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2491     this.addEvents({
2492         // raw events
2493         /**
2494          * @event click
2495          * The raw click event for the entire grid.
2496          * @param {Roo.bootstrap.MenuItem} this
2497          * @param {Roo.EventObject} e
2498          */
2499         "click" : true
2500     });
2501 };
2502
2503 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2504     
2505     href : false,
2506     html : false,
2507     preventDefault: false,
2508     isContainer : false,
2509     active : false,
2510     fa: false,
2511     
2512     getAutoCreate : function(){
2513         
2514         if(this.isContainer){
2515             return {
2516                 tag: 'li',
2517                 cls: 'dropdown-menu-item '
2518             };
2519         }
2520         var ctag = {
2521             tag: 'span',
2522             html: 'Link'
2523         };
2524         
2525         var anc = {
2526             tag : 'a',
2527             cls : 'dropdown-item',
2528             href : '#',
2529             cn : [  ]
2530         };
2531         
2532         if (this.fa !== false) {
2533             anc.cn.push({
2534                 tag : 'i',
2535                 cls : 'fa fa-' + this.fa
2536             });
2537         }
2538         
2539         anc.cn.push(ctag);
2540         
2541         
2542         var cfg= {
2543             tag: 'li',
2544             cls: 'dropdown-menu-item',
2545             cn: [ anc ]
2546         };
2547         if (this.parent().type == 'treeview') {
2548             cfg.cls = 'treeview-menu';
2549         }
2550         if (this.active) {
2551             cfg.cls += ' active';
2552         }
2553         
2554         
2555         
2556         anc.href = this.href || cfg.cn[0].href ;
2557         ctag.html = this.html || cfg.cn[0].html ;
2558         return cfg;
2559     },
2560     
2561     initEvents: function()
2562     {
2563         if (this.parent().type == 'treeview') {
2564             this.el.select('a').on('click', this.onClick, this);
2565         }
2566         
2567         if (this.menu) {
2568             this.menu.parentType = this.xtype;
2569             this.menu.triggerEl = this.el;
2570             this.menu = this.addxtype(Roo.apply({}, this.menu));
2571         }
2572         
2573     },
2574     onClick : function(e)
2575     {
2576         Roo.log('item on click ');
2577         
2578         if(this.preventDefault){
2579             e.preventDefault();
2580         }
2581         //this.parent().hideMenuItems();
2582         
2583         this.fireEvent('click', this, e);
2584     },
2585     getEl : function()
2586     {
2587         return this.el;
2588     } 
2589 });
2590
2591  
2592
2593  /*
2594  * - LGPL
2595  *
2596  * menu separator
2597  * 
2598  */
2599
2600
2601 /**
2602  * @class Roo.bootstrap.MenuSeparator
2603  * @extends Roo.bootstrap.Component
2604  * Bootstrap MenuSeparator class
2605  * 
2606  * @constructor
2607  * Create a new MenuItem
2608  * @param {Object} config The config object
2609  */
2610
2611
2612 Roo.bootstrap.MenuSeparator = function(config){
2613     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2614 };
2615
2616 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2617     
2618     getAutoCreate : function(){
2619         var cfg = {
2620             cls: 'divider',
2621             tag : 'li'
2622         };
2623         
2624         return cfg;
2625     }
2626    
2627 });
2628
2629  
2630
2631  
2632 /*
2633 * Licence: LGPL
2634 */
2635
2636 /**
2637  * @class Roo.bootstrap.Modal
2638  * @extends Roo.bootstrap.Component
2639  * Bootstrap Modal class
2640  * @cfg {String} title Title of dialog
2641  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2642  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2643  * @cfg {Boolean} specificTitle default false
2644  * @cfg {Array} buttons Array of buttons or standard button set..
2645  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2646  * @cfg {Boolean} animate default true
2647  * @cfg {Boolean} allow_close default true
2648  * @cfg {Boolean} fitwindow default false
2649  * @cfg {String} size (sm|lg) default empty
2650  * @cfg {Number} max_width set the max width of modal
2651  *
2652  *
2653  * @constructor
2654  * Create a new Modal Dialog
2655  * @param {Object} config The config object
2656  */
2657
2658 Roo.bootstrap.Modal = function(config){
2659     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2660     this.addEvents({
2661         // raw events
2662         /**
2663          * @event btnclick
2664          * The raw btnclick event for the button
2665          * @param {Roo.EventObject} e
2666          */
2667         "btnclick" : true,
2668         /**
2669          * @event resize
2670          * Fire when dialog resize
2671          * @param {Roo.bootstrap.Modal} this
2672          * @param {Roo.EventObject} e
2673          */
2674         "resize" : true
2675     });
2676     this.buttons = this.buttons || [];
2677
2678     if (this.tmpl) {
2679         this.tmpl = Roo.factory(this.tmpl);
2680     }
2681
2682 };
2683
2684 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2685
2686     title : 'test dialog',
2687
2688     buttons : false,
2689
2690     // set on load...
2691
2692     html: false,
2693
2694     tmp: false,
2695
2696     specificTitle: false,
2697
2698     buttonPosition: 'right',
2699
2700     allow_close : true,
2701
2702     animate : true,
2703
2704     fitwindow: false,
2705     
2706      // private
2707     dialogEl: false,
2708     bodyEl:  false,
2709     footerEl:  false,
2710     titleEl:  false,
2711     closeEl:  false,
2712
2713     size: '',
2714     
2715     max_width: 0,
2716     
2717     max_height: 0,
2718     
2719     fit_content: false,
2720
2721     onRender : function(ct, position)
2722     {
2723         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2724
2725         if(!this.el){
2726             var cfg = Roo.apply({},  this.getAutoCreate());
2727             cfg.id = Roo.id();
2728             //if(!cfg.name){
2729             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2730             //}
2731             //if (!cfg.name.length) {
2732             //    delete cfg.name;
2733            // }
2734             if (this.cls) {
2735                 cfg.cls += ' ' + this.cls;
2736             }
2737             if (this.style) {
2738                 cfg.style = this.style;
2739             }
2740             this.el = Roo.get(document.body).createChild(cfg, position);
2741         }
2742         //var type = this.el.dom.type;
2743
2744
2745         if(this.tabIndex !== undefined){
2746             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2747         }
2748
2749         this.dialogEl = this.el.select('.modal-dialog',true).first();
2750         this.bodyEl = this.el.select('.modal-body',true).first();
2751         this.closeEl = this.el.select('.modal-header .close', true).first();
2752         this.headerEl = this.el.select('.modal-header',true).first();
2753         this.titleEl = this.el.select('.modal-title',true).first();
2754         this.footerEl = this.el.select('.modal-footer',true).first();
2755
2756         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2757         
2758         //this.el.addClass("x-dlg-modal");
2759
2760         if (this.buttons.length) {
2761             Roo.each(this.buttons, function(bb) {
2762                 var b = Roo.apply({}, bb);
2763                 b.xns = b.xns || Roo.bootstrap;
2764                 b.xtype = b.xtype || 'Button';
2765                 if (typeof(b.listeners) == 'undefined') {
2766                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2767                 }
2768
2769                 var btn = Roo.factory(b);
2770
2771                 btn.render(this.getButtonContainer());
2772
2773             },this);
2774         }
2775         // render the children.
2776         var nitems = [];
2777
2778         if(typeof(this.items) != 'undefined'){
2779             var items = this.items;
2780             delete this.items;
2781
2782             for(var i =0;i < items.length;i++) {
2783                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2784             }
2785         }
2786
2787         this.items = nitems;
2788
2789         // where are these used - they used to be body/close/footer
2790
2791
2792         this.initEvents();
2793         //this.el.addClass([this.fieldClass, this.cls]);
2794
2795     },
2796
2797     getAutoCreate : function()
2798     {
2799         var bdy = {
2800                 cls : 'modal-body',
2801                 html : this.html || ''
2802         };
2803
2804         var title = {
2805             tag: 'h4',
2806             cls : 'modal-title',
2807             html : this.title
2808         };
2809
2810         if(this.specificTitle){
2811             title = this.title;
2812
2813         };
2814
2815         var header = [];
2816         if (this.allow_close && Roo.bootstrap.version == 3) {
2817             header.push({
2818                 tag: 'button',
2819                 cls : 'close',
2820                 html : '&times'
2821             });
2822         }
2823
2824         header.push(title);
2825
2826         if (this.allow_close && Roo.bootstrap.version == 4) {
2827             header.push({
2828                 tag: 'button',
2829                 cls : 'close',
2830                 html : '&times'
2831             });
2832         }
2833         
2834         var size = '';
2835
2836         if(this.size.length){
2837             size = 'modal-' + this.size;
2838         }
2839         
2840         var footer = Roo.bootstrap.version == 3 ?
2841             {
2842                 cls : 'modal-footer',
2843                 cn : [
2844                     {
2845                         tag: 'div',
2846                         cls: 'btn-' + this.buttonPosition
2847                     }
2848                 ]
2849
2850             } :
2851             {  // BS4 uses mr-auto on left buttons....
2852                 cls : 'modal-footer'
2853             };
2854
2855             
2856
2857         
2858         
2859         var modal = {
2860             cls: "modal",
2861              cn : [
2862                 {
2863                     cls: "modal-dialog " + size,
2864                     cn : [
2865                         {
2866                             cls : "modal-content",
2867                             cn : [
2868                                 {
2869                                     cls : 'modal-header',
2870                                     cn : header
2871                                 },
2872                                 bdy,
2873                                 footer
2874                             ]
2875
2876                         }
2877                     ]
2878
2879                 }
2880             ]
2881         };
2882
2883         if(this.animate){
2884             modal.cls += ' fade';
2885         }
2886
2887         return modal;
2888
2889     },
2890     getChildContainer : function() {
2891
2892          return this.bodyEl;
2893
2894     },
2895     getButtonContainer : function() {
2896         
2897          return Roo.bootstrap.version == 4 ?
2898             this.el.select('.modal-footer',true).first()
2899             : this.el.select('.modal-footer div',true).first();
2900
2901     },
2902     initEvents : function()
2903     {
2904         if (this.allow_close) {
2905             this.closeEl.on('click', this.hide, this);
2906         }
2907         Roo.EventManager.onWindowResize(this.resize, this, true);
2908
2909
2910     },
2911
2912     resize : function()
2913     {
2914         this.maskEl.setSize(
2915             Roo.lib.Dom.getViewWidth(true),
2916             Roo.lib.Dom.getViewHeight(true)
2917         );
2918         
2919         if (this.fitwindow) {
2920             this.setSize(
2921                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2922                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2923             );
2924             return;
2925         }
2926         
2927         if(this.max_width !== 0) {
2928             
2929             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2930             
2931             if(this.height) {
2932                 this.setSize(w, this.height);
2933                 return;
2934             }
2935             
2936             if(this.max_height) {
2937                 this.setSize(w,Math.min(
2938                     this.max_height,
2939                     Roo.lib.Dom.getViewportHeight(true) - 60
2940                 ));
2941                 
2942                 return;
2943             }
2944             
2945             if(!this.fit_content) {
2946                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2947                 return;
2948             }
2949             
2950             this.setSize(w, Math.min(
2951                 60 +
2952                 this.headerEl.getHeight() + 
2953                 this.footerEl.getHeight() + 
2954                 this.getChildHeight(this.bodyEl.dom.childNodes),
2955                 Roo.lib.Dom.getViewportHeight(true) - 60)
2956             );
2957         }
2958         
2959     },
2960
2961     setSize : function(w,h)
2962     {
2963         if (!w && !h) {
2964             return;
2965         }
2966         
2967         this.resizeTo(w,h);
2968     },
2969
2970     show : function() {
2971
2972         if (!this.rendered) {
2973             this.render();
2974         }
2975
2976         //this.el.setStyle('display', 'block');
2977         this.el.removeClass('hideing');
2978         this.el.dom.style.display='block';
2979         
2980         Roo.get(document.body).addClass('modal-open');
2981  
2982         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2983             var _this = this;
2984             (function(){
2985                 this.el.addClass('show');
2986                 this.el.addClass('in');
2987             }).defer(50, this);
2988         }else{
2989             this.el.addClass('show');
2990             this.el.addClass('in');
2991         }
2992
2993         // not sure how we can show data in here..
2994         //if (this.tmpl) {
2995         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2996         //}
2997
2998         Roo.get(document.body).addClass("x-body-masked");
2999         
3000         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
3001         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3002         this.maskEl.dom.style.display = 'block';
3003         this.maskEl.addClass('show');
3004         
3005         
3006         this.resize();
3007         
3008         this.fireEvent('show', this);
3009
3010         // set zindex here - otherwise it appears to be ignored...
3011         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3012
3013         (function () {
3014             this.items.forEach( function(e) {
3015                 e.layout ? e.layout() : false;
3016
3017             });
3018         }).defer(100,this);
3019
3020     },
3021     hide : function()
3022     {
3023         if(this.fireEvent("beforehide", this) !== false){
3024             
3025             this.maskEl.removeClass('show');
3026             
3027             this.maskEl.dom.style.display = '';
3028             Roo.get(document.body).removeClass("x-body-masked");
3029             this.el.removeClass('in');
3030             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3031
3032             if(this.animate){ // why
3033                 this.el.addClass('hideing');
3034                 this.el.removeClass('show');
3035                 (function(){
3036                     if (!this.el.hasClass('hideing')) {
3037                         return; // it's been shown again...
3038                     }
3039                     
3040                     this.el.dom.style.display='';
3041
3042                     Roo.get(document.body).removeClass('modal-open');
3043                     this.el.removeClass('hideing');
3044                 }).defer(150,this);
3045                 
3046             }else{
3047                 this.el.removeClass('show');
3048                 this.el.dom.style.display='';
3049                 Roo.get(document.body).removeClass('modal-open');
3050
3051             }
3052             this.fireEvent('hide', this);
3053         }
3054     },
3055     isVisible : function()
3056     {
3057         
3058         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3059         
3060     },
3061
3062     addButton : function(str, cb)
3063     {
3064
3065
3066         var b = Roo.apply({}, { html : str } );
3067         b.xns = b.xns || Roo.bootstrap;
3068         b.xtype = b.xtype || 'Button';
3069         if (typeof(b.listeners) == 'undefined') {
3070             b.listeners = { click : cb.createDelegate(this)  };
3071         }
3072
3073         var btn = Roo.factory(b);
3074
3075         btn.render(this.getButtonContainer());
3076
3077         return btn;
3078
3079     },
3080
3081     setDefaultButton : function(btn)
3082     {
3083         //this.el.select('.modal-footer').()
3084     },
3085     diff : false,
3086
3087     resizeTo: function(w,h)
3088     {
3089         // skip.. ?? why??
3090
3091         this.dialogEl.setWidth(w);
3092         if (this.diff === false) {
3093             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
3094         }
3095
3096         this.bodyEl.setHeight(h - this.diff);
3097
3098         this.fireEvent('resize', this);
3099
3100     },
3101     setContentSize  : function(w, h)
3102     {
3103
3104     },
3105     onButtonClick: function(btn,e)
3106     {
3107         //Roo.log([a,b,c]);
3108         this.fireEvent('btnclick', btn.name, e);
3109     },
3110      /**
3111      * Set the title of the Dialog
3112      * @param {String} str new Title
3113      */
3114     setTitle: function(str) {
3115         this.titleEl.dom.innerHTML = str;
3116     },
3117     /**
3118      * Set the body of the Dialog
3119      * @param {String} str new Title
3120      */
3121     setBody: function(str) {
3122         this.bodyEl.dom.innerHTML = str;
3123     },
3124     /**
3125      * Set the body of the Dialog using the template
3126      * @param {Obj} data - apply this data to the template and replace the body contents.
3127      */
3128     applyBody: function(obj)
3129     {
3130         if (!this.tmpl) {
3131             Roo.log("Error - using apply Body without a template");
3132             //code
3133         }
3134         this.tmpl.overwrite(this.bodyEl, obj);
3135     },
3136     
3137     getChildHeight : function(child_nodes)
3138     {
3139         if(
3140             !child_nodes ||
3141             child_nodes.length == 0
3142         ) {
3143             return;
3144         }
3145         
3146         var child_height = 0;
3147         
3148         for(var i = 0; i < child_nodes.length; i++) {
3149             
3150             /*
3151             * for modal with tabs...
3152             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3153                 
3154                 var layout_childs = child_nodes[i].childNodes;
3155                 
3156                 for(var j = 0; j < layout_childs.length; j++) {
3157                     
3158                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3159                         
3160                         var layout_body_childs = layout_childs[j].childNodes;
3161                         
3162                         for(var k = 0; k < layout_body_childs.length; k++) {
3163                             
3164                             if(layout_body_childs[k].classList.contains('navbar')) {
3165                                 child_height += layout_body_childs[k].offsetHeight;
3166                                 continue;
3167                             }
3168                             
3169                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3170                                 
3171                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3172                                 
3173                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3174                                     
3175                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3176                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3177                                         continue;
3178                                     }
3179                                     
3180                                 }
3181                                 
3182                             }
3183                             
3184                         }
3185                     }
3186                 }
3187                 continue;
3188             }
3189             */
3190             
3191             child_height += child_nodes[i].offsetHeight;
3192             // Roo.log(child_nodes[i].offsetHeight);
3193         }
3194         
3195         return child_height;
3196     }
3197
3198 });
3199
3200
3201 Roo.apply(Roo.bootstrap.Modal,  {
3202     /**
3203          * Button config that displays a single OK button
3204          * @type Object
3205          */
3206         OK :  [{
3207             name : 'ok',
3208             weight : 'primary',
3209             html : 'OK'
3210         }],
3211         /**
3212          * Button config that displays Yes and No buttons
3213          * @type Object
3214          */
3215         YESNO : [
3216             {
3217                 name  : 'no',
3218                 html : 'No'
3219             },
3220             {
3221                 name  :'yes',
3222                 weight : 'primary',
3223                 html : 'Yes'
3224             }
3225         ],
3226
3227         /**
3228          * Button config that displays OK and Cancel buttons
3229          * @type Object
3230          */
3231         OKCANCEL : [
3232             {
3233                name : 'cancel',
3234                 html : 'Cancel'
3235             },
3236             {
3237                 name : 'ok',
3238                 weight : 'primary',
3239                 html : 'OK'
3240             }
3241         ],
3242         /**
3243          * Button config that displays Yes, No and Cancel buttons
3244          * @type Object
3245          */
3246         YESNOCANCEL : [
3247             {
3248                 name : 'yes',
3249                 weight : 'primary',
3250                 html : 'Yes'
3251             },
3252             {
3253                 name : 'no',
3254                 html : 'No'
3255             },
3256             {
3257                 name : 'cancel',
3258                 html : 'Cancel'
3259             }
3260         ],
3261         
3262         zIndex : 10001
3263 });
3264 /*
3265  * - LGPL
3266  *
3267  * messagebox - can be used as a replace
3268  * 
3269  */
3270 /**
3271  * @class Roo.MessageBox
3272  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3273  * Example usage:
3274  *<pre><code>
3275 // Basic alert:
3276 Roo.Msg.alert('Status', 'Changes saved successfully.');
3277
3278 // Prompt for user data:
3279 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3280     if (btn == 'ok'){
3281         // process text value...
3282     }
3283 });
3284
3285 // Show a dialog using config options:
3286 Roo.Msg.show({
3287    title:'Save Changes?',
3288    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3289    buttons: Roo.Msg.YESNOCANCEL,
3290    fn: processResult,
3291    animEl: 'elId'
3292 });
3293 </code></pre>
3294  * @singleton
3295  */
3296 Roo.bootstrap.MessageBox = function(){
3297     var dlg, opt, mask, waitTimer;
3298     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3299     var buttons, activeTextEl, bwidth;
3300
3301     
3302     // private
3303     var handleButton = function(button){
3304         dlg.hide();
3305         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3306     };
3307
3308     // private
3309     var handleHide = function(){
3310         if(opt && opt.cls){
3311             dlg.el.removeClass(opt.cls);
3312         }
3313         //if(waitTimer){
3314         //    Roo.TaskMgr.stop(waitTimer);
3315         //    waitTimer = null;
3316         //}
3317     };
3318
3319     // private
3320     var updateButtons = function(b){
3321         var width = 0;
3322         if(!b){
3323             buttons["ok"].hide();
3324             buttons["cancel"].hide();
3325             buttons["yes"].hide();
3326             buttons["no"].hide();
3327             dlg.footerEl.hide();
3328             
3329             return width;
3330         }
3331         dlg.footerEl.show();
3332         for(var k in buttons){
3333             if(typeof buttons[k] != "function"){
3334                 if(b[k]){
3335                     buttons[k].show();
3336                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3337                     width += buttons[k].el.getWidth()+15;
3338                 }else{
3339                     buttons[k].hide();
3340                 }
3341             }
3342         }
3343         return width;
3344     };
3345
3346     // private
3347     var handleEsc = function(d, k, e){
3348         if(opt && opt.closable !== false){
3349             dlg.hide();
3350         }
3351         if(e){
3352             e.stopEvent();
3353         }
3354     };
3355
3356     return {
3357         /**
3358          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3359          * @return {Roo.BasicDialog} The BasicDialog element
3360          */
3361         getDialog : function(){
3362            if(!dlg){
3363                 dlg = new Roo.bootstrap.Modal( {
3364                     //draggable: true,
3365                     //resizable:false,
3366                     //constraintoviewport:false,
3367                     //fixedcenter:true,
3368                     //collapsible : false,
3369                     //shim:true,
3370                     //modal: true,
3371                 //    width: 'auto',
3372                   //  height:100,
3373                     //buttonAlign:"center",
3374                     closeClick : function(){
3375                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3376                             handleButton("no");
3377                         }else{
3378                             handleButton("cancel");
3379                         }
3380                     }
3381                 });
3382                 dlg.render();
3383                 dlg.on("hide", handleHide);
3384                 mask = dlg.mask;
3385                 //dlg.addKeyListener(27, handleEsc);
3386                 buttons = {};
3387                 this.buttons = buttons;
3388                 var bt = this.buttonText;
3389                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3390                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3391                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3392                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3393                 //Roo.log(buttons);
3394                 bodyEl = dlg.bodyEl.createChild({
3395
3396                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3397                         '<textarea class="roo-mb-textarea"></textarea>' +
3398                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3399                 });
3400                 msgEl = bodyEl.dom.firstChild;
3401                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3402                 textboxEl.enableDisplayMode();
3403                 textboxEl.addKeyListener([10,13], function(){
3404                     if(dlg.isVisible() && opt && opt.buttons){
3405                         if(opt.buttons.ok){
3406                             handleButton("ok");
3407                         }else if(opt.buttons.yes){
3408                             handleButton("yes");
3409                         }
3410                     }
3411                 });
3412                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3413                 textareaEl.enableDisplayMode();
3414                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3415                 progressEl.enableDisplayMode();
3416                 
3417                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3418                 var pf = progressEl.dom.firstChild;
3419                 if (pf) {
3420                     pp = Roo.get(pf.firstChild);
3421                     pp.setHeight(pf.offsetHeight);
3422                 }
3423                 
3424             }
3425             return dlg;
3426         },
3427
3428         /**
3429          * Updates the message box body text
3430          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3431          * the XHTML-compliant non-breaking space character '&amp;#160;')
3432          * @return {Roo.MessageBox} This message box
3433          */
3434         updateText : function(text)
3435         {
3436             if(!dlg.isVisible() && !opt.width){
3437                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3438                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3439             }
3440             msgEl.innerHTML = text || '&#160;';
3441       
3442             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3443             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3444             var w = Math.max(
3445                     Math.min(opt.width || cw , this.maxWidth), 
3446                     Math.max(opt.minWidth || this.minWidth, bwidth)
3447             );
3448             if(opt.prompt){
3449                 activeTextEl.setWidth(w);
3450             }
3451             if(dlg.isVisible()){
3452                 dlg.fixedcenter = false;
3453             }
3454             // to big, make it scroll. = But as usual stupid IE does not support
3455             // !important..
3456             
3457             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3458                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3459                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3460             } else {
3461                 bodyEl.dom.style.height = '';
3462                 bodyEl.dom.style.overflowY = '';
3463             }
3464             if (cw > w) {
3465                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3466             } else {
3467                 bodyEl.dom.style.overflowX = '';
3468             }
3469             
3470             dlg.setContentSize(w, bodyEl.getHeight());
3471             if(dlg.isVisible()){
3472                 dlg.fixedcenter = true;
3473             }
3474             return this;
3475         },
3476
3477         /**
3478          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3479          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3480          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3481          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3482          * @return {Roo.MessageBox} This message box
3483          */
3484         updateProgress : function(value, text){
3485             if(text){
3486                 this.updateText(text);
3487             }
3488             
3489             if (pp) { // weird bug on my firefox - for some reason this is not defined
3490                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3491                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3492             }
3493             return this;
3494         },        
3495
3496         /**
3497          * Returns true if the message box is currently displayed
3498          * @return {Boolean} True if the message box is visible, else false
3499          */
3500         isVisible : function(){
3501             return dlg && dlg.isVisible();  
3502         },
3503
3504         /**
3505          * Hides the message box if it is displayed
3506          */
3507         hide : function(){
3508             if(this.isVisible()){
3509                 dlg.hide();
3510             }  
3511         },
3512
3513         /**
3514          * Displays a new message box, or reinitializes an existing message box, based on the config options
3515          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3516          * The following config object properties are supported:
3517          * <pre>
3518 Property    Type             Description
3519 ----------  ---------------  ------------------------------------------------------------------------------------
3520 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3521                                    closes (defaults to undefined)
3522 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3523                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3524 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3525                                    progress and wait dialogs will ignore this property and always hide the
3526                                    close button as they can only be closed programmatically.
3527 cls               String           A custom CSS class to apply to the message box element
3528 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3529                                    displayed (defaults to 75)
3530 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3531                                    function will be btn (the name of the button that was clicked, if applicable,
3532                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3533                                    Progress and wait dialogs will ignore this option since they do not respond to
3534                                    user actions and can only be closed programmatically, so any required function
3535                                    should be called by the same code after it closes the dialog.
3536 icon              String           A CSS class that provides a background image to be used as an icon for
3537                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3538 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3539 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3540 modal             Boolean          False to allow user interaction with the page while the message box is
3541                                    displayed (defaults to true)
3542 msg               String           A string that will replace the existing message box body text (defaults
3543                                    to the XHTML-compliant non-breaking space character '&#160;')
3544 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3545 progress          Boolean          True to display a progress bar (defaults to false)
3546 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3547 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3548 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3549 title             String           The title text
3550 value             String           The string value to set into the active textbox element if displayed
3551 wait              Boolean          True to display a progress bar (defaults to false)
3552 width             Number           The width of the dialog in pixels
3553 </pre>
3554          *
3555          * Example usage:
3556          * <pre><code>
3557 Roo.Msg.show({
3558    title: 'Address',
3559    msg: 'Please enter your address:',
3560    width: 300,
3561    buttons: Roo.MessageBox.OKCANCEL,
3562    multiline: true,
3563    fn: saveAddress,
3564    animEl: 'addAddressBtn'
3565 });
3566 </code></pre>
3567          * @param {Object} config Configuration options
3568          * @return {Roo.MessageBox} This message box
3569          */
3570         show : function(options)
3571         {
3572             
3573             // this causes nightmares if you show one dialog after another
3574             // especially on callbacks..
3575              
3576             if(this.isVisible()){
3577                 
3578                 this.hide();
3579                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3580                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3581                 Roo.log("New Dialog Message:" +  options.msg )
3582                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3583                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3584                 
3585             }
3586             var d = this.getDialog();
3587             opt = options;
3588             d.setTitle(opt.title || "&#160;");
3589             d.closeEl.setDisplayed(opt.closable !== false);
3590             activeTextEl = textboxEl;
3591             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3592             if(opt.prompt){
3593                 if(opt.multiline){
3594                     textboxEl.hide();
3595                     textareaEl.show();
3596                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3597                         opt.multiline : this.defaultTextHeight);
3598                     activeTextEl = textareaEl;
3599                 }else{
3600                     textboxEl.show();
3601                     textareaEl.hide();
3602                 }
3603             }else{
3604                 textboxEl.hide();
3605                 textareaEl.hide();
3606             }
3607             progressEl.setDisplayed(opt.progress === true);
3608             if (opt.progress) {
3609                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3610             }
3611             this.updateProgress(0);
3612             activeTextEl.dom.value = opt.value || "";
3613             if(opt.prompt){
3614                 dlg.setDefaultButton(activeTextEl);
3615             }else{
3616                 var bs = opt.buttons;
3617                 var db = null;
3618                 if(bs && bs.ok){
3619                     db = buttons["ok"];
3620                 }else if(bs && bs.yes){
3621                     db = buttons["yes"];
3622                 }
3623                 dlg.setDefaultButton(db);
3624             }
3625             bwidth = updateButtons(opt.buttons);
3626             this.updateText(opt.msg);
3627             if(opt.cls){
3628                 d.el.addClass(opt.cls);
3629             }
3630             d.proxyDrag = opt.proxyDrag === true;
3631             d.modal = opt.modal !== false;
3632             d.mask = opt.modal !== false ? mask : false;
3633             if(!d.isVisible()){
3634                 // force it to the end of the z-index stack so it gets a cursor in FF
3635                 document.body.appendChild(dlg.el.dom);
3636                 d.animateTarget = null;
3637                 d.show(options.animEl);
3638             }
3639             return this;
3640         },
3641
3642         /**
3643          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3644          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3645          * and closing the message box when the process is complete.
3646          * @param {String} title The title bar text
3647          * @param {String} msg The message box body text
3648          * @return {Roo.MessageBox} This message box
3649          */
3650         progress : function(title, msg){
3651             this.show({
3652                 title : title,
3653                 msg : msg,
3654                 buttons: false,
3655                 progress:true,
3656                 closable:false,
3657                 minWidth: this.minProgressWidth,
3658                 modal : true
3659             });
3660             return this;
3661         },
3662
3663         /**
3664          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3665          * If a callback function is passed it will be called after the user clicks the button, and the
3666          * id of the button that was clicked will be passed as the only parameter to the callback
3667          * (could also be the top-right close button).
3668          * @param {String} title The title bar text
3669          * @param {String} msg The message box body text
3670          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3671          * @param {Object} scope (optional) The scope of the callback function
3672          * @return {Roo.MessageBox} This message box
3673          */
3674         alert : function(title, msg, fn, scope)
3675         {
3676             this.show({
3677                 title : title,
3678                 msg : msg,
3679                 buttons: this.OK,
3680                 fn: fn,
3681                 closable : false,
3682                 scope : scope,
3683                 modal : true
3684             });
3685             return this;
3686         },
3687
3688         /**
3689          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3690          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3691          * You are responsible for closing the message box when the process is complete.
3692          * @param {String} msg The message box body text
3693          * @param {String} title (optional) The title bar text
3694          * @return {Roo.MessageBox} This message box
3695          */
3696         wait : function(msg, title){
3697             this.show({
3698                 title : title,
3699                 msg : msg,
3700                 buttons: false,
3701                 closable:false,
3702                 progress:true,
3703                 modal:true,
3704                 width:300,
3705                 wait:true
3706             });
3707             waitTimer = Roo.TaskMgr.start({
3708                 run: function(i){
3709                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3710                 },
3711                 interval: 1000
3712             });
3713             return this;
3714         },
3715
3716         /**
3717          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3718          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3719          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3720          * @param {String} title The title bar text
3721          * @param {String} msg The message box body text
3722          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3723          * @param {Object} scope (optional) The scope of the callback function
3724          * @return {Roo.MessageBox} This message box
3725          */
3726         confirm : function(title, msg, fn, scope){
3727             this.show({
3728                 title : title,
3729                 msg : msg,
3730                 buttons: this.YESNO,
3731                 fn: fn,
3732                 scope : scope,
3733                 modal : true
3734             });
3735             return this;
3736         },
3737
3738         /**
3739          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3740          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3741          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3742          * (could also be the top-right close button) and the text that was entered will be passed as the two
3743          * parameters to the callback.
3744          * @param {String} title The title bar text
3745          * @param {String} msg The message box body text
3746          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3747          * @param {Object} scope (optional) The scope of the callback function
3748          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3749          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3750          * @return {Roo.MessageBox} This message box
3751          */
3752         prompt : function(title, msg, fn, scope, multiline){
3753             this.show({
3754                 title : title,
3755                 msg : msg,
3756                 buttons: this.OKCANCEL,
3757                 fn: fn,
3758                 minWidth:250,
3759                 scope : scope,
3760                 prompt:true,
3761                 multiline: multiline,
3762                 modal : true
3763             });
3764             return this;
3765         },
3766
3767         /**
3768          * Button config that displays a single OK button
3769          * @type Object
3770          */
3771         OK : {ok:true},
3772         /**
3773          * Button config that displays Yes and No buttons
3774          * @type Object
3775          */
3776         YESNO : {yes:true, no:true},
3777         /**
3778          * Button config that displays OK and Cancel buttons
3779          * @type Object
3780          */
3781         OKCANCEL : {ok:true, cancel:true},
3782         /**
3783          * Button config that displays Yes, No and Cancel buttons
3784          * @type Object
3785          */
3786         YESNOCANCEL : {yes:true, no:true, cancel:true},
3787
3788         /**
3789          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3790          * @type Number
3791          */
3792         defaultTextHeight : 75,
3793         /**
3794          * The maximum width in pixels of the message box (defaults to 600)
3795          * @type Number
3796          */
3797         maxWidth : 600,
3798         /**
3799          * The minimum width in pixels of the message box (defaults to 100)
3800          * @type Number
3801          */
3802         minWidth : 100,
3803         /**
3804          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3805          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3806          * @type Number
3807          */
3808         minProgressWidth : 250,
3809         /**
3810          * An object containing the default button text strings that can be overriden for localized language support.
3811          * Supported properties are: ok, cancel, yes and no.
3812          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3813          * @type Object
3814          */
3815         buttonText : {
3816             ok : "OK",
3817             cancel : "Cancel",
3818             yes : "Yes",
3819             no : "No"
3820         }
3821     };
3822 }();
3823
3824 /**
3825  * Shorthand for {@link Roo.MessageBox}
3826  */
3827 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3828 Roo.Msg = Roo.Msg || Roo.MessageBox;
3829 /*
3830  * - LGPL
3831  *
3832  * navbar
3833  * 
3834  */
3835
3836 /**
3837  * @class Roo.bootstrap.Navbar
3838  * @extends Roo.bootstrap.Component
3839  * Bootstrap Navbar class
3840
3841  * @constructor
3842  * Create a new Navbar
3843  * @param {Object} config The config object
3844  */
3845
3846
3847 Roo.bootstrap.Navbar = function(config){
3848     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3849     this.addEvents({
3850         // raw events
3851         /**
3852          * @event beforetoggle
3853          * Fire before toggle the menu
3854          * @param {Roo.EventObject} e
3855          */
3856         "beforetoggle" : true
3857     });
3858 };
3859
3860 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3861     
3862     
3863    
3864     // private
3865     navItems : false,
3866     loadMask : false,
3867     
3868     
3869     getAutoCreate : function(){
3870         
3871         
3872         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3873         
3874     },
3875     
3876     initEvents :function ()
3877     {
3878         //Roo.log(this.el.select('.navbar-toggle',true));
3879         this.el.select('.navbar-toggle',true).on('click', function() {
3880             if(this.fireEvent('beforetoggle', this) !== false){
3881                 var ce = this.el.select('.navbar-collapse',true).first();
3882                 ce.toggleClass('in'); // old...
3883                 if (ce.hasClass('collapse')) {
3884                     // show it...
3885                     ce.removeClass('collapse');
3886                     ce.addClass('show');
3887                     var h = ce.getHeight();
3888                     Roo.log(h);
3889                     ce.removeClass('show');
3890                     // at this point we should be able to see it..
3891                     ce.addClass('collapsing');
3892                     
3893                     ce.setHeight(0); // resize it ...
3894                     ce.on('transitionend', function() {
3895                         Roo.log('done transition');
3896                         ce.removeClass('collapsing');
3897                         ce.addClass('show');
3898                         ce.removeClass('collapse');
3899
3900                         ce.dom.style.height = '';
3901                     }, this, { single: true} );
3902                     ce.setHeight(h);
3903                     
3904                 } else {
3905                     ce.setHeight(ce.getHeight());
3906                     ce.removeClass('show');
3907                     ce.addClass('collapsing');
3908                     
3909                     ce.on('transitionend', function() {
3910                         ce.dom.style.height = '';
3911                         ce.removeClass('collapsing');
3912                         ce.addClass('collapse');
3913                     }, this, { single: true} );
3914                     ce.setHeight(0);
3915                 }
3916             }
3917             
3918         }, this);
3919         
3920         var mark = {
3921             tag: "div",
3922             cls:"x-dlg-mask"
3923         };
3924         
3925         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3926         
3927         var size = this.el.getSize();
3928         this.maskEl.setSize(size.width, size.height);
3929         this.maskEl.enableDisplayMode("block");
3930         this.maskEl.hide();
3931         
3932         if(this.loadMask){
3933             this.maskEl.show();
3934         }
3935     },
3936     
3937     
3938     getChildContainer : function()
3939     {
3940         if (this.el.select('.collapse').getCount()) {
3941             return this.el.select('.collapse',true).first();
3942         }
3943         
3944         return this.el;
3945     },
3946     
3947     mask : function()
3948     {
3949         this.maskEl.show();
3950     },
3951     
3952     unmask : function()
3953     {
3954         this.maskEl.hide();
3955     } 
3956     
3957     
3958     
3959     
3960 });
3961
3962
3963
3964  
3965
3966  /*
3967  * - LGPL
3968  *
3969  * navbar
3970  * 
3971  */
3972
3973 /**
3974  * @class Roo.bootstrap.NavSimplebar
3975  * @extends Roo.bootstrap.Navbar
3976  * Bootstrap Sidebar class
3977  *
3978  * @cfg {Boolean} inverse is inverted color
3979  * 
3980  * @cfg {String} type (nav | pills | tabs)
3981  * @cfg {Boolean} arrangement stacked | justified
3982  * @cfg {String} align (left | right) alignment
3983  * 
3984  * @cfg {Boolean} main (true|false) main nav bar? default false
3985  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3986  * 
3987  * @cfg {String} tag (header|footer|nav|div) default is nav 
3988
3989  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3990  * 
3991  * 
3992  * @constructor
3993  * Create a new Sidebar
3994  * @param {Object} config The config object
3995  */
3996
3997
3998 Roo.bootstrap.NavSimplebar = function(config){
3999     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4000 };
4001
4002 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
4003     
4004     inverse: false,
4005     
4006     type: false,
4007     arrangement: '',
4008     align : false,
4009     
4010     weight : 'light',
4011     
4012     main : false,
4013     
4014     
4015     tag : false,
4016     
4017     
4018     getAutoCreate : function(){
4019         
4020         
4021         var cfg = {
4022             tag : this.tag || 'div',
4023             cls : 'navbar navbar-expand-lg roo-navbar-simple'
4024         };
4025         if (['light','white'].indexOf(this.weight) > -1) {
4026             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4027         }
4028         cfg.cls += ' bg-' + this.weight;
4029         
4030         if (this.inverse) {
4031             cfg.cls += ' navbar-inverse';
4032             
4033         }
4034         
4035         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4036         
4037         //if (Roo.bootstrap.version == 4) {
4038         //    return cfg;
4039         //}
4040         
4041         cfg.cn = [
4042             {
4043                 cls: 'nav',
4044                 tag : 'ul'
4045             }
4046         ];
4047         
4048          
4049         this.type = this.type || 'nav';
4050         if (['tabs','pills'].indexOf(this.type) != -1) {
4051             cfg.cn[0].cls += ' nav-' + this.type
4052         
4053         
4054         } else {
4055             if (this.type!=='nav') {
4056                 Roo.log('nav type must be nav/tabs/pills')
4057             }
4058             cfg.cn[0].cls += ' navbar-nav'
4059         }
4060         
4061         
4062         
4063         
4064         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4065             cfg.cn[0].cls += ' nav-' + this.arrangement;
4066         }
4067         
4068         
4069         if (this.align === 'right') {
4070             cfg.cn[0].cls += ' navbar-right';
4071         }
4072         
4073         
4074         
4075         
4076         return cfg;
4077     
4078         
4079     }
4080     
4081     
4082     
4083 });
4084
4085
4086
4087  
4088
4089  
4090        /*
4091  * - LGPL
4092  *
4093  * navbar
4094  * navbar-fixed-top
4095  * navbar-expand-md  fixed-top 
4096  */
4097
4098 /**
4099  * @class Roo.bootstrap.NavHeaderbar
4100  * @extends Roo.bootstrap.NavSimplebar
4101  * Bootstrap Sidebar class
4102  *
4103  * @cfg {String} brand what is brand
4104  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4105  * @cfg {String} brand_href href of the brand
4106  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4107  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4108  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4109  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4110  * 
4111  * @constructor
4112  * Create a new Sidebar
4113  * @param {Object} config The config object
4114  */
4115
4116
4117 Roo.bootstrap.NavHeaderbar = function(config){
4118     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4119       
4120 };
4121
4122 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4123     
4124     position: '',
4125     brand: '',
4126     brand_href: false,
4127     srButton : true,
4128     autohide : false,
4129     desktopCenter : false,
4130    
4131     
4132     getAutoCreate : function(){
4133         
4134         var   cfg = {
4135             tag: this.nav || 'nav',
4136             cls: 'navbar navbar-expand-md',
4137             role: 'navigation',
4138             cn: []
4139         };
4140         
4141         var cn = cfg.cn;
4142         if (this.desktopCenter) {
4143             cn.push({cls : 'container', cn : []});
4144             cn = cn[0].cn;
4145         }
4146         
4147         if(this.srButton){
4148             var btn = {
4149                 tag: 'button',
4150                 type: 'button',
4151                 cls: 'navbar-toggle navbar-toggler',
4152                 'data-toggle': 'collapse',
4153                 cn: [
4154                     {
4155                         tag: 'span',
4156                         cls: 'sr-only',
4157                         html: 'Toggle navigation'
4158                     },
4159                     {
4160                         tag: 'span',
4161                         cls: 'icon-bar navbar-toggler-icon'
4162                     },
4163                     {
4164                         tag: 'span',
4165                         cls: 'icon-bar'
4166                     },
4167                     {
4168                         tag: 'span',
4169                         cls: 'icon-bar'
4170                     }
4171                 ]
4172             };
4173             
4174             cn.push( Roo.bootstrap.version == 4 ? btn : {
4175                 tag: 'div',
4176                 cls: 'navbar-header',
4177                 cn: [
4178                     btn
4179                 ]
4180             });
4181         }
4182         
4183         cn.push({
4184             tag: 'div',
4185             cls: 'collapse navbar-collapse',
4186             cn : []
4187         });
4188         
4189         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4190         
4191         if (['light','white'].indexOf(this.weight) > -1) {
4192             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4193         }
4194         cfg.cls += ' bg-' + this.weight;
4195         
4196         
4197         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4198             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4199             
4200             // tag can override this..
4201             
4202             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4203         }
4204         
4205         if (this.brand !== '') {
4206             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4207             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4208                 tag: 'a',
4209                 href: this.brand_href ? this.brand_href : '#',
4210                 cls: 'navbar-brand',
4211                 cn: [
4212                 this.brand
4213                 ]
4214             });
4215         }
4216         
4217         if(this.main){
4218             cfg.cls += ' main-nav';
4219         }
4220         
4221         
4222         return cfg;
4223
4224         
4225     },
4226     getHeaderChildContainer : function()
4227     {
4228         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4229             return this.el.select('.navbar-header',true).first();
4230         }
4231         
4232         return this.getChildContainer();
4233     },
4234     
4235     
4236     initEvents : function()
4237     {
4238         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4239         
4240         if (this.autohide) {
4241             
4242             var prevScroll = 0;
4243             var ft = this.el;
4244             
4245             Roo.get(document).on('scroll',function(e) {
4246                 var ns = Roo.get(document).getScroll().top;
4247                 var os = prevScroll;
4248                 prevScroll = ns;
4249                 
4250                 if(ns > os){
4251                     ft.removeClass('slideDown');
4252                     ft.addClass('slideUp');
4253                     return;
4254                 }
4255                 ft.removeClass('slideUp');
4256                 ft.addClass('slideDown');
4257                  
4258               
4259           },this);
4260         }
4261     }    
4262     
4263 });
4264
4265
4266
4267  
4268
4269  /*
4270  * - LGPL
4271  *
4272  * navbar
4273  * 
4274  */
4275
4276 /**
4277  * @class Roo.bootstrap.NavSidebar
4278  * @extends Roo.bootstrap.Navbar
4279  * Bootstrap Sidebar class
4280  * 
4281  * @constructor
4282  * Create a new Sidebar
4283  * @param {Object} config The config object
4284  */
4285
4286
4287 Roo.bootstrap.NavSidebar = function(config){
4288     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4289 };
4290
4291 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4292     
4293     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4294     
4295     getAutoCreate : function(){
4296         
4297         
4298         return  {
4299             tag: 'div',
4300             cls: 'sidebar sidebar-nav'
4301         };
4302     
4303         
4304     }
4305     
4306     
4307     
4308 });
4309
4310
4311
4312  
4313
4314  /*
4315  * - LGPL
4316  *
4317  * nav group
4318  * 
4319  */
4320
4321 /**
4322  * @class Roo.bootstrap.NavGroup
4323  * @extends Roo.bootstrap.Component
4324  * Bootstrap NavGroup class
4325  * @cfg {String} align (left|right)
4326  * @cfg {Boolean} inverse
4327  * @cfg {String} type (nav|pills|tab) default nav
4328  * @cfg {String} navId - reference Id for navbar.
4329
4330  * 
4331  * @constructor
4332  * Create a new nav group
4333  * @param {Object} config The config object
4334  */
4335
4336 Roo.bootstrap.NavGroup = function(config){
4337     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4338     this.navItems = [];
4339    
4340     Roo.bootstrap.NavGroup.register(this);
4341      this.addEvents({
4342         /**
4343              * @event changed
4344              * Fires when the active item changes
4345              * @param {Roo.bootstrap.NavGroup} this
4346              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4347              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4348          */
4349         'changed': true
4350      });
4351     
4352 };
4353
4354 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4355     
4356     align: '',
4357     inverse: false,
4358     form: false,
4359     type: 'nav',
4360     navId : '',
4361     // private
4362     
4363     navItems : false, 
4364     
4365     getAutoCreate : function()
4366     {
4367         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4368         
4369         cfg = {
4370             tag : 'ul',
4371             cls: 'nav' 
4372         };
4373         if (Roo.bootstrap.version == 4) {
4374             if (['nav','pills'].indexOf(this.type) != -1) {
4375                 cfg.cls = ' navbar-' + this.type; 
4376             }
4377         } else {
4378             if (['tabs','pills'].indexOf(this.type) != -1) {
4379                 cfg.cls += ' nav-' + this.type
4380             } else {
4381                 if (this.type !== 'nav') {
4382                     Roo.log('nav type must be nav/tabs/pills')
4383                 }
4384                 cfg.cls += ' navbar-nav'
4385             }
4386         }
4387         
4388         if (this.parent() && this.parent().sidebar) {
4389             cfg = {
4390                 tag: 'ul',
4391                 cls: 'dashboard-menu sidebar-menu'
4392             };
4393             
4394             return cfg;
4395         }
4396         
4397         if (this.form === true) {
4398             cfg = {
4399                 tag: 'form',
4400                 cls: 'navbar-form form-inline'
4401             };
4402             
4403             if (this.align === 'right') {
4404                 cfg.cls += ' navbar-right ml-md-auto';
4405             } else {
4406                 cfg.cls += ' navbar-left';
4407             }
4408         }
4409         
4410         if (this.align === 'right') {
4411             cfg.cls += ' navbar-right ml-md-auto';
4412         } else {
4413             cfg.cls += ' mr-auto';
4414         }
4415         
4416         if (this.inverse) {
4417             cfg.cls += ' navbar-inverse';
4418             
4419         }
4420         
4421         
4422         return cfg;
4423     },
4424     /**
4425     * sets the active Navigation item
4426     * @param {Roo.bootstrap.NavItem} the new current navitem
4427     */
4428     setActiveItem : function(item)
4429     {
4430         var prev = false;
4431         Roo.each(this.navItems, function(v){
4432             if (v == item) {
4433                 return ;
4434             }
4435             if (v.isActive()) {
4436                 v.setActive(false, true);
4437                 prev = v;
4438                 
4439             }
4440             
4441         });
4442
4443         item.setActive(true, true);
4444         this.fireEvent('changed', this, item, prev);
4445         
4446         
4447     },
4448     /**
4449     * gets the active Navigation item
4450     * @return {Roo.bootstrap.NavItem} the current navitem
4451     */
4452     getActive : function()
4453     {
4454         
4455         var prev = false;
4456         Roo.each(this.navItems, function(v){
4457             
4458             if (v.isActive()) {
4459                 prev = v;
4460                 
4461             }
4462             
4463         });
4464         return prev;
4465     },
4466     
4467     indexOfNav : function()
4468     {
4469         
4470         var prev = false;
4471         Roo.each(this.navItems, function(v,i){
4472             
4473             if (v.isActive()) {
4474                 prev = i;
4475                 
4476             }
4477             
4478         });
4479         return prev;
4480     },
4481     /**
4482     * adds a Navigation item
4483     * @param {Roo.bootstrap.NavItem} the navitem to add
4484     */
4485     addItem : function(cfg)
4486     {
4487         if (this.form && Roo.bootstrap.version == 4) {
4488             cfg.tag = 'div';
4489         }
4490         var cn = new Roo.bootstrap.NavItem(cfg);
4491         this.register(cn);
4492         cn.parentId = this.id;
4493         cn.onRender(this.el, null);
4494         return cn;
4495     },
4496     /**
4497     * register a Navigation item
4498     * @param {Roo.bootstrap.NavItem} the navitem to add
4499     */
4500     register : function(item)
4501     {
4502         this.navItems.push( item);
4503         item.navId = this.navId;
4504     
4505     },
4506     
4507     /**
4508     * clear all the Navigation item
4509     */
4510    
4511     clearAll : function()
4512     {
4513         this.navItems = [];
4514         this.el.dom.innerHTML = '';
4515     },
4516     
4517     getNavItem: function(tabId)
4518     {
4519         var ret = false;
4520         Roo.each(this.navItems, function(e) {
4521             if (e.tabId == tabId) {
4522                ret =  e;
4523                return false;
4524             }
4525             return true;
4526             
4527         });
4528         return ret;
4529     },
4530     
4531     setActiveNext : function()
4532     {
4533         var i = this.indexOfNav(this.getActive());
4534         if (i > this.navItems.length) {
4535             return;
4536         }
4537         this.setActiveItem(this.navItems[i+1]);
4538     },
4539     setActivePrev : function()
4540     {
4541         var i = this.indexOfNav(this.getActive());
4542         if (i  < 1) {
4543             return;
4544         }
4545         this.setActiveItem(this.navItems[i-1]);
4546     },
4547     clearWasActive : function(except) {
4548         Roo.each(this.navItems, function(e) {
4549             if (e.tabId != except.tabId && e.was_active) {
4550                e.was_active = false;
4551                return false;
4552             }
4553             return true;
4554             
4555         });
4556     },
4557     getWasActive : function ()
4558     {
4559         var r = false;
4560         Roo.each(this.navItems, function(e) {
4561             if (e.was_active) {
4562                r = e;
4563                return false;
4564             }
4565             return true;
4566             
4567         });
4568         return r;
4569     }
4570     
4571     
4572 });
4573
4574  
4575 Roo.apply(Roo.bootstrap.NavGroup, {
4576     
4577     groups: {},
4578      /**
4579     * register a Navigation Group
4580     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4581     */
4582     register : function(navgrp)
4583     {
4584         this.groups[navgrp.navId] = navgrp;
4585         
4586     },
4587     /**
4588     * fetch a Navigation Group based on the navigation ID
4589     * @param {string} the navgroup to add
4590     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4591     */
4592     get: function(navId) {
4593         if (typeof(this.groups[navId]) == 'undefined') {
4594             return false;
4595             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4596         }
4597         return this.groups[navId] ;
4598     }
4599     
4600     
4601     
4602 });
4603
4604  /*
4605  * - LGPL
4606  *
4607  * row
4608  * 
4609  */
4610
4611 /**
4612  * @class Roo.bootstrap.NavItem
4613  * @extends Roo.bootstrap.Component
4614  * Bootstrap Navbar.NavItem class
4615  * @cfg {String} href  link to
4616  * @cfg {String} html content of button
4617  * @cfg {String} badge text inside badge
4618  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4619  * @cfg {String} glyphicon DEPRICATED - use fa
4620  * @cfg {String} icon DEPRICATED - use fa
4621  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4622  * @cfg {Boolean} active Is item active
4623  * @cfg {Boolean} disabled Is item disabled
4624  
4625  * @cfg {Boolean} preventDefault (true | false) default false
4626  * @cfg {String} tabId the tab that this item activates.
4627  * @cfg {String} tagtype (a|span) render as a href or span?
4628  * @cfg {Boolean} animateRef (true|false) link to element default false  
4629   
4630  * @constructor
4631  * Create a new Navbar Item
4632  * @param {Object} config The config object
4633  */
4634 Roo.bootstrap.NavItem = function(config){
4635     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4636     this.addEvents({
4637         // raw events
4638         /**
4639          * @event click
4640          * The raw click event for the entire grid.
4641          * @param {Roo.EventObject} e
4642          */
4643         "click" : true,
4644          /**
4645             * @event changed
4646             * Fires when the active item active state changes
4647             * @param {Roo.bootstrap.NavItem} this
4648             * @param {boolean} state the new state
4649              
4650          */
4651         'changed': true,
4652         /**
4653             * @event scrollto
4654             * Fires when scroll to element
4655             * @param {Roo.bootstrap.NavItem} this
4656             * @param {Object} options
4657             * @param {Roo.EventObject} e
4658              
4659          */
4660         'scrollto': true
4661     });
4662    
4663 };
4664
4665 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4666     
4667     href: false,
4668     html: '',
4669     badge: '',
4670     icon: false,
4671     fa : false,
4672     glyphicon: false,
4673     active: false,
4674     preventDefault : false,
4675     tabId : false,
4676     tagtype : 'a',
4677     tag: 'li',
4678     disabled : false,
4679     animateRef : false,
4680     was_active : false,
4681     
4682     getAutoCreate : function(){
4683          
4684         var cfg = {
4685             tag: this.tag,
4686             cls: 'nav-item'
4687             
4688         };
4689         
4690         if (this.active) {
4691             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4692         }
4693         if (this.disabled) {
4694             cfg.cls += ' disabled';
4695         }
4696         
4697         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4698             cfg.cn = [
4699                 {
4700                     tag: this.tagtype,
4701                     href : this.href || "#",
4702                     html: this.html || ''
4703                 }
4704             ];
4705             if (this.tagtype == 'a') {
4706                 cfg.cn[0].cls = 'nav-link';
4707             }
4708             if (this.icon) {
4709                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4710             }
4711             if (this.fa) {
4712                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>'
4713             }
4714             if(this.glyphicon) {
4715                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4716             }
4717             
4718             if (this.menu) {
4719                 
4720                 cfg.cn[0].html += " <span class='caret'></span>";
4721              
4722             }
4723             
4724             if (this.badge !== '') {
4725                  
4726                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4727             }
4728         }
4729         
4730         
4731         
4732         return cfg;
4733     },
4734     onRender : function(ct, position)
4735     {
4736        // Roo.log("Call onRender: " + this.xtype);
4737         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
4738             this.tag = 'div';
4739         }
4740         
4741         return Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
4742     },
4743       
4744     
4745     initEvents: function() 
4746     {
4747         if (typeof (this.menu) != 'undefined') {
4748             this.menu.parentType = this.xtype;
4749             this.menu.triggerEl = this.el;
4750             this.menu = this.addxtype(Roo.apply({}, this.menu));
4751         }
4752         
4753         this.el.select('a',true).on('click', this.onClick, this);
4754         
4755         if(this.tagtype == 'span'){
4756             this.el.select('span',true).on('click', this.onClick, this);
4757         }
4758        
4759         // at this point parent should be available..
4760         this.parent().register(this);
4761     },
4762     
4763     onClick : function(e)
4764     {
4765         if (e.getTarget('.dropdown-menu-item')) {
4766             // did you click on a menu itemm.... - then don't trigger onclick..
4767             return;
4768         }
4769         
4770         if(
4771                 this.preventDefault || 
4772                 this.href == '#' 
4773         ){
4774             Roo.log("NavItem - prevent Default?");
4775             e.preventDefault();
4776         }
4777         
4778         if (this.disabled) {
4779             return;
4780         }
4781         
4782         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4783         if (tg && tg.transition) {
4784             Roo.log("waiting for the transitionend");
4785             return;
4786         }
4787         
4788         
4789         
4790         //Roo.log("fire event clicked");
4791         if(this.fireEvent('click', this, e) === false){
4792             return;
4793         };
4794         
4795         if(this.tagtype == 'span'){
4796             return;
4797         }
4798         
4799         //Roo.log(this.href);
4800         var ael = this.el.select('a',true).first();
4801         //Roo.log(ael);
4802         
4803         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4804             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4805             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4806                 return; // ignore... - it's a 'hash' to another page.
4807             }
4808             Roo.log("NavItem - prevent Default?");
4809             e.preventDefault();
4810             this.scrollToElement(e);
4811         }
4812         
4813         
4814         var p =  this.parent();
4815    
4816         if (['tabs','pills'].indexOf(p.type)!==-1) {
4817             if (typeof(p.setActiveItem) !== 'undefined') {
4818                 p.setActiveItem(this);
4819             }
4820         }
4821         
4822         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4823         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4824             // remove the collapsed menu expand...
4825             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4826         }
4827     },
4828     
4829     isActive: function () {
4830         return this.active
4831     },
4832     setActive : function(state, fire, is_was_active)
4833     {
4834         if (this.active && !state && this.navId) {
4835             this.was_active = true;
4836             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4837             if (nv) {
4838                 nv.clearWasActive(this);
4839             }
4840             
4841         }
4842         this.active = state;
4843         
4844         if (!state ) {
4845             this.el.removeClass('active');
4846         } else if (!this.el.hasClass('active')) {
4847             this.el.addClass('active');
4848         }
4849         if (fire) {
4850             this.fireEvent('changed', this, state);
4851         }
4852         
4853         // show a panel if it's registered and related..
4854         
4855         if (!this.navId || !this.tabId || !state || is_was_active) {
4856             return;
4857         }
4858         
4859         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4860         if (!tg) {
4861             return;
4862         }
4863         var pan = tg.getPanelByName(this.tabId);
4864         if (!pan) {
4865             return;
4866         }
4867         // if we can not flip to new panel - go back to old nav highlight..
4868         if (false == tg.showPanel(pan)) {
4869             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4870             if (nv) {
4871                 var onav = nv.getWasActive();
4872                 if (onav) {
4873                     onav.setActive(true, false, true);
4874                 }
4875             }
4876             
4877         }
4878         
4879         
4880         
4881     },
4882      // this should not be here...
4883     setDisabled : function(state)
4884     {
4885         this.disabled = state;
4886         if (!state ) {
4887             this.el.removeClass('disabled');
4888         } else if (!this.el.hasClass('disabled')) {
4889             this.el.addClass('disabled');
4890         }
4891         
4892     },
4893     
4894     /**
4895      * Fetch the element to display the tooltip on.
4896      * @return {Roo.Element} defaults to this.el
4897      */
4898     tooltipEl : function()
4899     {
4900         return this.el.select('' + this.tagtype + '', true).first();
4901     },
4902     
4903     scrollToElement : function(e)
4904     {
4905         var c = document.body;
4906         
4907         /*
4908          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4909          */
4910         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4911             c = document.documentElement;
4912         }
4913         
4914         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4915         
4916         if(!target){
4917             return;
4918         }
4919
4920         var o = target.calcOffsetsTo(c);
4921         
4922         var options = {
4923             target : target,
4924             value : o[1]
4925         };
4926         
4927         this.fireEvent('scrollto', this, options, e);
4928         
4929         Roo.get(c).scrollTo('top', options.value, true);
4930         
4931         return;
4932     }
4933 });
4934  
4935
4936  /*
4937  * - LGPL
4938  *
4939  * sidebar item
4940  *
4941  *  li
4942  *    <span> icon </span>
4943  *    <span> text </span>
4944  *    <span>badge </span>
4945  */
4946
4947 /**
4948  * @class Roo.bootstrap.NavSidebarItem
4949  * @extends Roo.bootstrap.NavItem
4950  * Bootstrap Navbar.NavSidebarItem class
4951  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4952  * {Boolean} open is the menu open
4953  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4954  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4955  * {String} buttonSize (sm|md|lg)the extra classes for the button
4956  * {Boolean} showArrow show arrow next to the text (default true)
4957  * @constructor
4958  * Create a new Navbar Button
4959  * @param {Object} config The config object
4960  */
4961 Roo.bootstrap.NavSidebarItem = function(config){
4962     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4963     this.addEvents({
4964         // raw events
4965         /**
4966          * @event click
4967          * The raw click event for the entire grid.
4968          * @param {Roo.EventObject} e
4969          */
4970         "click" : true,
4971          /**
4972             * @event changed
4973             * Fires when the active item active state changes
4974             * @param {Roo.bootstrap.NavSidebarItem} this
4975             * @param {boolean} state the new state
4976              
4977          */
4978         'changed': true
4979     });
4980    
4981 };
4982
4983 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4984     
4985     badgeWeight : 'default',
4986     
4987     open: false,
4988     
4989     buttonView : false,
4990     
4991     buttonWeight : 'default',
4992     
4993     buttonSize : 'md',
4994     
4995     showArrow : true,
4996     
4997     getAutoCreate : function(){
4998         
4999         
5000         var a = {
5001                 tag: 'a',
5002                 href : this.href || '#',
5003                 cls: '',
5004                 html : '',
5005                 cn : []
5006         };
5007         
5008         if(this.buttonView){
5009             a = {
5010                 tag: 'button',
5011                 href : this.href || '#',
5012                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5013                 html : this.html,
5014                 cn : []
5015             };
5016         }
5017         
5018         var cfg = {
5019             tag: 'li',
5020             cls: '',
5021             cn: [ a ]
5022         };
5023         
5024         if (this.active) {
5025             cfg.cls += ' active';
5026         }
5027         
5028         if (this.disabled) {
5029             cfg.cls += ' disabled';
5030         }
5031         if (this.open) {
5032             cfg.cls += ' open x-open';
5033         }
5034         // left icon..
5035         if (this.glyphicon || this.icon) {
5036             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5037             a.cn.push({ tag : 'i', cls : c }) ;
5038         }
5039         
5040         if(!this.buttonView){
5041             var span = {
5042                 tag: 'span',
5043                 html : this.html || ''
5044             };
5045
5046             a.cn.push(span);
5047             
5048         }
5049         
5050         if (this.badge !== '') {
5051             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5052         }
5053         
5054         if (this.menu) {
5055             
5056             if(this.showArrow){
5057                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5058             }
5059             
5060             a.cls += ' dropdown-toggle treeview' ;
5061         }
5062         
5063         return cfg;
5064     },
5065     
5066     initEvents : function()
5067     { 
5068         if (typeof (this.menu) != 'undefined') {
5069             this.menu.parentType = this.xtype;
5070             this.menu.triggerEl = this.el;
5071             this.menu = this.addxtype(Roo.apply({}, this.menu));
5072         }
5073         
5074         this.el.on('click', this.onClick, this);
5075         
5076         if(this.badge !== ''){
5077             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5078         }
5079         
5080     },
5081     
5082     onClick : function(e)
5083     {
5084         if(this.disabled){
5085             e.preventDefault();
5086             return;
5087         }
5088         
5089         if(this.preventDefault){
5090             e.preventDefault();
5091         }
5092         
5093         this.fireEvent('click', this);
5094     },
5095     
5096     disable : function()
5097     {
5098         this.setDisabled(true);
5099     },
5100     
5101     enable : function()
5102     {
5103         this.setDisabled(false);
5104     },
5105     
5106     setDisabled : function(state)
5107     {
5108         if(this.disabled == state){
5109             return;
5110         }
5111         
5112         this.disabled = state;
5113         
5114         if (state) {
5115             this.el.addClass('disabled');
5116             return;
5117         }
5118         
5119         this.el.removeClass('disabled');
5120         
5121         return;
5122     },
5123     
5124     setActive : function(state)
5125     {
5126         if(this.active == state){
5127             return;
5128         }
5129         
5130         this.active = state;
5131         
5132         if (state) {
5133             this.el.addClass('active');
5134             return;
5135         }
5136         
5137         this.el.removeClass('active');
5138         
5139         return;
5140     },
5141     
5142     isActive: function () 
5143     {
5144         return this.active;
5145     },
5146     
5147     setBadge : function(str)
5148     {
5149         if(!this.badgeEl){
5150             return;
5151         }
5152         
5153         this.badgeEl.dom.innerHTML = str;
5154     }
5155     
5156    
5157      
5158  
5159 });
5160  
5161
5162  /*
5163  * - LGPL
5164  *
5165  * row
5166  * 
5167  */
5168
5169 /**
5170  * @class Roo.bootstrap.Row
5171  * @extends Roo.bootstrap.Component
5172  * Bootstrap Row class (contains columns...)
5173  * 
5174  * @constructor
5175  * Create a new Row
5176  * @param {Object} config The config object
5177  */
5178
5179 Roo.bootstrap.Row = function(config){
5180     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5181 };
5182
5183 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5184     
5185     getAutoCreate : function(){
5186        return {
5187             cls: 'row clearfix'
5188        };
5189     }
5190     
5191     
5192 });
5193
5194  
5195
5196  /*
5197  * - LGPL
5198  *
5199  * element
5200  * 
5201  */
5202
5203 /**
5204  * @class Roo.bootstrap.Element
5205  * @extends Roo.bootstrap.Component
5206  * Bootstrap Element class
5207  * @cfg {String} html contents of the element
5208  * @cfg {String} tag tag of the element
5209  * @cfg {String} cls class of the element
5210  * @cfg {Boolean} preventDefault (true|false) default false
5211  * @cfg {Boolean} clickable (true|false) default false
5212  * 
5213  * @constructor
5214  * Create a new Element
5215  * @param {Object} config The config object
5216  */
5217
5218 Roo.bootstrap.Element = function(config){
5219     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5220     
5221     this.addEvents({
5222         // raw events
5223         /**
5224          * @event click
5225          * When a element is chick
5226          * @param {Roo.bootstrap.Element} this
5227          * @param {Roo.EventObject} e
5228          */
5229         "click" : true
5230     });
5231 };
5232
5233 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5234     
5235     tag: 'div',
5236     cls: '',
5237     html: '',
5238     preventDefault: false, 
5239     clickable: false,
5240     
5241     getAutoCreate : function(){
5242         
5243         var cfg = {
5244             tag: this.tag,
5245             // cls: this.cls, double assign in parent class Component.js :: onRender
5246             html: this.html
5247         };
5248         
5249         return cfg;
5250     },
5251     
5252     initEvents: function() 
5253     {
5254         Roo.bootstrap.Element.superclass.initEvents.call(this);
5255         
5256         if(this.clickable){
5257             this.el.on('click', this.onClick, this);
5258         }
5259         
5260     },
5261     
5262     onClick : function(e)
5263     {
5264         if(this.preventDefault){
5265             e.preventDefault();
5266         }
5267         
5268         this.fireEvent('click', this, e);
5269     },
5270     
5271     getValue : function()
5272     {
5273         return this.el.dom.innerHTML;
5274     },
5275     
5276     setValue : function(value)
5277     {
5278         this.el.dom.innerHTML = value;
5279     }
5280    
5281 });
5282
5283  
5284
5285  /*
5286  * - LGPL
5287  *
5288  * pagination
5289  * 
5290  */
5291
5292 /**
5293  * @class Roo.bootstrap.Pagination
5294  * @extends Roo.bootstrap.Component
5295  * Bootstrap Pagination class
5296  * @cfg {String} size xs | sm | md | lg
5297  * @cfg {Boolean} inverse false | true
5298  * 
5299  * @constructor
5300  * Create a new Pagination
5301  * @param {Object} config The config object
5302  */
5303
5304 Roo.bootstrap.Pagination = function(config){
5305     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5306 };
5307
5308 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5309     
5310     cls: false,
5311     size: false,
5312     inverse: false,
5313     
5314     getAutoCreate : function(){
5315         var cfg = {
5316             tag: 'ul',
5317                 cls: 'pagination'
5318         };
5319         if (this.inverse) {
5320             cfg.cls += ' inverse';
5321         }
5322         if (this.html) {
5323             cfg.html=this.html;
5324         }
5325         if (this.cls) {
5326             cfg.cls += " " + this.cls;
5327         }
5328         return cfg;
5329     }
5330    
5331 });
5332
5333  
5334
5335  /*
5336  * - LGPL
5337  *
5338  * Pagination item
5339  * 
5340  */
5341
5342
5343 /**
5344  * @class Roo.bootstrap.PaginationItem
5345  * @extends Roo.bootstrap.Component
5346  * Bootstrap PaginationItem class
5347  * @cfg {String} html text
5348  * @cfg {String} href the link
5349  * @cfg {Boolean} preventDefault (true | false) default true
5350  * @cfg {Boolean} active (true | false) default false
5351  * @cfg {Boolean} disabled default false
5352  * 
5353  * 
5354  * @constructor
5355  * Create a new PaginationItem
5356  * @param {Object} config The config object
5357  */
5358
5359
5360 Roo.bootstrap.PaginationItem = function(config){
5361     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5362     this.addEvents({
5363         // raw events
5364         /**
5365          * @event click
5366          * The raw click event for the entire grid.
5367          * @param {Roo.EventObject} e
5368          */
5369         "click" : true
5370     });
5371 };
5372
5373 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5374     
5375     href : false,
5376     html : false,
5377     preventDefault: true,
5378     active : false,
5379     cls : false,
5380     disabled: false,
5381     
5382     getAutoCreate : function(){
5383         var cfg= {
5384             tag: 'li',
5385             cn: [
5386                 {
5387                     tag : 'a',
5388                     href : this.href ? this.href : '#',
5389                     html : this.html ? this.html : ''
5390                 }
5391             ]
5392         };
5393         
5394         if(this.cls){
5395             cfg.cls = this.cls;
5396         }
5397         
5398         if(this.disabled){
5399             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5400         }
5401         
5402         if(this.active){
5403             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5404         }
5405         
5406         return cfg;
5407     },
5408     
5409     initEvents: function() {
5410         
5411         this.el.on('click', this.onClick, this);
5412         
5413     },
5414     onClick : function(e)
5415     {
5416         Roo.log('PaginationItem on click ');
5417         if(this.preventDefault){
5418             e.preventDefault();
5419         }
5420         
5421         if(this.disabled){
5422             return;
5423         }
5424         
5425         this.fireEvent('click', this, e);
5426     }
5427    
5428 });
5429
5430  
5431
5432  /*
5433  * - LGPL
5434  *
5435  * slider
5436  * 
5437  */
5438
5439
5440 /**
5441  * @class Roo.bootstrap.Slider
5442  * @extends Roo.bootstrap.Component
5443  * Bootstrap Slider class
5444  *    
5445  * @constructor
5446  * Create a new Slider
5447  * @param {Object} config The config object
5448  */
5449
5450 Roo.bootstrap.Slider = function(config){
5451     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5452 };
5453
5454 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5455     
5456     getAutoCreate : function(){
5457         
5458         var cfg = {
5459             tag: 'div',
5460             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5461             cn: [
5462                 {
5463                     tag: 'a',
5464                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5465                 }
5466             ]
5467         };
5468         
5469         return cfg;
5470     }
5471    
5472 });
5473
5474  /*
5475  * Based on:
5476  * Ext JS Library 1.1.1
5477  * Copyright(c) 2006-2007, Ext JS, LLC.
5478  *
5479  * Originally Released Under LGPL - original licence link has changed is not relivant.
5480  *
5481  * Fork - LGPL
5482  * <script type="text/javascript">
5483  */
5484  
5485
5486 /**
5487  * @class Roo.grid.ColumnModel
5488  * @extends Roo.util.Observable
5489  * This is the default implementation of a ColumnModel used by the Grid. It defines
5490  * the columns in the grid.
5491  * <br>Usage:<br>
5492  <pre><code>
5493  var colModel = new Roo.grid.ColumnModel([
5494         {header: "Ticker", width: 60, sortable: true, locked: true},
5495         {header: "Company Name", width: 150, sortable: true},
5496         {header: "Market Cap.", width: 100, sortable: true},
5497         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5498         {header: "Employees", width: 100, sortable: true, resizable: false}
5499  ]);
5500  </code></pre>
5501  * <p>
5502  
5503  * The config options listed for this class are options which may appear in each
5504  * individual column definition.
5505  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5506  * @constructor
5507  * @param {Object} config An Array of column config objects. See this class's
5508  * config objects for details.
5509 */
5510 Roo.grid.ColumnModel = function(config){
5511         /**
5512      * The config passed into the constructor
5513      */
5514     this.config = config;
5515     this.lookup = {};
5516
5517     // if no id, create one
5518     // if the column does not have a dataIndex mapping,
5519     // map it to the order it is in the config
5520     for(var i = 0, len = config.length; i < len; i++){
5521         var c = config[i];
5522         if(typeof c.dataIndex == "undefined"){
5523             c.dataIndex = i;
5524         }
5525         if(typeof c.renderer == "string"){
5526             c.renderer = Roo.util.Format[c.renderer];
5527         }
5528         if(typeof c.id == "undefined"){
5529             c.id = Roo.id();
5530         }
5531         if(c.editor && c.editor.xtype){
5532             c.editor  = Roo.factory(c.editor, Roo.grid);
5533         }
5534         if(c.editor && c.editor.isFormField){
5535             c.editor = new Roo.grid.GridEditor(c.editor);
5536         }
5537         this.lookup[c.id] = c;
5538     }
5539
5540     /**
5541      * The width of columns which have no width specified (defaults to 100)
5542      * @type Number
5543      */
5544     this.defaultWidth = 100;
5545
5546     /**
5547      * Default sortable of columns which have no sortable specified (defaults to false)
5548      * @type Boolean
5549      */
5550     this.defaultSortable = false;
5551
5552     this.addEvents({
5553         /**
5554              * @event widthchange
5555              * Fires when the width of a column changes.
5556              * @param {ColumnModel} this
5557              * @param {Number} columnIndex The column index
5558              * @param {Number} newWidth The new width
5559              */
5560             "widthchange": true,
5561         /**
5562              * @event headerchange
5563              * Fires when the text of a header changes.
5564              * @param {ColumnModel} this
5565              * @param {Number} columnIndex The column index
5566              * @param {Number} newText The new header text
5567              */
5568             "headerchange": true,
5569         /**
5570              * @event hiddenchange
5571              * Fires when a column is hidden or "unhidden".
5572              * @param {ColumnModel} this
5573              * @param {Number} columnIndex The column index
5574              * @param {Boolean} hidden true if hidden, false otherwise
5575              */
5576             "hiddenchange": true,
5577             /**
5578          * @event columnmoved
5579          * Fires when a column is moved.
5580          * @param {ColumnModel} this
5581          * @param {Number} oldIndex
5582          * @param {Number} newIndex
5583          */
5584         "columnmoved" : true,
5585         /**
5586          * @event columlockchange
5587          * Fires when a column's locked state is changed
5588          * @param {ColumnModel} this
5589          * @param {Number} colIndex
5590          * @param {Boolean} locked true if locked
5591          */
5592         "columnlockchange" : true
5593     });
5594     Roo.grid.ColumnModel.superclass.constructor.call(this);
5595 };
5596 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5597     /**
5598      * @cfg {String} header The header text to display in the Grid view.
5599      */
5600     /**
5601      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5602      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5603      * specified, the column's index is used as an index into the Record's data Array.
5604      */
5605     /**
5606      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5607      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5608      */
5609     /**
5610      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5611      * Defaults to the value of the {@link #defaultSortable} property.
5612      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5613      */
5614     /**
5615      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5616      */
5617     /**
5618      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5619      */
5620     /**
5621      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5622      */
5623     /**
5624      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5625      */
5626     /**
5627      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5628      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5629      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5630      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5631      */
5632        /**
5633      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5634      */
5635     /**
5636      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5637      */
5638     /**
5639      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5640      */
5641     /**
5642      * @cfg {String} cursor (Optional)
5643      */
5644     /**
5645      * @cfg {String} tooltip (Optional)
5646      */
5647     /**
5648      * @cfg {Number} xs (Optional)
5649      */
5650     /**
5651      * @cfg {Number} sm (Optional)
5652      */
5653     /**
5654      * @cfg {Number} md (Optional)
5655      */
5656     /**
5657      * @cfg {Number} lg (Optional)
5658      */
5659     /**
5660      * Returns the id of the column at the specified index.
5661      * @param {Number} index The column index
5662      * @return {String} the id
5663      */
5664     getColumnId : function(index){
5665         return this.config[index].id;
5666     },
5667
5668     /**
5669      * Returns the column for a specified id.
5670      * @param {String} id The column id
5671      * @return {Object} the column
5672      */
5673     getColumnById : function(id){
5674         return this.lookup[id];
5675     },
5676
5677     
5678     /**
5679      * Returns the column for a specified dataIndex.
5680      * @param {String} dataIndex The column dataIndex
5681      * @return {Object|Boolean} the column or false if not found
5682      */
5683     getColumnByDataIndex: function(dataIndex){
5684         var index = this.findColumnIndex(dataIndex);
5685         return index > -1 ? this.config[index] : false;
5686     },
5687     
5688     /**
5689      * Returns the index for a specified column id.
5690      * @param {String} id The column id
5691      * @return {Number} the index, or -1 if not found
5692      */
5693     getIndexById : function(id){
5694         for(var i = 0, len = this.config.length; i < len; i++){
5695             if(this.config[i].id == id){
5696                 return i;
5697             }
5698         }
5699         return -1;
5700     },
5701     
5702     /**
5703      * Returns the index for a specified column dataIndex.
5704      * @param {String} dataIndex The column dataIndex
5705      * @return {Number} the index, or -1 if not found
5706      */
5707     
5708     findColumnIndex : function(dataIndex){
5709         for(var i = 0, len = this.config.length; i < len; i++){
5710             if(this.config[i].dataIndex == dataIndex){
5711                 return i;
5712             }
5713         }
5714         return -1;
5715     },
5716     
5717     
5718     moveColumn : function(oldIndex, newIndex){
5719         var c = this.config[oldIndex];
5720         this.config.splice(oldIndex, 1);
5721         this.config.splice(newIndex, 0, c);
5722         this.dataMap = null;
5723         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5724     },
5725
5726     isLocked : function(colIndex){
5727         return this.config[colIndex].locked === true;
5728     },
5729
5730     setLocked : function(colIndex, value, suppressEvent){
5731         if(this.isLocked(colIndex) == value){
5732             return;
5733         }
5734         this.config[colIndex].locked = value;
5735         if(!suppressEvent){
5736             this.fireEvent("columnlockchange", this, colIndex, value);
5737         }
5738     },
5739
5740     getTotalLockedWidth : function(){
5741         var totalWidth = 0;
5742         for(var i = 0; i < this.config.length; i++){
5743             if(this.isLocked(i) && !this.isHidden(i)){
5744                 this.totalWidth += this.getColumnWidth(i);
5745             }
5746         }
5747         return totalWidth;
5748     },
5749
5750     getLockedCount : function(){
5751         for(var i = 0, len = this.config.length; i < len; i++){
5752             if(!this.isLocked(i)){
5753                 return i;
5754             }
5755         }
5756         
5757         return this.config.length;
5758     },
5759
5760     /**
5761      * Returns the number of columns.
5762      * @return {Number}
5763      */
5764     getColumnCount : function(visibleOnly){
5765         if(visibleOnly === true){
5766             var c = 0;
5767             for(var i = 0, len = this.config.length; i < len; i++){
5768                 if(!this.isHidden(i)){
5769                     c++;
5770                 }
5771             }
5772             return c;
5773         }
5774         return this.config.length;
5775     },
5776
5777     /**
5778      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5779      * @param {Function} fn
5780      * @param {Object} scope (optional)
5781      * @return {Array} result
5782      */
5783     getColumnsBy : function(fn, scope){
5784         var r = [];
5785         for(var i = 0, len = this.config.length; i < len; i++){
5786             var c = this.config[i];
5787             if(fn.call(scope||this, c, i) === true){
5788                 r[r.length] = c;
5789             }
5790         }
5791         return r;
5792     },
5793
5794     /**
5795      * Returns true if the specified column is sortable.
5796      * @param {Number} col The column index
5797      * @return {Boolean}
5798      */
5799     isSortable : function(col){
5800         if(typeof this.config[col].sortable == "undefined"){
5801             return this.defaultSortable;
5802         }
5803         return this.config[col].sortable;
5804     },
5805
5806     /**
5807      * Returns the rendering (formatting) function defined for the column.
5808      * @param {Number} col The column index.
5809      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5810      */
5811     getRenderer : function(col){
5812         if(!this.config[col].renderer){
5813             return Roo.grid.ColumnModel.defaultRenderer;
5814         }
5815         return this.config[col].renderer;
5816     },
5817
5818     /**
5819      * Sets the rendering (formatting) function for a column.
5820      * @param {Number} col The column index
5821      * @param {Function} fn The function to use to process the cell's raw data
5822      * to return HTML markup for the grid view. The render function is called with
5823      * the following parameters:<ul>
5824      * <li>Data value.</li>
5825      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5826      * <li>css A CSS style string to apply to the table cell.</li>
5827      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5828      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5829      * <li>Row index</li>
5830      * <li>Column index</li>
5831      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5832      */
5833     setRenderer : function(col, fn){
5834         this.config[col].renderer = fn;
5835     },
5836
5837     /**
5838      * Returns the width for the specified column.
5839      * @param {Number} col The column index
5840      * @return {Number}
5841      */
5842     getColumnWidth : function(col){
5843         return this.config[col].width * 1 || this.defaultWidth;
5844     },
5845
5846     /**
5847      * Sets the width for a column.
5848      * @param {Number} col The column index
5849      * @param {Number} width The new width
5850      */
5851     setColumnWidth : function(col, width, suppressEvent){
5852         this.config[col].width = width;
5853         this.totalWidth = null;
5854         if(!suppressEvent){
5855              this.fireEvent("widthchange", this, col, width);
5856         }
5857     },
5858
5859     /**
5860      * Returns the total width of all columns.
5861      * @param {Boolean} includeHidden True to include hidden column widths
5862      * @return {Number}
5863      */
5864     getTotalWidth : function(includeHidden){
5865         if(!this.totalWidth){
5866             this.totalWidth = 0;
5867             for(var i = 0, len = this.config.length; i < len; i++){
5868                 if(includeHidden || !this.isHidden(i)){
5869                     this.totalWidth += this.getColumnWidth(i);
5870                 }
5871             }
5872         }
5873         return this.totalWidth;
5874     },
5875
5876     /**
5877      * Returns the header for the specified column.
5878      * @param {Number} col The column index
5879      * @return {String}
5880      */
5881     getColumnHeader : function(col){
5882         return this.config[col].header;
5883     },
5884
5885     /**
5886      * Sets the header for a column.
5887      * @param {Number} col The column index
5888      * @param {String} header The new header
5889      */
5890     setColumnHeader : function(col, header){
5891         this.config[col].header = header;
5892         this.fireEvent("headerchange", this, col, header);
5893     },
5894
5895     /**
5896      * Returns the tooltip for the specified column.
5897      * @param {Number} col The column index
5898      * @return {String}
5899      */
5900     getColumnTooltip : function(col){
5901             return this.config[col].tooltip;
5902     },
5903     /**
5904      * Sets the tooltip for a column.
5905      * @param {Number} col The column index
5906      * @param {String} tooltip The new tooltip
5907      */
5908     setColumnTooltip : function(col, tooltip){
5909             this.config[col].tooltip = tooltip;
5910     },
5911
5912     /**
5913      * Returns the dataIndex for the specified column.
5914      * @param {Number} col The column index
5915      * @return {Number}
5916      */
5917     getDataIndex : function(col){
5918         return this.config[col].dataIndex;
5919     },
5920
5921     /**
5922      * Sets the dataIndex for a column.
5923      * @param {Number} col The column index
5924      * @param {Number} dataIndex The new dataIndex
5925      */
5926     setDataIndex : function(col, dataIndex){
5927         this.config[col].dataIndex = dataIndex;
5928     },
5929
5930     
5931     
5932     /**
5933      * Returns true if the cell is editable.
5934      * @param {Number} colIndex The column index
5935      * @param {Number} rowIndex The row index - this is nto actually used..?
5936      * @return {Boolean}
5937      */
5938     isCellEditable : function(colIndex, rowIndex){
5939         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5940     },
5941
5942     /**
5943      * Returns the editor defined for the cell/column.
5944      * return false or null to disable editing.
5945      * @param {Number} colIndex The column index
5946      * @param {Number} rowIndex The row index
5947      * @return {Object}
5948      */
5949     getCellEditor : function(colIndex, rowIndex){
5950         return this.config[colIndex].editor;
5951     },
5952
5953     /**
5954      * Sets if a column is editable.
5955      * @param {Number} col The column index
5956      * @param {Boolean} editable True if the column is editable
5957      */
5958     setEditable : function(col, editable){
5959         this.config[col].editable = editable;
5960     },
5961
5962
5963     /**
5964      * Returns true if the column is hidden.
5965      * @param {Number} colIndex The column index
5966      * @return {Boolean}
5967      */
5968     isHidden : function(colIndex){
5969         return this.config[colIndex].hidden;
5970     },
5971
5972
5973     /**
5974      * Returns true if the column width cannot be changed
5975      */
5976     isFixed : function(colIndex){
5977         return this.config[colIndex].fixed;
5978     },
5979
5980     /**
5981      * Returns true if the column can be resized
5982      * @return {Boolean}
5983      */
5984     isResizable : function(colIndex){
5985         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5986     },
5987     /**
5988      * Sets if a column is hidden.
5989      * @param {Number} colIndex The column index
5990      * @param {Boolean} hidden True if the column is hidden
5991      */
5992     setHidden : function(colIndex, hidden){
5993         this.config[colIndex].hidden = hidden;
5994         this.totalWidth = null;
5995         this.fireEvent("hiddenchange", this, colIndex, hidden);
5996     },
5997
5998     /**
5999      * Sets the editor for a column.
6000      * @param {Number} col The column index
6001      * @param {Object} editor The editor object
6002      */
6003     setEditor : function(col, editor){
6004         this.config[col].editor = editor;
6005     }
6006 });
6007
6008 Roo.grid.ColumnModel.defaultRenderer = function(value)
6009 {
6010     if(typeof value == "object") {
6011         return value;
6012     }
6013         if(typeof value == "string" && value.length < 1){
6014             return "&#160;";
6015         }
6016     
6017         return String.format("{0}", value);
6018 };
6019
6020 // Alias for backwards compatibility
6021 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6022 /*
6023  * Based on:
6024  * Ext JS Library 1.1.1
6025  * Copyright(c) 2006-2007, Ext JS, LLC.
6026  *
6027  * Originally Released Under LGPL - original licence link has changed is not relivant.
6028  *
6029  * Fork - LGPL
6030  * <script type="text/javascript">
6031  */
6032  
6033 /**
6034  * @class Roo.LoadMask
6035  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6036  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6037  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6038  * element's UpdateManager load indicator and will be destroyed after the initial load.
6039  * @constructor
6040  * Create a new LoadMask
6041  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6042  * @param {Object} config The config object
6043  */
6044 Roo.LoadMask = function(el, config){
6045     this.el = Roo.get(el);
6046     Roo.apply(this, config);
6047     if(this.store){
6048         this.store.on('beforeload', this.onBeforeLoad, this);
6049         this.store.on('load', this.onLoad, this);
6050         this.store.on('loadexception', this.onLoadException, this);
6051         this.removeMask = false;
6052     }else{
6053         var um = this.el.getUpdateManager();
6054         um.showLoadIndicator = false; // disable the default indicator
6055         um.on('beforeupdate', this.onBeforeLoad, this);
6056         um.on('update', this.onLoad, this);
6057         um.on('failure', this.onLoad, this);
6058         this.removeMask = true;
6059     }
6060 };
6061
6062 Roo.LoadMask.prototype = {
6063     /**
6064      * @cfg {Boolean} removeMask
6065      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6066      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6067      */
6068     /**
6069      * @cfg {String} msg
6070      * The text to display in a centered loading message box (defaults to 'Loading...')
6071      */
6072     msg : 'Loading...',
6073     /**
6074      * @cfg {String} msgCls
6075      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6076      */
6077     msgCls : 'x-mask-loading',
6078
6079     /**
6080      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6081      * @type Boolean
6082      */
6083     disabled: false,
6084
6085     /**
6086      * Disables the mask to prevent it from being displayed
6087      */
6088     disable : function(){
6089        this.disabled = true;
6090     },
6091
6092     /**
6093      * Enables the mask so that it can be displayed
6094      */
6095     enable : function(){
6096         this.disabled = false;
6097     },
6098     
6099     onLoadException : function()
6100     {
6101         Roo.log(arguments);
6102         
6103         if (typeof(arguments[3]) != 'undefined') {
6104             Roo.MessageBox.alert("Error loading",arguments[3]);
6105         } 
6106         /*
6107         try {
6108             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6109                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6110             }   
6111         } catch(e) {
6112             
6113         }
6114         */
6115     
6116         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6117     },
6118     // private
6119     onLoad : function()
6120     {
6121         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6122     },
6123
6124     // private
6125     onBeforeLoad : function(){
6126         if(!this.disabled){
6127             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6128         }
6129     },
6130
6131     // private
6132     destroy : function(){
6133         if(this.store){
6134             this.store.un('beforeload', this.onBeforeLoad, this);
6135             this.store.un('load', this.onLoad, this);
6136             this.store.un('loadexception', this.onLoadException, this);
6137         }else{
6138             var um = this.el.getUpdateManager();
6139             um.un('beforeupdate', this.onBeforeLoad, this);
6140             um.un('update', this.onLoad, this);
6141             um.un('failure', this.onLoad, this);
6142         }
6143     }
6144 };/*
6145  * - LGPL
6146  *
6147  * table
6148  * 
6149  */
6150
6151 /**
6152  * @class Roo.bootstrap.Table
6153  * @extends Roo.bootstrap.Component
6154  * Bootstrap Table class
6155  * @cfg {String} cls table class
6156  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6157  * @cfg {String} bgcolor Specifies the background color for a table
6158  * @cfg {Number} border Specifies whether the table cells should have borders or not
6159  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6160  * @cfg {Number} cellspacing Specifies the space between cells
6161  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6162  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6163  * @cfg {String} sortable Specifies that the table should be sortable
6164  * @cfg {String} summary Specifies a summary of the content of a table
6165  * @cfg {Number} width Specifies the width of a table
6166  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6167  * 
6168  * @cfg {boolean} striped Should the rows be alternative striped
6169  * @cfg {boolean} bordered Add borders to the table
6170  * @cfg {boolean} hover Add hover highlighting
6171  * @cfg {boolean} condensed Format condensed
6172  * @cfg {boolean} responsive Format condensed
6173  * @cfg {Boolean} loadMask (true|false) default false
6174  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6175  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6176  * @cfg {Boolean} rowSelection (true|false) default false
6177  * @cfg {Boolean} cellSelection (true|false) default false
6178  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6179  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6180  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6181  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6182  
6183  * 
6184  * @constructor
6185  * Create a new Table
6186  * @param {Object} config The config object
6187  */
6188
6189 Roo.bootstrap.Table = function(config){
6190     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6191     
6192   
6193     
6194     // BC...
6195     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6196     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6197     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6198     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6199     
6200     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6201     if (this.sm) {
6202         this.sm.grid = this;
6203         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6204         this.sm = this.selModel;
6205         this.sm.xmodule = this.xmodule || false;
6206     }
6207     
6208     if (this.cm && typeof(this.cm.config) == 'undefined') {
6209         this.colModel = new Roo.grid.ColumnModel(this.cm);
6210         this.cm = this.colModel;
6211         this.cm.xmodule = this.xmodule || false;
6212     }
6213     if (this.store) {
6214         this.store= Roo.factory(this.store, Roo.data);
6215         this.ds = this.store;
6216         this.ds.xmodule = this.xmodule || false;
6217          
6218     }
6219     if (this.footer && this.store) {
6220         this.footer.dataSource = this.ds;
6221         this.footer = Roo.factory(this.footer);
6222     }
6223     
6224     /** @private */
6225     this.addEvents({
6226         /**
6227          * @event cellclick
6228          * Fires when a cell is clicked
6229          * @param {Roo.bootstrap.Table} this
6230          * @param {Roo.Element} el
6231          * @param {Number} rowIndex
6232          * @param {Number} columnIndex
6233          * @param {Roo.EventObject} e
6234          */
6235         "cellclick" : true,
6236         /**
6237          * @event celldblclick
6238          * Fires when a cell is double clicked
6239          * @param {Roo.bootstrap.Table} this
6240          * @param {Roo.Element} el
6241          * @param {Number} rowIndex
6242          * @param {Number} columnIndex
6243          * @param {Roo.EventObject} e
6244          */
6245         "celldblclick" : true,
6246         /**
6247          * @event rowclick
6248          * Fires when a row is clicked
6249          * @param {Roo.bootstrap.Table} this
6250          * @param {Roo.Element} el
6251          * @param {Number} rowIndex
6252          * @param {Roo.EventObject} e
6253          */
6254         "rowclick" : true,
6255         /**
6256          * @event rowdblclick
6257          * Fires when a row is double clicked
6258          * @param {Roo.bootstrap.Table} this
6259          * @param {Roo.Element} el
6260          * @param {Number} rowIndex
6261          * @param {Roo.EventObject} e
6262          */
6263         "rowdblclick" : true,
6264         /**
6265          * @event mouseover
6266          * Fires when a mouseover occur
6267          * @param {Roo.bootstrap.Table} this
6268          * @param {Roo.Element} el
6269          * @param {Number} rowIndex
6270          * @param {Number} columnIndex
6271          * @param {Roo.EventObject} e
6272          */
6273         "mouseover" : true,
6274         /**
6275          * @event mouseout
6276          * Fires when a mouseout occur
6277          * @param {Roo.bootstrap.Table} this
6278          * @param {Roo.Element} el
6279          * @param {Number} rowIndex
6280          * @param {Number} columnIndex
6281          * @param {Roo.EventObject} e
6282          */
6283         "mouseout" : true,
6284         /**
6285          * @event rowclass
6286          * Fires when a row is rendered, so you can change add a style to it.
6287          * @param {Roo.bootstrap.Table} this
6288          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6289          */
6290         'rowclass' : true,
6291           /**
6292          * @event rowsrendered
6293          * Fires when all the  rows have been rendered
6294          * @param {Roo.bootstrap.Table} this
6295          */
6296         'rowsrendered' : true,
6297         /**
6298          * @event contextmenu
6299          * The raw contextmenu event for the entire grid.
6300          * @param {Roo.EventObject} e
6301          */
6302         "contextmenu" : true,
6303         /**
6304          * @event rowcontextmenu
6305          * Fires when a row is right clicked
6306          * @param {Roo.bootstrap.Table} this
6307          * @param {Number} rowIndex
6308          * @param {Roo.EventObject} e
6309          */
6310         "rowcontextmenu" : true,
6311         /**
6312          * @event cellcontextmenu
6313          * Fires when a cell is right clicked
6314          * @param {Roo.bootstrap.Table} this
6315          * @param {Number} rowIndex
6316          * @param {Number} cellIndex
6317          * @param {Roo.EventObject} e
6318          */
6319          "cellcontextmenu" : true,
6320          /**
6321          * @event headercontextmenu
6322          * Fires when a header is right clicked
6323          * @param {Roo.bootstrap.Table} this
6324          * @param {Number} columnIndex
6325          * @param {Roo.EventObject} e
6326          */
6327         "headercontextmenu" : true
6328     });
6329 };
6330
6331 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6332     
6333     cls: false,
6334     align: false,
6335     bgcolor: false,
6336     border: false,
6337     cellpadding: false,
6338     cellspacing: false,
6339     frame: false,
6340     rules: false,
6341     sortable: false,
6342     summary: false,
6343     width: false,
6344     striped : false,
6345     scrollBody : false,
6346     bordered: false,
6347     hover:  false,
6348     condensed : false,
6349     responsive : false,
6350     sm : false,
6351     cm : false,
6352     store : false,
6353     loadMask : false,
6354     footerShow : true,
6355     headerShow : true,
6356   
6357     rowSelection : false,
6358     cellSelection : false,
6359     layout : false,
6360     
6361     // Roo.Element - the tbody
6362     mainBody: false,
6363     // Roo.Element - thead element
6364     mainHead: false,
6365     
6366     container: false, // used by gridpanel...
6367     
6368     lazyLoad : false,
6369     
6370     CSS : Roo.util.CSS,
6371     
6372     auto_hide_footer : false,
6373     
6374     getAutoCreate : function()
6375     {
6376         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6377         
6378         cfg = {
6379             tag: 'table',
6380             cls : 'table',
6381             cn : []
6382         };
6383         if (this.scrollBody) {
6384             cfg.cls += ' table-body-fixed';
6385         }    
6386         if (this.striped) {
6387             cfg.cls += ' table-striped';
6388         }
6389         
6390         if (this.hover) {
6391             cfg.cls += ' table-hover';
6392         }
6393         if (this.bordered) {
6394             cfg.cls += ' table-bordered';
6395         }
6396         if (this.condensed) {
6397             cfg.cls += ' table-condensed';
6398         }
6399         if (this.responsive) {
6400             cfg.cls += ' table-responsive';
6401         }
6402         
6403         if (this.cls) {
6404             cfg.cls+=  ' ' +this.cls;
6405         }
6406         
6407         // this lot should be simplifed...
6408         var _t = this;
6409         var cp = [
6410             'align',
6411             'bgcolor',
6412             'border',
6413             'cellpadding',
6414             'cellspacing',
6415             'frame',
6416             'rules',
6417             'sortable',
6418             'summary',
6419             'width'
6420         ].forEach(function(k) {
6421             if (_t[k]) {
6422                 cfg[k] = _t[k];
6423             }
6424         });
6425         
6426         
6427         if (this.layout) {
6428             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6429         }
6430         
6431         if(this.store || this.cm){
6432             if(this.headerShow){
6433                 cfg.cn.push(this.renderHeader());
6434             }
6435             
6436             cfg.cn.push(this.renderBody());
6437             
6438             if(this.footerShow){
6439                 cfg.cn.push(this.renderFooter());
6440             }
6441             // where does this come from?
6442             //cfg.cls+=  ' TableGrid';
6443         }
6444         
6445         return { cn : [ cfg ] };
6446     },
6447     
6448     initEvents : function()
6449     {   
6450         if(!this.store || !this.cm){
6451             return;
6452         }
6453         if (this.selModel) {
6454             this.selModel.initEvents();
6455         }
6456         
6457         
6458         //Roo.log('initEvents with ds!!!!');
6459         
6460         this.mainBody = this.el.select('tbody', true).first();
6461         this.mainHead = this.el.select('thead', true).first();
6462         this.mainFoot = this.el.select('tfoot', true).first();
6463         
6464         
6465         
6466         var _this = this;
6467         
6468         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6469             e.on('click', _this.sort, _this);
6470         });
6471         
6472         this.mainBody.on("click", this.onClick, this);
6473         this.mainBody.on("dblclick", this.onDblClick, this);
6474         
6475         // why is this done????? = it breaks dialogs??
6476         //this.parent().el.setStyle('position', 'relative');
6477         
6478         
6479         if (this.footer) {
6480             this.footer.parentId = this.id;
6481             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6482             
6483             if(this.lazyLoad){
6484                 this.el.select('tfoot tr td').first().addClass('hide');
6485             }
6486         } 
6487         
6488         if(this.loadMask) {
6489             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6490         }
6491         
6492         this.store.on('load', this.onLoad, this);
6493         this.store.on('beforeload', this.onBeforeLoad, this);
6494         this.store.on('update', this.onUpdate, this);
6495         this.store.on('add', this.onAdd, this);
6496         this.store.on("clear", this.clear, this);
6497         
6498         this.el.on("contextmenu", this.onContextMenu, this);
6499         
6500         this.mainBody.on('scroll', this.onBodyScroll, this);
6501         
6502         this.cm.on("headerchange", this.onHeaderChange, this);
6503         
6504         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6505         
6506     },
6507     
6508     onContextMenu : function(e, t)
6509     {
6510         this.processEvent("contextmenu", e);
6511     },
6512     
6513     processEvent : function(name, e)
6514     {
6515         if (name != 'touchstart' ) {
6516             this.fireEvent(name, e);    
6517         }
6518         
6519         var t = e.getTarget();
6520         
6521         var cell = Roo.get(t);
6522         
6523         if(!cell){
6524             return;
6525         }
6526         
6527         if(cell.findParent('tfoot', false, true)){
6528             return;
6529         }
6530         
6531         if(cell.findParent('thead', false, true)){
6532             
6533             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6534                 cell = Roo.get(t).findParent('th', false, true);
6535                 if (!cell) {
6536                     Roo.log("failed to find th in thead?");
6537                     Roo.log(e.getTarget());
6538                     return;
6539                 }
6540             }
6541             
6542             var cellIndex = cell.dom.cellIndex;
6543             
6544             var ename = name == 'touchstart' ? 'click' : name;
6545             this.fireEvent("header" + ename, this, cellIndex, e);
6546             
6547             return;
6548         }
6549         
6550         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6551             cell = Roo.get(t).findParent('td', false, true);
6552             if (!cell) {
6553                 Roo.log("failed to find th in tbody?");
6554                 Roo.log(e.getTarget());
6555                 return;
6556             }
6557         }
6558         
6559         var row = cell.findParent('tr', false, true);
6560         var cellIndex = cell.dom.cellIndex;
6561         var rowIndex = row.dom.rowIndex - 1;
6562         
6563         if(row !== false){
6564             
6565             this.fireEvent("row" + name, this, rowIndex, e);
6566             
6567             if(cell !== false){
6568             
6569                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6570             }
6571         }
6572         
6573     },
6574     
6575     onMouseover : function(e, el)
6576     {
6577         var cell = Roo.get(el);
6578         
6579         if(!cell){
6580             return;
6581         }
6582         
6583         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6584             cell = cell.findParent('td', false, true);
6585         }
6586         
6587         var row = cell.findParent('tr', false, true);
6588         var cellIndex = cell.dom.cellIndex;
6589         var rowIndex = row.dom.rowIndex - 1; // start from 0
6590         
6591         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6592         
6593     },
6594     
6595     onMouseout : function(e, el)
6596     {
6597         var cell = Roo.get(el);
6598         
6599         if(!cell){
6600             return;
6601         }
6602         
6603         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6604             cell = cell.findParent('td', false, true);
6605         }
6606         
6607         var row = cell.findParent('tr', false, true);
6608         var cellIndex = cell.dom.cellIndex;
6609         var rowIndex = row.dom.rowIndex - 1; // start from 0
6610         
6611         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6612         
6613     },
6614     
6615     onClick : function(e, el)
6616     {
6617         var cell = Roo.get(el);
6618         
6619         if(!cell || (!this.cellSelection && !this.rowSelection)){
6620             return;
6621         }
6622         
6623         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6624             cell = cell.findParent('td', false, true);
6625         }
6626         
6627         if(!cell || typeof(cell) == 'undefined'){
6628             return;
6629         }
6630         
6631         var row = cell.findParent('tr', false, true);
6632         
6633         if(!row || typeof(row) == 'undefined'){
6634             return;
6635         }
6636         
6637         var cellIndex = cell.dom.cellIndex;
6638         var rowIndex = this.getRowIndex(row);
6639         
6640         // why??? - should these not be based on SelectionModel?
6641         if(this.cellSelection){
6642             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6643         }
6644         
6645         if(this.rowSelection){
6646             this.fireEvent('rowclick', this, row, rowIndex, e);
6647         }
6648         
6649         
6650     },
6651         
6652     onDblClick : function(e,el)
6653     {
6654         var cell = Roo.get(el);
6655         
6656         if(!cell || (!this.cellSelection && !this.rowSelection)){
6657             return;
6658         }
6659         
6660         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6661             cell = cell.findParent('td', false, true);
6662         }
6663         
6664         if(!cell || typeof(cell) == 'undefined'){
6665             return;
6666         }
6667         
6668         var row = cell.findParent('tr', false, true);
6669         
6670         if(!row || typeof(row) == 'undefined'){
6671             return;
6672         }
6673         
6674         var cellIndex = cell.dom.cellIndex;
6675         var rowIndex = this.getRowIndex(row);
6676         
6677         if(this.cellSelection){
6678             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6679         }
6680         
6681         if(this.rowSelection){
6682             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6683         }
6684     },
6685     
6686     sort : function(e,el)
6687     {
6688         var col = Roo.get(el);
6689         
6690         if(!col.hasClass('sortable')){
6691             return;
6692         }
6693         
6694         var sort = col.attr('sort');
6695         var dir = 'ASC';
6696         
6697         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6698             dir = 'DESC';
6699         }
6700         
6701         this.store.sortInfo = {field : sort, direction : dir};
6702         
6703         if (this.footer) {
6704             Roo.log("calling footer first");
6705             this.footer.onClick('first');
6706         } else {
6707         
6708             this.store.load({ params : { start : 0 } });
6709         }
6710     },
6711     
6712     renderHeader : function()
6713     {
6714         var header = {
6715             tag: 'thead',
6716             cn : []
6717         };
6718         
6719         var cm = this.cm;
6720         this.totalWidth = 0;
6721         
6722         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6723             
6724             var config = cm.config[i];
6725             
6726             var c = {
6727                 tag: 'th',
6728                 cls : 'x-hcol-' + i,
6729                 style : '',
6730                 html: cm.getColumnHeader(i)
6731             };
6732             
6733             var hh = '';
6734             
6735             if(typeof(config.sortable) != 'undefined' && config.sortable){
6736                 c.cls = 'sortable';
6737                 c.html = '<i class="glyphicon"></i>' + c.html;
6738             }
6739             
6740             if(typeof(config.lgHeader) != 'undefined'){
6741                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6742             }
6743             
6744             if(typeof(config.mdHeader) != 'undefined'){
6745                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6746             }
6747             
6748             if(typeof(config.smHeader) != 'undefined'){
6749                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6750             }
6751             
6752             if(typeof(config.xsHeader) != 'undefined'){
6753                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6754             }
6755             
6756             if(hh.length){
6757                 c.html = hh;
6758             }
6759             
6760             if(typeof(config.tooltip) != 'undefined'){
6761                 c.tooltip = config.tooltip;
6762             }
6763             
6764             if(typeof(config.colspan) != 'undefined'){
6765                 c.colspan = config.colspan;
6766             }
6767             
6768             if(typeof(config.hidden) != 'undefined' && config.hidden){
6769                 c.style += ' display:none;';
6770             }
6771             
6772             if(typeof(config.dataIndex) != 'undefined'){
6773                 c.sort = config.dataIndex;
6774             }
6775             
6776            
6777             
6778             if(typeof(config.align) != 'undefined' && config.align.length){
6779                 c.style += ' text-align:' + config.align + ';';
6780             }
6781             
6782             if(typeof(config.width) != 'undefined'){
6783                 c.style += ' width:' + config.width + 'px;';
6784                 this.totalWidth += config.width;
6785             } else {
6786                 this.totalWidth += 100; // assume minimum of 100 per column?
6787             }
6788             
6789             if(typeof(config.cls) != 'undefined'){
6790                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6791             }
6792             
6793             ['xs','sm','md','lg'].map(function(size){
6794                 
6795                 if(typeof(config[size]) == 'undefined'){
6796                     return;
6797                 }
6798                 
6799                 if (!config[size]) { // 0 = hidden
6800                     c.cls += ' hidden-' + size;
6801                     return;
6802                 }
6803                 
6804                 c.cls += ' col-' + size + '-' + config[size];
6805
6806             });
6807             
6808             header.cn.push(c)
6809         }
6810         
6811         return header;
6812     },
6813     
6814     renderBody : function()
6815     {
6816         var body = {
6817             tag: 'tbody',
6818             cn : [
6819                 {
6820                     tag: 'tr',
6821                     cn : [
6822                         {
6823                             tag : 'td',
6824                             colspan :  this.cm.getColumnCount()
6825                         }
6826                     ]
6827                 }
6828             ]
6829         };
6830         
6831         return body;
6832     },
6833     
6834     renderFooter : function()
6835     {
6836         var footer = {
6837             tag: 'tfoot',
6838             cn : [
6839                 {
6840                     tag: 'tr',
6841                     cn : [
6842                         {
6843                             tag : 'td',
6844                             colspan :  this.cm.getColumnCount()
6845                         }
6846                     ]
6847                 }
6848             ]
6849         };
6850         
6851         return footer;
6852     },
6853     
6854     
6855     
6856     onLoad : function()
6857     {
6858 //        Roo.log('ds onload');
6859         this.clear();
6860         
6861         var _this = this;
6862         var cm = this.cm;
6863         var ds = this.store;
6864         
6865         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6866             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6867             if (_this.store.sortInfo) {
6868                     
6869                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6870                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6871                 }
6872                 
6873                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6874                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6875                 }
6876             }
6877         });
6878         
6879         var tbody =  this.mainBody;
6880               
6881         if(ds.getCount() > 0){
6882             ds.data.each(function(d,rowIndex){
6883                 var row =  this.renderRow(cm, ds, rowIndex);
6884                 
6885                 tbody.createChild(row);
6886                 
6887                 var _this = this;
6888                 
6889                 if(row.cellObjects.length){
6890                     Roo.each(row.cellObjects, function(r){
6891                         _this.renderCellObject(r);
6892                     })
6893                 }
6894                 
6895             }, this);
6896         }
6897         
6898         var tfoot = this.el.select('tfoot', true).first();
6899         
6900         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6901             
6902             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6903             
6904             var total = this.ds.getTotalCount();
6905             
6906             if(this.footer.pageSize < total){
6907                 this.mainFoot.show();
6908             }
6909         }
6910         
6911         Roo.each(this.el.select('tbody td', true).elements, function(e){
6912             e.on('mouseover', _this.onMouseover, _this);
6913         });
6914         
6915         Roo.each(this.el.select('tbody td', true).elements, function(e){
6916             e.on('mouseout', _this.onMouseout, _this);
6917         });
6918         this.fireEvent('rowsrendered', this);
6919         
6920         this.autoSize();
6921     },
6922     
6923     
6924     onUpdate : function(ds,record)
6925     {
6926         this.refreshRow(record);
6927         this.autoSize();
6928     },
6929     
6930     onRemove : function(ds, record, index, isUpdate){
6931         if(isUpdate !== true){
6932             this.fireEvent("beforerowremoved", this, index, record);
6933         }
6934         var bt = this.mainBody.dom;
6935         
6936         var rows = this.el.select('tbody > tr', true).elements;
6937         
6938         if(typeof(rows[index]) != 'undefined'){
6939             bt.removeChild(rows[index].dom);
6940         }
6941         
6942 //        if(bt.rows[index]){
6943 //            bt.removeChild(bt.rows[index]);
6944 //        }
6945         
6946         if(isUpdate !== true){
6947             //this.stripeRows(index);
6948             //this.syncRowHeights(index, index);
6949             //this.layout();
6950             this.fireEvent("rowremoved", this, index, record);
6951         }
6952     },
6953     
6954     onAdd : function(ds, records, rowIndex)
6955     {
6956         //Roo.log('on Add called');
6957         // - note this does not handle multiple adding very well..
6958         var bt = this.mainBody.dom;
6959         for (var i =0 ; i < records.length;i++) {
6960             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6961             //Roo.log(records[i]);
6962             //Roo.log(this.store.getAt(rowIndex+i));
6963             this.insertRow(this.store, rowIndex + i, false);
6964             return;
6965         }
6966         
6967     },
6968     
6969     
6970     refreshRow : function(record){
6971         var ds = this.store, index;
6972         if(typeof record == 'number'){
6973             index = record;
6974             record = ds.getAt(index);
6975         }else{
6976             index = ds.indexOf(record);
6977         }
6978         this.insertRow(ds, index, true);
6979         this.autoSize();
6980         this.onRemove(ds, record, index+1, true);
6981         this.autoSize();
6982         //this.syncRowHeights(index, index);
6983         //this.layout();
6984         this.fireEvent("rowupdated", this, index, record);
6985     },
6986     
6987     insertRow : function(dm, rowIndex, isUpdate){
6988         
6989         if(!isUpdate){
6990             this.fireEvent("beforerowsinserted", this, rowIndex);
6991         }
6992             //var s = this.getScrollState();
6993         var row = this.renderRow(this.cm, this.store, rowIndex);
6994         // insert before rowIndex..
6995         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6996         
6997         var _this = this;
6998                 
6999         if(row.cellObjects.length){
7000             Roo.each(row.cellObjects, function(r){
7001                 _this.renderCellObject(r);
7002             })
7003         }
7004             
7005         if(!isUpdate){
7006             this.fireEvent("rowsinserted", this, rowIndex);
7007             //this.syncRowHeights(firstRow, lastRow);
7008             //this.stripeRows(firstRow);
7009             //this.layout();
7010         }
7011         
7012     },
7013     
7014     
7015     getRowDom : function(rowIndex)
7016     {
7017         var rows = this.el.select('tbody > tr', true).elements;
7018         
7019         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7020         
7021     },
7022     // returns the object tree for a tr..
7023   
7024     
7025     renderRow : function(cm, ds, rowIndex) 
7026     {
7027         var d = ds.getAt(rowIndex);
7028         
7029         var row = {
7030             tag : 'tr',
7031             cls : 'x-row-' + rowIndex,
7032             cn : []
7033         };
7034             
7035         var cellObjects = [];
7036         
7037         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7038             var config = cm.config[i];
7039             
7040             var renderer = cm.getRenderer(i);
7041             var value = '';
7042             var id = false;
7043             
7044             if(typeof(renderer) !== 'undefined'){
7045                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7046             }
7047             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7048             // and are rendered into the cells after the row is rendered - using the id for the element.
7049             
7050             if(typeof(value) === 'object'){
7051                 id = Roo.id();
7052                 cellObjects.push({
7053                     container : id,
7054                     cfg : value 
7055                 })
7056             }
7057             
7058             var rowcfg = {
7059                 record: d,
7060                 rowIndex : rowIndex,
7061                 colIndex : i,
7062                 rowClass : ''
7063             };
7064
7065             this.fireEvent('rowclass', this, rowcfg);
7066             
7067             var td = {
7068                 tag: 'td',
7069                 cls : rowcfg.rowClass + ' x-col-' + i,
7070                 style: '',
7071                 html: (typeof(value) === 'object') ? '' : value
7072             };
7073             
7074             if (id) {
7075                 td.id = id;
7076             }
7077             
7078             if(typeof(config.colspan) != 'undefined'){
7079                 td.colspan = config.colspan;
7080             }
7081             
7082             if(typeof(config.hidden) != 'undefined' && config.hidden){
7083                 td.style += ' display:none;';
7084             }
7085             
7086             if(typeof(config.align) != 'undefined' && config.align.length){
7087                 td.style += ' text-align:' + config.align + ';';
7088             }
7089             if(typeof(config.valign) != 'undefined' && config.valign.length){
7090                 td.style += ' vertical-align:' + config.valign + ';';
7091             }
7092             
7093             if(typeof(config.width) != 'undefined'){
7094                 td.style += ' width:' +  config.width + 'px;';
7095             }
7096             
7097             if(typeof(config.cursor) != 'undefined'){
7098                 td.style += ' cursor:' +  config.cursor + ';';
7099             }
7100             
7101             if(typeof(config.cls) != 'undefined'){
7102                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7103             }
7104             
7105             ['xs','sm','md','lg'].map(function(size){
7106                 
7107                 if(typeof(config[size]) == 'undefined'){
7108                     return;
7109                 }
7110                 
7111                 if (!config[size]) { // 0 = hidden
7112                     td.cls += ' hidden-' + size;
7113                     return;
7114                 }
7115                 
7116                 td.cls += ' col-' + size + '-' + config[size];
7117
7118             });
7119             
7120             row.cn.push(td);
7121            
7122         }
7123         
7124         row.cellObjects = cellObjects;
7125         
7126         return row;
7127           
7128     },
7129     
7130     
7131     
7132     onBeforeLoad : function()
7133     {
7134         
7135     },
7136      /**
7137      * Remove all rows
7138      */
7139     clear : function()
7140     {
7141         this.el.select('tbody', true).first().dom.innerHTML = '';
7142     },
7143     /**
7144      * Show or hide a row.
7145      * @param {Number} rowIndex to show or hide
7146      * @param {Boolean} state hide
7147      */
7148     setRowVisibility : function(rowIndex, state)
7149     {
7150         var bt = this.mainBody.dom;
7151         
7152         var rows = this.el.select('tbody > tr', true).elements;
7153         
7154         if(typeof(rows[rowIndex]) == 'undefined'){
7155             return;
7156         }
7157         rows[rowIndex].dom.style.display = state ? '' : 'none';
7158     },
7159     
7160     
7161     getSelectionModel : function(){
7162         if(!this.selModel){
7163             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7164         }
7165         return this.selModel;
7166     },
7167     /*
7168      * Render the Roo.bootstrap object from renderder
7169      */
7170     renderCellObject : function(r)
7171     {
7172         var _this = this;
7173         
7174         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7175         
7176         var t = r.cfg.render(r.container);
7177         
7178         if(r.cfg.cn){
7179             Roo.each(r.cfg.cn, function(c){
7180                 var child = {
7181                     container: t.getChildContainer(),
7182                     cfg: c
7183                 };
7184                 _this.renderCellObject(child);
7185             })
7186         }
7187     },
7188     
7189     getRowIndex : function(row)
7190     {
7191         var rowIndex = -1;
7192         
7193         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7194             if(el != row){
7195                 return;
7196             }
7197             
7198             rowIndex = index;
7199         });
7200         
7201         return rowIndex;
7202     },
7203      /**
7204      * Returns the grid's underlying element = used by panel.Grid
7205      * @return {Element} The element
7206      */
7207     getGridEl : function(){
7208         return this.el;
7209     },
7210      /**
7211      * Forces a resize - used by panel.Grid
7212      * @return {Element} The element
7213      */
7214     autoSize : function()
7215     {
7216         //var ctr = Roo.get(this.container.dom.parentElement);
7217         var ctr = Roo.get(this.el.dom);
7218         
7219         var thd = this.getGridEl().select('thead',true).first();
7220         var tbd = this.getGridEl().select('tbody', true).first();
7221         var tfd = this.getGridEl().select('tfoot', true).first();
7222         
7223         var cw = ctr.getWidth();
7224         
7225         if (tbd) {
7226             
7227             tbd.setSize(ctr.getWidth(),
7228                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7229             );
7230             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7231             cw -= barsize;
7232         }
7233         cw = Math.max(cw, this.totalWidth);
7234         this.getGridEl().select('tr',true).setWidth(cw);
7235         // resize 'expandable coloumn?
7236         
7237         return; // we doe not have a view in this design..
7238         
7239     },
7240     onBodyScroll: function()
7241     {
7242         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7243         if(this.mainHead){
7244             this.mainHead.setStyle({
7245                 'position' : 'relative',
7246                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7247             });
7248         }
7249         
7250         if(this.lazyLoad){
7251             
7252             var scrollHeight = this.mainBody.dom.scrollHeight;
7253             
7254             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7255             
7256             var height = this.mainBody.getHeight();
7257             
7258             if(scrollHeight - height == scrollTop) {
7259                 
7260                 var total = this.ds.getTotalCount();
7261                 
7262                 if(this.footer.cursor + this.footer.pageSize < total){
7263                     
7264                     this.footer.ds.load({
7265                         params : {
7266                             start : this.footer.cursor + this.footer.pageSize,
7267                             limit : this.footer.pageSize
7268                         },
7269                         add : true
7270                     });
7271                 }
7272             }
7273             
7274         }
7275     },
7276     
7277     onHeaderChange : function()
7278     {
7279         var header = this.renderHeader();
7280         var table = this.el.select('table', true).first();
7281         
7282         this.mainHead.remove();
7283         this.mainHead = table.createChild(header, this.mainBody, false);
7284     },
7285     
7286     onHiddenChange : function(colModel, colIndex, hidden)
7287     {
7288         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7289         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7290         
7291         this.CSS.updateRule(thSelector, "display", "");
7292         this.CSS.updateRule(tdSelector, "display", "");
7293         
7294         if(hidden){
7295             this.CSS.updateRule(thSelector, "display", "none");
7296             this.CSS.updateRule(tdSelector, "display", "none");
7297         }
7298         
7299         this.onHeaderChange();
7300         this.onLoad();
7301     },
7302     
7303     setColumnWidth: function(col_index, width)
7304     {
7305         // width = "md-2 xs-2..."
7306         if(!this.colModel.config[col_index]) {
7307             return;
7308         }
7309         
7310         var w = width.split(" ");
7311         
7312         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7313         
7314         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7315         
7316         
7317         for(var j = 0; j < w.length; j++) {
7318             
7319             if(!w[j]) {
7320                 continue;
7321             }
7322             
7323             var size_cls = w[j].split("-");
7324             
7325             if(!Number.isInteger(size_cls[1] * 1)) {
7326                 continue;
7327             }
7328             
7329             if(!this.colModel.config[col_index][size_cls[0]]) {
7330                 continue;
7331             }
7332             
7333             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7334                 continue;
7335             }
7336             
7337             h_row[0].classList.replace(
7338                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7339                 "col-"+size_cls[0]+"-"+size_cls[1]
7340             );
7341             
7342             for(var i = 0; i < rows.length; i++) {
7343                 
7344                 var size_cls = w[j].split("-");
7345                 
7346                 if(!Number.isInteger(size_cls[1] * 1)) {
7347                     continue;
7348                 }
7349                 
7350                 if(!this.colModel.config[col_index][size_cls[0]]) {
7351                     continue;
7352                 }
7353                 
7354                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7355                     continue;
7356                 }
7357                 
7358                 rows[i].classList.replace(
7359                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7360                     "col-"+size_cls[0]+"-"+size_cls[1]
7361                 );
7362             }
7363             
7364             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7365         }
7366     }
7367 });
7368
7369  
7370
7371  /*
7372  * - LGPL
7373  *
7374  * table cell
7375  * 
7376  */
7377
7378 /**
7379  * @class Roo.bootstrap.TableCell
7380  * @extends Roo.bootstrap.Component
7381  * Bootstrap TableCell class
7382  * @cfg {String} html cell contain text
7383  * @cfg {String} cls cell class
7384  * @cfg {String} tag cell tag (td|th) default td
7385  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7386  * @cfg {String} align Aligns the content in a cell
7387  * @cfg {String} axis Categorizes cells
7388  * @cfg {String} bgcolor Specifies the background color of a cell
7389  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7390  * @cfg {Number} colspan Specifies the number of columns a cell should span
7391  * @cfg {String} headers Specifies one or more header cells a cell is related to
7392  * @cfg {Number} height Sets the height of a cell
7393  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7394  * @cfg {Number} rowspan Sets the number of rows a cell should span
7395  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7396  * @cfg {String} valign Vertical aligns the content in a cell
7397  * @cfg {Number} width Specifies the width of a cell
7398  * 
7399  * @constructor
7400  * Create a new TableCell
7401  * @param {Object} config The config object
7402  */
7403
7404 Roo.bootstrap.TableCell = function(config){
7405     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7406 };
7407
7408 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7409     
7410     html: false,
7411     cls: false,
7412     tag: false,
7413     abbr: false,
7414     align: false,
7415     axis: false,
7416     bgcolor: false,
7417     charoff: false,
7418     colspan: false,
7419     headers: false,
7420     height: false,
7421     nowrap: false,
7422     rowspan: false,
7423     scope: false,
7424     valign: false,
7425     width: false,
7426     
7427     
7428     getAutoCreate : function(){
7429         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7430         
7431         cfg = {
7432             tag: 'td'
7433         };
7434         
7435         if(this.tag){
7436             cfg.tag = this.tag;
7437         }
7438         
7439         if (this.html) {
7440             cfg.html=this.html
7441         }
7442         if (this.cls) {
7443             cfg.cls=this.cls
7444         }
7445         if (this.abbr) {
7446             cfg.abbr=this.abbr
7447         }
7448         if (this.align) {
7449             cfg.align=this.align
7450         }
7451         if (this.axis) {
7452             cfg.axis=this.axis
7453         }
7454         if (this.bgcolor) {
7455             cfg.bgcolor=this.bgcolor
7456         }
7457         if (this.charoff) {
7458             cfg.charoff=this.charoff
7459         }
7460         if (this.colspan) {
7461             cfg.colspan=this.colspan
7462         }
7463         if (this.headers) {
7464             cfg.headers=this.headers
7465         }
7466         if (this.height) {
7467             cfg.height=this.height
7468         }
7469         if (this.nowrap) {
7470             cfg.nowrap=this.nowrap
7471         }
7472         if (this.rowspan) {
7473             cfg.rowspan=this.rowspan
7474         }
7475         if (this.scope) {
7476             cfg.scope=this.scope
7477         }
7478         if (this.valign) {
7479             cfg.valign=this.valign
7480         }
7481         if (this.width) {
7482             cfg.width=this.width
7483         }
7484         
7485         
7486         return cfg;
7487     }
7488    
7489 });
7490
7491  
7492
7493  /*
7494  * - LGPL
7495  *
7496  * table row
7497  * 
7498  */
7499
7500 /**
7501  * @class Roo.bootstrap.TableRow
7502  * @extends Roo.bootstrap.Component
7503  * Bootstrap TableRow class
7504  * @cfg {String} cls row class
7505  * @cfg {String} align Aligns the content in a table row
7506  * @cfg {String} bgcolor Specifies a background color for a table row
7507  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7508  * @cfg {String} valign Vertical aligns the content in a table row
7509  * 
7510  * @constructor
7511  * Create a new TableRow
7512  * @param {Object} config The config object
7513  */
7514
7515 Roo.bootstrap.TableRow = function(config){
7516     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7517 };
7518
7519 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7520     
7521     cls: false,
7522     align: false,
7523     bgcolor: false,
7524     charoff: false,
7525     valign: false,
7526     
7527     getAutoCreate : function(){
7528         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7529         
7530         cfg = {
7531             tag: 'tr'
7532         };
7533             
7534         if(this.cls){
7535             cfg.cls = this.cls;
7536         }
7537         if(this.align){
7538             cfg.align = this.align;
7539         }
7540         if(this.bgcolor){
7541             cfg.bgcolor = this.bgcolor;
7542         }
7543         if(this.charoff){
7544             cfg.charoff = this.charoff;
7545         }
7546         if(this.valign){
7547             cfg.valign = this.valign;
7548         }
7549         
7550         return cfg;
7551     }
7552    
7553 });
7554
7555  
7556
7557  /*
7558  * - LGPL
7559  *
7560  * table body
7561  * 
7562  */
7563
7564 /**
7565  * @class Roo.bootstrap.TableBody
7566  * @extends Roo.bootstrap.Component
7567  * Bootstrap TableBody class
7568  * @cfg {String} cls element class
7569  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7570  * @cfg {String} align Aligns the content inside the element
7571  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7572  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7573  * 
7574  * @constructor
7575  * Create a new TableBody
7576  * @param {Object} config The config object
7577  */
7578
7579 Roo.bootstrap.TableBody = function(config){
7580     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7581 };
7582
7583 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7584     
7585     cls: false,
7586     tag: false,
7587     align: false,
7588     charoff: false,
7589     valign: false,
7590     
7591     getAutoCreate : function(){
7592         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7593         
7594         cfg = {
7595             tag: 'tbody'
7596         };
7597             
7598         if (this.cls) {
7599             cfg.cls=this.cls
7600         }
7601         if(this.tag){
7602             cfg.tag = this.tag;
7603         }
7604         
7605         if(this.align){
7606             cfg.align = this.align;
7607         }
7608         if(this.charoff){
7609             cfg.charoff = this.charoff;
7610         }
7611         if(this.valign){
7612             cfg.valign = this.valign;
7613         }
7614         
7615         return cfg;
7616     }
7617     
7618     
7619 //    initEvents : function()
7620 //    {
7621 //        
7622 //        if(!this.store){
7623 //            return;
7624 //        }
7625 //        
7626 //        this.store = Roo.factory(this.store, Roo.data);
7627 //        this.store.on('load', this.onLoad, this);
7628 //        
7629 //        this.store.load();
7630 //        
7631 //    },
7632 //    
7633 //    onLoad: function () 
7634 //    {   
7635 //        this.fireEvent('load', this);
7636 //    }
7637 //    
7638 //   
7639 });
7640
7641  
7642
7643  /*
7644  * Based on:
7645  * Ext JS Library 1.1.1
7646  * Copyright(c) 2006-2007, Ext JS, LLC.
7647  *
7648  * Originally Released Under LGPL - original licence link has changed is not relivant.
7649  *
7650  * Fork - LGPL
7651  * <script type="text/javascript">
7652  */
7653
7654 // as we use this in bootstrap.
7655 Roo.namespace('Roo.form');
7656  /**
7657  * @class Roo.form.Action
7658  * Internal Class used to handle form actions
7659  * @constructor
7660  * @param {Roo.form.BasicForm} el The form element or its id
7661  * @param {Object} config Configuration options
7662  */
7663
7664  
7665  
7666 // define the action interface
7667 Roo.form.Action = function(form, options){
7668     this.form = form;
7669     this.options = options || {};
7670 };
7671 /**
7672  * Client Validation Failed
7673  * @const 
7674  */
7675 Roo.form.Action.CLIENT_INVALID = 'client';
7676 /**
7677  * Server Validation Failed
7678  * @const 
7679  */
7680 Roo.form.Action.SERVER_INVALID = 'server';
7681  /**
7682  * Connect to Server Failed
7683  * @const 
7684  */
7685 Roo.form.Action.CONNECT_FAILURE = 'connect';
7686 /**
7687  * Reading Data from Server Failed
7688  * @const 
7689  */
7690 Roo.form.Action.LOAD_FAILURE = 'load';
7691
7692 Roo.form.Action.prototype = {
7693     type : 'default',
7694     failureType : undefined,
7695     response : undefined,
7696     result : undefined,
7697
7698     // interface method
7699     run : function(options){
7700
7701     },
7702
7703     // interface method
7704     success : function(response){
7705
7706     },
7707
7708     // interface method
7709     handleResponse : function(response){
7710
7711     },
7712
7713     // default connection failure
7714     failure : function(response){
7715         
7716         this.response = response;
7717         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7718         this.form.afterAction(this, false);
7719     },
7720
7721     processResponse : function(response){
7722         this.response = response;
7723         if(!response.responseText){
7724             return true;
7725         }
7726         this.result = this.handleResponse(response);
7727         return this.result;
7728     },
7729
7730     // utility functions used internally
7731     getUrl : function(appendParams){
7732         var url = this.options.url || this.form.url || this.form.el.dom.action;
7733         if(appendParams){
7734             var p = this.getParams();
7735             if(p){
7736                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7737             }
7738         }
7739         return url;
7740     },
7741
7742     getMethod : function(){
7743         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7744     },
7745
7746     getParams : function(){
7747         var bp = this.form.baseParams;
7748         var p = this.options.params;
7749         if(p){
7750             if(typeof p == "object"){
7751                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7752             }else if(typeof p == 'string' && bp){
7753                 p += '&' + Roo.urlEncode(bp);
7754             }
7755         }else if(bp){
7756             p = Roo.urlEncode(bp);
7757         }
7758         return p;
7759     },
7760
7761     createCallback : function(){
7762         return {
7763             success: this.success,
7764             failure: this.failure,
7765             scope: this,
7766             timeout: (this.form.timeout*1000),
7767             upload: this.form.fileUpload ? this.success : undefined
7768         };
7769     }
7770 };
7771
7772 Roo.form.Action.Submit = function(form, options){
7773     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7774 };
7775
7776 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7777     type : 'submit',
7778
7779     haveProgress : false,
7780     uploadComplete : false,
7781     
7782     // uploadProgress indicator.
7783     uploadProgress : function()
7784     {
7785         if (!this.form.progressUrl) {
7786             return;
7787         }
7788         
7789         if (!this.haveProgress) {
7790             Roo.MessageBox.progress("Uploading", "Uploading");
7791         }
7792         if (this.uploadComplete) {
7793            Roo.MessageBox.hide();
7794            return;
7795         }
7796         
7797         this.haveProgress = true;
7798    
7799         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7800         
7801         var c = new Roo.data.Connection();
7802         c.request({
7803             url : this.form.progressUrl,
7804             params: {
7805                 id : uid
7806             },
7807             method: 'GET',
7808             success : function(req){
7809                //console.log(data);
7810                 var rdata = false;
7811                 var edata;
7812                 try  {
7813                    rdata = Roo.decode(req.responseText)
7814                 } catch (e) {
7815                     Roo.log("Invalid data from server..");
7816                     Roo.log(edata);
7817                     return;
7818                 }
7819                 if (!rdata || !rdata.success) {
7820                     Roo.log(rdata);
7821                     Roo.MessageBox.alert(Roo.encode(rdata));
7822                     return;
7823                 }
7824                 var data = rdata.data;
7825                 
7826                 if (this.uploadComplete) {
7827                    Roo.MessageBox.hide();
7828                    return;
7829                 }
7830                    
7831                 if (data){
7832                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7833                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7834                     );
7835                 }
7836                 this.uploadProgress.defer(2000,this);
7837             },
7838        
7839             failure: function(data) {
7840                 Roo.log('progress url failed ');
7841                 Roo.log(data);
7842             },
7843             scope : this
7844         });
7845            
7846     },
7847     
7848     
7849     run : function()
7850     {
7851         // run get Values on the form, so it syncs any secondary forms.
7852         this.form.getValues();
7853         
7854         var o = this.options;
7855         var method = this.getMethod();
7856         var isPost = method == 'POST';
7857         if(o.clientValidation === false || this.form.isValid()){
7858             
7859             if (this.form.progressUrl) {
7860                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7861                     (new Date() * 1) + '' + Math.random());
7862                     
7863             } 
7864             
7865             
7866             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7867                 form:this.form.el.dom,
7868                 url:this.getUrl(!isPost),
7869                 method: method,
7870                 params:isPost ? this.getParams() : null,
7871                 isUpload: this.form.fileUpload
7872             }));
7873             
7874             this.uploadProgress();
7875
7876         }else if (o.clientValidation !== false){ // client validation failed
7877             this.failureType = Roo.form.Action.CLIENT_INVALID;
7878             this.form.afterAction(this, false);
7879         }
7880     },
7881
7882     success : function(response)
7883     {
7884         this.uploadComplete= true;
7885         if (this.haveProgress) {
7886             Roo.MessageBox.hide();
7887         }
7888         
7889         
7890         var result = this.processResponse(response);
7891         if(result === true || result.success){
7892             this.form.afterAction(this, true);
7893             return;
7894         }
7895         if(result.errors){
7896             this.form.markInvalid(result.errors);
7897             this.failureType = Roo.form.Action.SERVER_INVALID;
7898         }
7899         this.form.afterAction(this, false);
7900     },
7901     failure : function(response)
7902     {
7903         this.uploadComplete= true;
7904         if (this.haveProgress) {
7905             Roo.MessageBox.hide();
7906         }
7907         
7908         this.response = response;
7909         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7910         this.form.afterAction(this, false);
7911     },
7912     
7913     handleResponse : function(response){
7914         if(this.form.errorReader){
7915             var rs = this.form.errorReader.read(response);
7916             var errors = [];
7917             if(rs.records){
7918                 for(var i = 0, len = rs.records.length; i < len; i++) {
7919                     var r = rs.records[i];
7920                     errors[i] = r.data;
7921                 }
7922             }
7923             if(errors.length < 1){
7924                 errors = null;
7925             }
7926             return {
7927                 success : rs.success,
7928                 errors : errors
7929             };
7930         }
7931         var ret = false;
7932         try {
7933             ret = Roo.decode(response.responseText);
7934         } catch (e) {
7935             ret = {
7936                 success: false,
7937                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7938                 errors : []
7939             };
7940         }
7941         return ret;
7942         
7943     }
7944 });
7945
7946
7947 Roo.form.Action.Load = function(form, options){
7948     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7949     this.reader = this.form.reader;
7950 };
7951
7952 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7953     type : 'load',
7954
7955     run : function(){
7956         
7957         Roo.Ajax.request(Roo.apply(
7958                 this.createCallback(), {
7959                     method:this.getMethod(),
7960                     url:this.getUrl(false),
7961                     params:this.getParams()
7962         }));
7963     },
7964
7965     success : function(response){
7966         
7967         var result = this.processResponse(response);
7968         if(result === true || !result.success || !result.data){
7969             this.failureType = Roo.form.Action.LOAD_FAILURE;
7970             this.form.afterAction(this, false);
7971             return;
7972         }
7973         this.form.clearInvalid();
7974         this.form.setValues(result.data);
7975         this.form.afterAction(this, true);
7976     },
7977
7978     handleResponse : function(response){
7979         if(this.form.reader){
7980             var rs = this.form.reader.read(response);
7981             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7982             return {
7983                 success : rs.success,
7984                 data : data
7985             };
7986         }
7987         return Roo.decode(response.responseText);
7988     }
7989 });
7990
7991 Roo.form.Action.ACTION_TYPES = {
7992     'load' : Roo.form.Action.Load,
7993     'submit' : Roo.form.Action.Submit
7994 };/*
7995  * - LGPL
7996  *
7997  * form
7998  *
7999  */
8000
8001 /**
8002  * @class Roo.bootstrap.Form
8003  * @extends Roo.bootstrap.Component
8004  * Bootstrap Form class
8005  * @cfg {String} method  GET | POST (default POST)
8006  * @cfg {String} labelAlign top | left (default top)
8007  * @cfg {String} align left  | right - for navbars
8008  * @cfg {Boolean} loadMask load mask when submit (default true)
8009
8010  *
8011  * @constructor
8012  * Create a new Form
8013  * @param {Object} config The config object
8014  */
8015
8016
8017 Roo.bootstrap.Form = function(config){
8018     
8019     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8020     
8021     Roo.bootstrap.Form.popover.apply();
8022     
8023     this.addEvents({
8024         /**
8025          * @event clientvalidation
8026          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8027          * @param {Form} this
8028          * @param {Boolean} valid true if the form has passed client-side validation
8029          */
8030         clientvalidation: true,
8031         /**
8032          * @event beforeaction
8033          * Fires before any action is performed. Return false to cancel the action.
8034          * @param {Form} this
8035          * @param {Action} action The action to be performed
8036          */
8037         beforeaction: true,
8038         /**
8039          * @event actionfailed
8040          * Fires when an action fails.
8041          * @param {Form} this
8042          * @param {Action} action The action that failed
8043          */
8044         actionfailed : true,
8045         /**
8046          * @event actioncomplete
8047          * Fires when an action is completed.
8048          * @param {Form} this
8049          * @param {Action} action The action that completed
8050          */
8051         actioncomplete : true
8052     });
8053 };
8054
8055 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8056
8057      /**
8058      * @cfg {String} method
8059      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8060      */
8061     method : 'POST',
8062     /**
8063      * @cfg {String} url
8064      * The URL to use for form actions if one isn't supplied in the action options.
8065      */
8066     /**
8067      * @cfg {Boolean} fileUpload
8068      * Set to true if this form is a file upload.
8069      */
8070
8071     /**
8072      * @cfg {Object} baseParams
8073      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8074      */
8075
8076     /**
8077      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8078      */
8079     timeout: 30,
8080     /**
8081      * @cfg {Sting} align (left|right) for navbar forms
8082      */
8083     align : 'left',
8084
8085     // private
8086     activeAction : null,
8087
8088     /**
8089      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8090      * element by passing it or its id or mask the form itself by passing in true.
8091      * @type Mixed
8092      */
8093     waitMsgTarget : false,
8094
8095     loadMask : true,
8096     
8097     /**
8098      * @cfg {Boolean} errorMask (true|false) default false
8099      */
8100     errorMask : false,
8101     
8102     /**
8103      * @cfg {Number} maskOffset Default 100
8104      */
8105     maskOffset : 100,
8106     
8107     /**
8108      * @cfg {Boolean} maskBody
8109      */
8110     maskBody : false,
8111
8112     getAutoCreate : function(){
8113
8114         var cfg = {
8115             tag: 'form',
8116             method : this.method || 'POST',
8117             id : this.id || Roo.id(),
8118             cls : ''
8119         };
8120         if (this.parent().xtype.match(/^Nav/)) {
8121             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8122
8123         }
8124
8125         if (this.labelAlign == 'left' ) {
8126             cfg.cls += ' form-horizontal';
8127         }
8128
8129
8130         return cfg;
8131     },
8132     initEvents : function()
8133     {
8134         this.el.on('submit', this.onSubmit, this);
8135         // this was added as random key presses on the form where triggering form submit.
8136         this.el.on('keypress', function(e) {
8137             if (e.getCharCode() != 13) {
8138                 return true;
8139             }
8140             // we might need to allow it for textareas.. and some other items.
8141             // check e.getTarget().
8142
8143             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8144                 return true;
8145             }
8146
8147             Roo.log("keypress blocked");
8148
8149             e.preventDefault();
8150             return false;
8151         });
8152         
8153     },
8154     // private
8155     onSubmit : function(e){
8156         e.stopEvent();
8157     },
8158
8159      /**
8160      * Returns true if client-side validation on the form is successful.
8161      * @return Boolean
8162      */
8163     isValid : function(){
8164         var items = this.getItems();
8165         var valid = true;
8166         var target = false;
8167         
8168         items.each(function(f){
8169             
8170             if(f.validate()){
8171                 return;
8172             }
8173             
8174             Roo.log('invalid field: ' + f.name);
8175             
8176             valid = false;
8177
8178             if(!target && f.el.isVisible(true)){
8179                 target = f;
8180             }
8181            
8182         });
8183         
8184         if(this.errorMask && !valid){
8185             Roo.bootstrap.Form.popover.mask(this, target);
8186         }
8187         
8188         return valid;
8189     },
8190     
8191     /**
8192      * Returns true if any fields in this form have changed since their original load.
8193      * @return Boolean
8194      */
8195     isDirty : function(){
8196         var dirty = false;
8197         var items = this.getItems();
8198         items.each(function(f){
8199            if(f.isDirty()){
8200                dirty = true;
8201                return false;
8202            }
8203            return true;
8204         });
8205         return dirty;
8206     },
8207      /**
8208      * Performs a predefined action (submit or load) or custom actions you define on this form.
8209      * @param {String} actionName The name of the action type
8210      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8211      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8212      * accept other config options):
8213      * <pre>
8214 Property          Type             Description
8215 ----------------  ---------------  ----------------------------------------------------------------------------------
8216 url               String           The url for the action (defaults to the form's url)
8217 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8218 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8219 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8220                                    validate the form on the client (defaults to false)
8221      * </pre>
8222      * @return {BasicForm} this
8223      */
8224     doAction : function(action, options){
8225         if(typeof action == 'string'){
8226             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8227         }
8228         if(this.fireEvent('beforeaction', this, action) !== false){
8229             this.beforeAction(action);
8230             action.run.defer(100, action);
8231         }
8232         return this;
8233     },
8234
8235     // private
8236     beforeAction : function(action){
8237         var o = action.options;
8238         
8239         if(this.loadMask){
8240             
8241             if(this.maskBody){
8242                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8243             } else {
8244                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8245             }
8246         }
8247         // not really supported yet.. ??
8248
8249         //if(this.waitMsgTarget === true){
8250         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8251         //}else if(this.waitMsgTarget){
8252         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8253         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8254         //}else {
8255         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8256        // }
8257
8258     },
8259
8260     // private
8261     afterAction : function(action, success){
8262         this.activeAction = null;
8263         var o = action.options;
8264
8265         if(this.loadMask){
8266             
8267             if(this.maskBody){
8268                 Roo.get(document.body).unmask();
8269             } else {
8270                 this.el.unmask();
8271             }
8272         }
8273         
8274         //if(this.waitMsgTarget === true){
8275 //            this.el.unmask();
8276         //}else if(this.waitMsgTarget){
8277         //    this.waitMsgTarget.unmask();
8278         //}else{
8279         //    Roo.MessageBox.updateProgress(1);
8280         //    Roo.MessageBox.hide();
8281        // }
8282         //
8283         if(success){
8284             if(o.reset){
8285                 this.reset();
8286             }
8287             Roo.callback(o.success, o.scope, [this, action]);
8288             this.fireEvent('actioncomplete', this, action);
8289
8290         }else{
8291
8292             // failure condition..
8293             // we have a scenario where updates need confirming.
8294             // eg. if a locking scenario exists..
8295             // we look for { errors : { needs_confirm : true }} in the response.
8296             if (
8297                 (typeof(action.result) != 'undefined')  &&
8298                 (typeof(action.result.errors) != 'undefined')  &&
8299                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8300            ){
8301                 var _t = this;
8302                 Roo.log("not supported yet");
8303                  /*
8304
8305                 Roo.MessageBox.confirm(
8306                     "Change requires confirmation",
8307                     action.result.errorMsg,
8308                     function(r) {
8309                         if (r != 'yes') {
8310                             return;
8311                         }
8312                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8313                     }
8314
8315                 );
8316                 */
8317
8318
8319                 return;
8320             }
8321
8322             Roo.callback(o.failure, o.scope, [this, action]);
8323             // show an error message if no failed handler is set..
8324             if (!this.hasListener('actionfailed')) {
8325                 Roo.log("need to add dialog support");
8326                 /*
8327                 Roo.MessageBox.alert("Error",
8328                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8329                         action.result.errorMsg :
8330                         "Saving Failed, please check your entries or try again"
8331                 );
8332                 */
8333             }
8334
8335             this.fireEvent('actionfailed', this, action);
8336         }
8337
8338     },
8339     /**
8340      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8341      * @param {String} id The value to search for
8342      * @return Field
8343      */
8344     findField : function(id){
8345         var items = this.getItems();
8346         var field = items.get(id);
8347         if(!field){
8348              items.each(function(f){
8349                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8350                     field = f;
8351                     return false;
8352                 }
8353                 return true;
8354             });
8355         }
8356         return field || null;
8357     },
8358      /**
8359      * Mark fields in this form invalid in bulk.
8360      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8361      * @return {BasicForm} this
8362      */
8363     markInvalid : function(errors){
8364         if(errors instanceof Array){
8365             for(var i = 0, len = errors.length; i < len; i++){
8366                 var fieldError = errors[i];
8367                 var f = this.findField(fieldError.id);
8368                 if(f){
8369                     f.markInvalid(fieldError.msg);
8370                 }
8371             }
8372         }else{
8373             var field, id;
8374             for(id in errors){
8375                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8376                     field.markInvalid(errors[id]);
8377                 }
8378             }
8379         }
8380         //Roo.each(this.childForms || [], function (f) {
8381         //    f.markInvalid(errors);
8382         //});
8383
8384         return this;
8385     },
8386
8387     /**
8388      * Set values for fields in this form in bulk.
8389      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8390      * @return {BasicForm} this
8391      */
8392     setValues : function(values){
8393         if(values instanceof Array){ // array of objects
8394             for(var i = 0, len = values.length; i < len; i++){
8395                 var v = values[i];
8396                 var f = this.findField(v.id);
8397                 if(f){
8398                     f.setValue(v.value);
8399                     if(this.trackResetOnLoad){
8400                         f.originalValue = f.getValue();
8401                     }
8402                 }
8403             }
8404         }else{ // object hash
8405             var field, id;
8406             for(id in values){
8407                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8408
8409                     if (field.setFromData &&
8410                         field.valueField &&
8411                         field.displayField &&
8412                         // combos' with local stores can
8413                         // be queried via setValue()
8414                         // to set their value..
8415                         (field.store && !field.store.isLocal)
8416                         ) {
8417                         // it's a combo
8418                         var sd = { };
8419                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8420                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8421                         field.setFromData(sd);
8422
8423                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8424                         
8425                         field.setFromData(values);
8426                         
8427                     } else {
8428                         field.setValue(values[id]);
8429                     }
8430
8431
8432                     if(this.trackResetOnLoad){
8433                         field.originalValue = field.getValue();
8434                     }
8435                 }
8436             }
8437         }
8438
8439         //Roo.each(this.childForms || [], function (f) {
8440         //    f.setValues(values);
8441         //});
8442
8443         return this;
8444     },
8445
8446     /**
8447      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8448      * they are returned as an array.
8449      * @param {Boolean} asString
8450      * @return {Object}
8451      */
8452     getValues : function(asString){
8453         //if (this.childForms) {
8454             // copy values from the child forms
8455         //    Roo.each(this.childForms, function (f) {
8456         //        this.setValues(f.getValues());
8457         //    }, this);
8458         //}
8459
8460
8461
8462         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8463         if(asString === true){
8464             return fs;
8465         }
8466         return Roo.urlDecode(fs);
8467     },
8468
8469     /**
8470      * Returns the fields in this form as an object with key/value pairs.
8471      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8472      * @return {Object}
8473      */
8474     getFieldValues : function(with_hidden)
8475     {
8476         var items = this.getItems();
8477         var ret = {};
8478         items.each(function(f){
8479             
8480             if (!f.getName()) {
8481                 return;
8482             }
8483             
8484             var v = f.getValue();
8485             
8486             if (f.inputType =='radio') {
8487                 if (typeof(ret[f.getName()]) == 'undefined') {
8488                     ret[f.getName()] = ''; // empty..
8489                 }
8490
8491                 if (!f.el.dom.checked) {
8492                     return;
8493
8494                 }
8495                 v = f.el.dom.value;
8496
8497             }
8498             
8499             if(f.xtype == 'MoneyField'){
8500                 ret[f.currencyName] = f.getCurrency();
8501             }
8502
8503             // not sure if this supported any more..
8504             if ((typeof(v) == 'object') && f.getRawValue) {
8505                 v = f.getRawValue() ; // dates..
8506             }
8507             // combo boxes where name != hiddenName...
8508             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8509                 ret[f.name] = f.getRawValue();
8510             }
8511             ret[f.getName()] = v;
8512         });
8513
8514         return ret;
8515     },
8516
8517     /**
8518      * Clears all invalid messages in this form.
8519      * @return {BasicForm} this
8520      */
8521     clearInvalid : function(){
8522         var items = this.getItems();
8523
8524         items.each(function(f){
8525            f.clearInvalid();
8526         });
8527
8528         return this;
8529     },
8530
8531     /**
8532      * Resets this form.
8533      * @return {BasicForm} this
8534      */
8535     reset : function(){
8536         var items = this.getItems();
8537         items.each(function(f){
8538             f.reset();
8539         });
8540
8541         Roo.each(this.childForms || [], function (f) {
8542             f.reset();
8543         });
8544
8545
8546         return this;
8547     },
8548     
8549     getItems : function()
8550     {
8551         var r=new Roo.util.MixedCollection(false, function(o){
8552             return o.id || (o.id = Roo.id());
8553         });
8554         var iter = function(el) {
8555             if (el.inputEl) {
8556                 r.add(el);
8557             }
8558             if (!el.items) {
8559                 return;
8560             }
8561             Roo.each(el.items,function(e) {
8562                 iter(e);
8563             });
8564         };
8565
8566         iter(this);
8567         return r;
8568     },
8569     
8570     hideFields : function(items)
8571     {
8572         Roo.each(items, function(i){
8573             
8574             var f = this.findField(i);
8575             
8576             if(!f){
8577                 return;
8578             }
8579             
8580             f.hide();
8581             
8582         }, this);
8583     },
8584     
8585     showFields : function(items)
8586     {
8587         Roo.each(items, function(i){
8588             
8589             var f = this.findField(i);
8590             
8591             if(!f){
8592                 return;
8593             }
8594             
8595             f.show();
8596             
8597         }, this);
8598     }
8599
8600 });
8601
8602 Roo.apply(Roo.bootstrap.Form, {
8603     
8604     popover : {
8605         
8606         padding : 5,
8607         
8608         isApplied : false,
8609         
8610         isMasked : false,
8611         
8612         form : false,
8613         
8614         target : false,
8615         
8616         toolTip : false,
8617         
8618         intervalID : false,
8619         
8620         maskEl : false,
8621         
8622         apply : function()
8623         {
8624             if(this.isApplied){
8625                 return;
8626             }
8627             
8628             this.maskEl = {
8629                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8630                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8631                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8632                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8633             };
8634             
8635             this.maskEl.top.enableDisplayMode("block");
8636             this.maskEl.left.enableDisplayMode("block");
8637             this.maskEl.bottom.enableDisplayMode("block");
8638             this.maskEl.right.enableDisplayMode("block");
8639             
8640             this.toolTip = new Roo.bootstrap.Tooltip({
8641                 cls : 'roo-form-error-popover',
8642                 alignment : {
8643                     'left' : ['r-l', [-2,0], 'right'],
8644                     'right' : ['l-r', [2,0], 'left'],
8645                     'bottom' : ['tl-bl', [0,2], 'top'],
8646                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8647                 }
8648             });
8649             
8650             this.toolTip.render(Roo.get(document.body));
8651
8652             this.toolTip.el.enableDisplayMode("block");
8653             
8654             Roo.get(document.body).on('click', function(){
8655                 this.unmask();
8656             }, this);
8657             
8658             Roo.get(document.body).on('touchstart', function(){
8659                 this.unmask();
8660             }, this);
8661             
8662             this.isApplied = true
8663         },
8664         
8665         mask : function(form, target)
8666         {
8667             this.form = form;
8668             
8669             this.target = target;
8670             
8671             if(!this.form.errorMask || !target.el){
8672                 return;
8673             }
8674             
8675             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8676             
8677             Roo.log(scrollable);
8678             
8679             var ot = this.target.el.calcOffsetsTo(scrollable);
8680             
8681             var scrollTo = ot[1] - this.form.maskOffset;
8682             
8683             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8684             
8685             scrollable.scrollTo('top', scrollTo);
8686             
8687             var box = this.target.el.getBox();
8688             Roo.log(box);
8689             var zIndex = Roo.bootstrap.Modal.zIndex++;
8690
8691             
8692             this.maskEl.top.setStyle('position', 'absolute');
8693             this.maskEl.top.setStyle('z-index', zIndex);
8694             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8695             this.maskEl.top.setLeft(0);
8696             this.maskEl.top.setTop(0);
8697             this.maskEl.top.show();
8698             
8699             this.maskEl.left.setStyle('position', 'absolute');
8700             this.maskEl.left.setStyle('z-index', zIndex);
8701             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8702             this.maskEl.left.setLeft(0);
8703             this.maskEl.left.setTop(box.y - this.padding);
8704             this.maskEl.left.show();
8705
8706             this.maskEl.bottom.setStyle('position', 'absolute');
8707             this.maskEl.bottom.setStyle('z-index', zIndex);
8708             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8709             this.maskEl.bottom.setLeft(0);
8710             this.maskEl.bottom.setTop(box.bottom + this.padding);
8711             this.maskEl.bottom.show();
8712
8713             this.maskEl.right.setStyle('position', 'absolute');
8714             this.maskEl.right.setStyle('z-index', zIndex);
8715             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8716             this.maskEl.right.setLeft(box.right + this.padding);
8717             this.maskEl.right.setTop(box.y - this.padding);
8718             this.maskEl.right.show();
8719
8720             this.toolTip.bindEl = this.target.el;
8721
8722             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8723
8724             var tip = this.target.blankText;
8725
8726             if(this.target.getValue() !== '' ) {
8727                 
8728                 if (this.target.invalidText.length) {
8729                     tip = this.target.invalidText;
8730                 } else if (this.target.regexText.length){
8731                     tip = this.target.regexText;
8732                 }
8733             }
8734
8735             this.toolTip.show(tip);
8736
8737             this.intervalID = window.setInterval(function() {
8738                 Roo.bootstrap.Form.popover.unmask();
8739             }, 10000);
8740
8741             window.onwheel = function(){ return false;};
8742             
8743             (function(){ this.isMasked = true; }).defer(500, this);
8744             
8745         },
8746         
8747         unmask : function()
8748         {
8749             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8750                 return;
8751             }
8752             
8753             this.maskEl.top.setStyle('position', 'absolute');
8754             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8755             this.maskEl.top.hide();
8756
8757             this.maskEl.left.setStyle('position', 'absolute');
8758             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8759             this.maskEl.left.hide();
8760
8761             this.maskEl.bottom.setStyle('position', 'absolute');
8762             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8763             this.maskEl.bottom.hide();
8764
8765             this.maskEl.right.setStyle('position', 'absolute');
8766             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8767             this.maskEl.right.hide();
8768             
8769             this.toolTip.hide();
8770             
8771             this.toolTip.el.hide();
8772             
8773             window.onwheel = function(){ return true;};
8774             
8775             if(this.intervalID){
8776                 window.clearInterval(this.intervalID);
8777                 this.intervalID = false;
8778             }
8779             
8780             this.isMasked = false;
8781             
8782         }
8783         
8784     }
8785     
8786 });
8787
8788 /*
8789  * Based on:
8790  * Ext JS Library 1.1.1
8791  * Copyright(c) 2006-2007, Ext JS, LLC.
8792  *
8793  * Originally Released Under LGPL - original licence link has changed is not relivant.
8794  *
8795  * Fork - LGPL
8796  * <script type="text/javascript">
8797  */
8798 /**
8799  * @class Roo.form.VTypes
8800  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8801  * @singleton
8802  */
8803 Roo.form.VTypes = function(){
8804     // closure these in so they are only created once.
8805     var alpha = /^[a-zA-Z_]+$/;
8806     var alphanum = /^[a-zA-Z0-9_]+$/;
8807     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8808     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8809
8810     // All these messages and functions are configurable
8811     return {
8812         /**
8813          * The function used to validate email addresses
8814          * @param {String} value The email address
8815          */
8816         'email' : function(v){
8817             return email.test(v);
8818         },
8819         /**
8820          * The error text to display when the email validation function returns false
8821          * @type String
8822          */
8823         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8824         /**
8825          * The keystroke filter mask to be applied on email input
8826          * @type RegExp
8827          */
8828         'emailMask' : /[a-z0-9_\.\-@]/i,
8829
8830         /**
8831          * The function used to validate URLs
8832          * @param {String} value The URL
8833          */
8834         'url' : function(v){
8835             return url.test(v);
8836         },
8837         /**
8838          * The error text to display when the url validation function returns false
8839          * @type String
8840          */
8841         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8842         
8843         /**
8844          * The function used to validate alpha values
8845          * @param {String} value The value
8846          */
8847         'alpha' : function(v){
8848             return alpha.test(v);
8849         },
8850         /**
8851          * The error text to display when the alpha validation function returns false
8852          * @type String
8853          */
8854         'alphaText' : 'This field should only contain letters and _',
8855         /**
8856          * The keystroke filter mask to be applied on alpha input
8857          * @type RegExp
8858          */
8859         'alphaMask' : /[a-z_]/i,
8860
8861         /**
8862          * The function used to validate alphanumeric values
8863          * @param {String} value The value
8864          */
8865         'alphanum' : function(v){
8866             return alphanum.test(v);
8867         },
8868         /**
8869          * The error text to display when the alphanumeric validation function returns false
8870          * @type String
8871          */
8872         'alphanumText' : 'This field should only contain letters, numbers and _',
8873         /**
8874          * The keystroke filter mask to be applied on alphanumeric input
8875          * @type RegExp
8876          */
8877         'alphanumMask' : /[a-z0-9_]/i
8878     };
8879 }();/*
8880  * - LGPL
8881  *
8882  * Input
8883  * 
8884  */
8885
8886 /**
8887  * @class Roo.bootstrap.Input
8888  * @extends Roo.bootstrap.Component
8889  * Bootstrap Input class
8890  * @cfg {Boolean} disabled is it disabled
8891  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8892  * @cfg {String} name name of the input
8893  * @cfg {string} fieldLabel - the label associated
8894  * @cfg {string} placeholder - placeholder to put in text.
8895  * @cfg {string}  before - input group add on before
8896  * @cfg {string} after - input group add on after
8897  * @cfg {string} size - (lg|sm) or leave empty..
8898  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8899  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8900  * @cfg {Number} md colspan out of 12 for computer-sized screens
8901  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8902  * @cfg {string} value default value of the input
8903  * @cfg {Number} labelWidth set the width of label 
8904  * @cfg {Number} labellg set the width of label (1-12)
8905  * @cfg {Number} labelmd set the width of label (1-12)
8906  * @cfg {Number} labelsm set the width of label (1-12)
8907  * @cfg {Number} labelxs set the width of label (1-12)
8908  * @cfg {String} labelAlign (top|left)
8909  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8910  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8911  * @cfg {String} indicatorpos (left|right) default left
8912  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8913  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8914
8915  * @cfg {String} align (left|center|right) Default left
8916  * @cfg {Boolean} forceFeedback (true|false) Default false
8917  * 
8918  * @constructor
8919  * Create a new Input
8920  * @param {Object} config The config object
8921  */
8922
8923 Roo.bootstrap.Input = function(config){
8924     
8925     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8926     
8927     this.addEvents({
8928         /**
8929          * @event focus
8930          * Fires when this field receives input focus.
8931          * @param {Roo.form.Field} this
8932          */
8933         focus : true,
8934         /**
8935          * @event blur
8936          * Fires when this field loses input focus.
8937          * @param {Roo.form.Field} this
8938          */
8939         blur : true,
8940         /**
8941          * @event specialkey
8942          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8943          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8944          * @param {Roo.form.Field} this
8945          * @param {Roo.EventObject} e The event object
8946          */
8947         specialkey : true,
8948         /**
8949          * @event change
8950          * Fires just before the field blurs if the field value has changed.
8951          * @param {Roo.form.Field} this
8952          * @param {Mixed} newValue The new value
8953          * @param {Mixed} oldValue The original value
8954          */
8955         change : true,
8956         /**
8957          * @event invalid
8958          * Fires after the field has been marked as invalid.
8959          * @param {Roo.form.Field} this
8960          * @param {String} msg The validation message
8961          */
8962         invalid : true,
8963         /**
8964          * @event valid
8965          * Fires after the field has been validated with no errors.
8966          * @param {Roo.form.Field} this
8967          */
8968         valid : true,
8969          /**
8970          * @event keyup
8971          * Fires after the key up
8972          * @param {Roo.form.Field} this
8973          * @param {Roo.EventObject}  e The event Object
8974          */
8975         keyup : true
8976     });
8977 };
8978
8979 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8980      /**
8981      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8982       automatic validation (defaults to "keyup").
8983      */
8984     validationEvent : "keyup",
8985      /**
8986      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8987      */
8988     validateOnBlur : true,
8989     /**
8990      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8991      */
8992     validationDelay : 250,
8993      /**
8994      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8995      */
8996     focusClass : "x-form-focus",  // not needed???
8997     
8998        
8999     /**
9000      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9001      */
9002     invalidClass : "has-warning",
9003     
9004     /**
9005      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9006      */
9007     validClass : "has-success",
9008     
9009     /**
9010      * @cfg {Boolean} hasFeedback (true|false) default true
9011      */
9012     hasFeedback : true,
9013     
9014     /**
9015      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9016      */
9017     invalidFeedbackClass : "glyphicon-warning-sign",
9018     
9019     /**
9020      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9021      */
9022     validFeedbackClass : "glyphicon-ok",
9023     
9024     /**
9025      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9026      */
9027     selectOnFocus : false,
9028     
9029      /**
9030      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9031      */
9032     maskRe : null,
9033        /**
9034      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9035      */
9036     vtype : null,
9037     
9038       /**
9039      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9040      */
9041     disableKeyFilter : false,
9042     
9043        /**
9044      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9045      */
9046     disabled : false,
9047      /**
9048      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9049      */
9050     allowBlank : true,
9051     /**
9052      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9053      */
9054     blankText : "Please complete this mandatory field",
9055     
9056      /**
9057      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9058      */
9059     minLength : 0,
9060     /**
9061      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9062      */
9063     maxLength : Number.MAX_VALUE,
9064     /**
9065      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9066      */
9067     minLengthText : "The minimum length for this field is {0}",
9068     /**
9069      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9070      */
9071     maxLengthText : "The maximum length for this field is {0}",
9072   
9073     
9074     /**
9075      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9076      * If available, this function will be called only after the basic validators all return true, and will be passed the
9077      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9078      */
9079     validator : null,
9080     /**
9081      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9082      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9083      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9084      */
9085     regex : null,
9086     /**
9087      * @cfg {String} regexText -- Depricated - use Invalid Text
9088      */
9089     regexText : "",
9090     
9091     /**
9092      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9093      */
9094     invalidText : "",
9095     
9096     
9097     
9098     autocomplete: false,
9099     
9100     
9101     fieldLabel : '',
9102     inputType : 'text',
9103     
9104     name : false,
9105     placeholder: false,
9106     before : false,
9107     after : false,
9108     size : false,
9109     hasFocus : false,
9110     preventMark: false,
9111     isFormField : true,
9112     value : '',
9113     labelWidth : 2,
9114     labelAlign : false,
9115     readOnly : false,
9116     align : false,
9117     formatedValue : false,
9118     forceFeedback : false,
9119     
9120     indicatorpos : 'left',
9121     
9122     labellg : 0,
9123     labelmd : 0,
9124     labelsm : 0,
9125     labelxs : 0,
9126     
9127     capture : '',
9128     accept : '',
9129     
9130     parentLabelAlign : function()
9131     {
9132         var parent = this;
9133         while (parent.parent()) {
9134             parent = parent.parent();
9135             if (typeof(parent.labelAlign) !='undefined') {
9136                 return parent.labelAlign;
9137             }
9138         }
9139         return 'left';
9140         
9141     },
9142     
9143     getAutoCreate : function()
9144     {
9145         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9146         
9147         var id = Roo.id();
9148         
9149         var cfg = {};
9150         
9151         if(this.inputType != 'hidden'){
9152             cfg.cls = 'form-group' //input-group
9153         }
9154         
9155         var input =  {
9156             tag: 'input',
9157             id : id,
9158             type : this.inputType,
9159             value : this.value,
9160             cls : 'form-control',
9161             placeholder : this.placeholder || '',
9162             autocomplete : this.autocomplete || 'new-password'
9163         };
9164         
9165         if(this.capture.length){
9166             input.capture = this.capture;
9167         }
9168         
9169         if(this.accept.length){
9170             input.accept = this.accept + "/*";
9171         }
9172         
9173         if(this.align){
9174             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9175         }
9176         
9177         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9178             input.maxLength = this.maxLength;
9179         }
9180         
9181         if (this.disabled) {
9182             input.disabled=true;
9183         }
9184         
9185         if (this.readOnly) {
9186             input.readonly=true;
9187         }
9188         
9189         if (this.name) {
9190             input.name = this.name;
9191         }
9192         
9193         if (this.size) {
9194             input.cls += ' input-' + this.size;
9195         }
9196         
9197         var settings=this;
9198         ['xs','sm','md','lg'].map(function(size){
9199             if (settings[size]) {
9200                 cfg.cls += ' col-' + size + '-' + settings[size];
9201             }
9202         });
9203         
9204         var inputblock = input;
9205         
9206         var feedback = {
9207             tag: 'span',
9208             cls: 'glyphicon form-control-feedback'
9209         };
9210             
9211         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9212             
9213             inputblock = {
9214                 cls : 'has-feedback',
9215                 cn :  [
9216                     input,
9217                     feedback
9218                 ] 
9219             };  
9220         }
9221         
9222         if (this.before || this.after) {
9223             
9224             inputblock = {
9225                 cls : 'input-group',
9226                 cn :  [] 
9227             };
9228             
9229             if (this.before && typeof(this.before) == 'string') {
9230                 
9231                 inputblock.cn.push({
9232                     tag :'span',
9233                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9234                     html : this.before
9235                 });
9236             }
9237             if (this.before && typeof(this.before) == 'object') {
9238                 this.before = Roo.factory(this.before);
9239                 
9240                 inputblock.cn.push({
9241                     tag :'span',
9242                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9243                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9244                 });
9245             }
9246             
9247             inputblock.cn.push(input);
9248             
9249             if (this.after && typeof(this.after) == 'string') {
9250                 inputblock.cn.push({
9251                     tag :'span',
9252                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9253                     html : this.after
9254                 });
9255             }
9256             if (this.after && typeof(this.after) == 'object') {
9257                 this.after = Roo.factory(this.after);
9258                 
9259                 inputblock.cn.push({
9260                     tag :'span',
9261                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9262                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9263                 });
9264             }
9265             
9266             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9267                 inputblock.cls += ' has-feedback';
9268                 inputblock.cn.push(feedback);
9269             }
9270         };
9271         var indicator = {
9272             tag : 'i',
9273             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9274             tooltip : 'This field is required'
9275         };
9276         if (Roo.bootstrap.version == 4) {
9277             indicator = {
9278                 tag : 'i',
9279                 style : 'display-none'
9280             };
9281         }
9282         if (align ==='left' && this.fieldLabel.length) {
9283             
9284             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9285             
9286             cfg.cn = [
9287                 indicator,
9288                 {
9289                     tag: 'label',
9290                     'for' :  id,
9291                     cls : 'control-label col-form-label',
9292                     html : this.fieldLabel
9293
9294                 },
9295                 {
9296                     cls : "", 
9297                     cn: [
9298                         inputblock
9299                     ]
9300                 }
9301             ];
9302             
9303             var labelCfg = cfg.cn[1];
9304             var contentCfg = cfg.cn[2];
9305             
9306             if(this.indicatorpos == 'right'){
9307                 cfg.cn = [
9308                     {
9309                         tag: 'label',
9310                         'for' :  id,
9311                         cls : 'control-label col-form-label',
9312                         cn : [
9313                             {
9314                                 tag : 'span',
9315                                 html : this.fieldLabel
9316                             },
9317                             indicator
9318                         ]
9319                     },
9320                     {
9321                         cls : "",
9322                         cn: [
9323                             inputblock
9324                         ]
9325                     }
9326
9327                 ];
9328                 
9329                 labelCfg = cfg.cn[0];
9330                 contentCfg = cfg.cn[1];
9331             
9332             }
9333             
9334             if(this.labelWidth > 12){
9335                 labelCfg.style = "width: " + this.labelWidth + 'px';
9336             }
9337             
9338             if(this.labelWidth < 13 && this.labelmd == 0){
9339                 this.labelmd = this.labelWidth;
9340             }
9341             
9342             if(this.labellg > 0){
9343                 labelCfg.cls += ' col-lg-' + this.labellg;
9344                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9345             }
9346             
9347             if(this.labelmd > 0){
9348                 labelCfg.cls += ' col-md-' + this.labelmd;
9349                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9350             }
9351             
9352             if(this.labelsm > 0){
9353                 labelCfg.cls += ' col-sm-' + this.labelsm;
9354                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9355             }
9356             
9357             if(this.labelxs > 0){
9358                 labelCfg.cls += ' col-xs-' + this.labelxs;
9359                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9360             }
9361             
9362             
9363         } else if ( this.fieldLabel.length) {
9364                 
9365             cfg.cn = [
9366                 {
9367                     tag : 'i',
9368                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9369                     tooltip : 'This field is required'
9370                 },
9371                 {
9372                     tag: 'label',
9373                    //cls : 'input-group-addon',
9374                     html : this.fieldLabel
9375
9376                 },
9377
9378                inputblock
9379
9380            ];
9381            
9382            if(this.indicatorpos == 'right'){
9383                 
9384                 cfg.cn = [
9385                     {
9386                         tag: 'label',
9387                        //cls : 'input-group-addon',
9388                         html : this.fieldLabel
9389
9390                     },
9391                     {
9392                         tag : 'i',
9393                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9394                         tooltip : 'This field is required'
9395                     },
9396
9397                    inputblock
9398
9399                ];
9400
9401             }
9402
9403         } else {
9404             
9405             cfg.cn = [
9406
9407                     inputblock
9408
9409             ];
9410                 
9411                 
9412         };
9413         
9414         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9415            cfg.cls += ' navbar-form';
9416         }
9417         
9418         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9419             // on BS4 we do this only if not form 
9420             cfg.cls += ' navbar-form';
9421             cfg.tag = 'li';
9422         }
9423         
9424         return cfg;
9425         
9426     },
9427     /**
9428      * return the real input element.
9429      */
9430     inputEl: function ()
9431     {
9432         return this.el.select('input.form-control',true).first();
9433     },
9434     
9435     tooltipEl : function()
9436     {
9437         return this.inputEl();
9438     },
9439     
9440     indicatorEl : function()
9441     {
9442         if (Roo.bootstrap.version == 4) {
9443             return false; // not enabled in v4 yet.
9444         }
9445         
9446         var indicator = this.el.select('i.roo-required-indicator',true).first();
9447         
9448         if(!indicator){
9449             return false;
9450         }
9451         
9452         return indicator;
9453         
9454     },
9455     
9456     setDisabled : function(v)
9457     {
9458         var i  = this.inputEl().dom;
9459         if (!v) {
9460             i.removeAttribute('disabled');
9461             return;
9462             
9463         }
9464         i.setAttribute('disabled','true');
9465     },
9466     initEvents : function()
9467     {
9468           
9469         this.inputEl().on("keydown" , this.fireKey,  this);
9470         this.inputEl().on("focus", this.onFocus,  this);
9471         this.inputEl().on("blur", this.onBlur,  this);
9472         
9473         this.inputEl().relayEvent('keyup', this);
9474         
9475         this.indicator = this.indicatorEl();
9476         
9477         if(this.indicator){
9478             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9479         }
9480  
9481         // reference to original value for reset
9482         this.originalValue = this.getValue();
9483         //Roo.form.TextField.superclass.initEvents.call(this);
9484         if(this.validationEvent == 'keyup'){
9485             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9486             this.inputEl().on('keyup', this.filterValidation, this);
9487         }
9488         else if(this.validationEvent !== false){
9489             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9490         }
9491         
9492         if(this.selectOnFocus){
9493             this.on("focus", this.preFocus, this);
9494             
9495         }
9496         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9497             this.inputEl().on("keypress", this.filterKeys, this);
9498         } else {
9499             this.inputEl().relayEvent('keypress', this);
9500         }
9501        /* if(this.grow){
9502             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9503             this.el.on("click", this.autoSize,  this);
9504         }
9505         */
9506         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9507             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9508         }
9509         
9510         if (typeof(this.before) == 'object') {
9511             this.before.render(this.el.select('.roo-input-before',true).first());
9512         }
9513         if (typeof(this.after) == 'object') {
9514             this.after.render(this.el.select('.roo-input-after',true).first());
9515         }
9516         
9517         this.inputEl().on('change', this.onChange, this);
9518         
9519     },
9520     filterValidation : function(e){
9521         if(!e.isNavKeyPress()){
9522             this.validationTask.delay(this.validationDelay);
9523         }
9524     },
9525      /**
9526      * Validates the field value
9527      * @return {Boolean} True if the value is valid, else false
9528      */
9529     validate : function(){
9530         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9531         if(this.disabled || this.validateValue(this.getRawValue())){
9532             this.markValid();
9533             return true;
9534         }
9535         
9536         this.markInvalid();
9537         return false;
9538     },
9539     
9540     
9541     /**
9542      * Validates a value according to the field's validation rules and marks the field as invalid
9543      * if the validation fails
9544      * @param {Mixed} value The value to validate
9545      * @return {Boolean} True if the value is valid, else false
9546      */
9547     validateValue : function(value)
9548     {
9549         if(this.getVisibilityEl().hasClass('hidden')){
9550             return true;
9551         }
9552         
9553         if(value.length < 1)  { // if it's blank
9554             if(this.allowBlank){
9555                 return true;
9556             }
9557             return false;
9558         }
9559         
9560         if(value.length < this.minLength){
9561             return false;
9562         }
9563         if(value.length > this.maxLength){
9564             return false;
9565         }
9566         if(this.vtype){
9567             var vt = Roo.form.VTypes;
9568             if(!vt[this.vtype](value, this)){
9569                 return false;
9570             }
9571         }
9572         if(typeof this.validator == "function"){
9573             var msg = this.validator(value);
9574             if(msg !== true){
9575                 return false;
9576             }
9577             if (typeof(msg) == 'string') {
9578                 this.invalidText = msg;
9579             }
9580         }
9581         
9582         if(this.regex && !this.regex.test(value)){
9583             return false;
9584         }
9585         
9586         return true;
9587     },
9588     
9589      // private
9590     fireKey : function(e){
9591         //Roo.log('field ' + e.getKey());
9592         if(e.isNavKeyPress()){
9593             this.fireEvent("specialkey", this, e);
9594         }
9595     },
9596     focus : function (selectText){
9597         if(this.rendered){
9598             this.inputEl().focus();
9599             if(selectText === true){
9600                 this.inputEl().dom.select();
9601             }
9602         }
9603         return this;
9604     } ,
9605     
9606     onFocus : function(){
9607         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9608            // this.el.addClass(this.focusClass);
9609         }
9610         if(!this.hasFocus){
9611             this.hasFocus = true;
9612             this.startValue = this.getValue();
9613             this.fireEvent("focus", this);
9614         }
9615     },
9616     
9617     beforeBlur : Roo.emptyFn,
9618
9619     
9620     // private
9621     onBlur : function(){
9622         this.beforeBlur();
9623         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9624             //this.el.removeClass(this.focusClass);
9625         }
9626         this.hasFocus = false;
9627         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9628             this.validate();
9629         }
9630         var v = this.getValue();
9631         if(String(v) !== String(this.startValue)){
9632             this.fireEvent('change', this, v, this.startValue);
9633         }
9634         this.fireEvent("blur", this);
9635     },
9636     
9637     onChange : function(e)
9638     {
9639         var v = this.getValue();
9640         if(String(v) !== String(this.startValue)){
9641             this.fireEvent('change', this, v, this.startValue);
9642         }
9643         
9644     },
9645     
9646     /**
9647      * Resets the current field value to the originally loaded value and clears any validation messages
9648      */
9649     reset : function(){
9650         this.setValue(this.originalValue);
9651         this.validate();
9652     },
9653      /**
9654      * Returns the name of the field
9655      * @return {Mixed} name The name field
9656      */
9657     getName: function(){
9658         return this.name;
9659     },
9660      /**
9661      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9662      * @return {Mixed} value The field value
9663      */
9664     getValue : function(){
9665         
9666         var v = this.inputEl().getValue();
9667         
9668         return v;
9669     },
9670     /**
9671      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9672      * @return {Mixed} value The field value
9673      */
9674     getRawValue : function(){
9675         var v = this.inputEl().getValue();
9676         
9677         return v;
9678     },
9679     
9680     /**
9681      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9682      * @param {Mixed} value The value to set
9683      */
9684     setRawValue : function(v){
9685         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9686     },
9687     
9688     selectText : function(start, end){
9689         var v = this.getRawValue();
9690         if(v.length > 0){
9691             start = start === undefined ? 0 : start;
9692             end = end === undefined ? v.length : end;
9693             var d = this.inputEl().dom;
9694             if(d.setSelectionRange){
9695                 d.setSelectionRange(start, end);
9696             }else if(d.createTextRange){
9697                 var range = d.createTextRange();
9698                 range.moveStart("character", start);
9699                 range.moveEnd("character", v.length-end);
9700                 range.select();
9701             }
9702         }
9703     },
9704     
9705     /**
9706      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9707      * @param {Mixed} value The value to set
9708      */
9709     setValue : function(v){
9710         this.value = v;
9711         if(this.rendered){
9712             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9713             this.validate();
9714         }
9715     },
9716     
9717     /*
9718     processValue : function(value){
9719         if(this.stripCharsRe){
9720             var newValue = value.replace(this.stripCharsRe, '');
9721             if(newValue !== value){
9722                 this.setRawValue(newValue);
9723                 return newValue;
9724             }
9725         }
9726         return value;
9727     },
9728   */
9729     preFocus : function(){
9730         
9731         if(this.selectOnFocus){
9732             this.inputEl().dom.select();
9733         }
9734     },
9735     filterKeys : function(e){
9736         var k = e.getKey();
9737         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9738             return;
9739         }
9740         var c = e.getCharCode(), cc = String.fromCharCode(c);
9741         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9742             return;
9743         }
9744         if(!this.maskRe.test(cc)){
9745             e.stopEvent();
9746         }
9747     },
9748      /**
9749      * Clear any invalid styles/messages for this field
9750      */
9751     clearInvalid : function(){
9752         
9753         if(!this.el || this.preventMark){ // not rendered
9754             return;
9755         }
9756         
9757         
9758         this.el.removeClass([this.invalidClass, 'is-invalid']);
9759         
9760         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9761             
9762             var feedback = this.el.select('.form-control-feedback', true).first();
9763             
9764             if(feedback){
9765                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9766             }
9767             
9768         }
9769         
9770         if(this.indicator){
9771             this.indicator.removeClass('visible');
9772             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9773         }
9774         
9775         this.fireEvent('valid', this);
9776     },
9777     
9778      /**
9779      * Mark this field as valid
9780      */
9781     markValid : function()
9782     {
9783         if(!this.el  || this.preventMark){ // not rendered...
9784             return;
9785         }
9786         
9787         this.el.removeClass([this.invalidClass, this.validClass]);
9788         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9789
9790         var feedback = this.el.select('.form-control-feedback', true).first();
9791             
9792         if(feedback){
9793             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9794         }
9795         
9796         if(this.indicator){
9797             this.indicator.removeClass('visible');
9798             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9799         }
9800         
9801         if(this.disabled){
9802             return;
9803         }
9804         
9805         if(this.allowBlank && !this.getRawValue().length){
9806             return;
9807         }
9808         if (Roo.bootstrap.version == 3) {
9809             this.el.addClass(this.validClass);
9810         } else {
9811             this.inputEl().addClass('is-valid');
9812         }
9813
9814         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9815             
9816             var feedback = this.el.select('.form-control-feedback', true).first();
9817             
9818             if(feedback){
9819                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9820                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9821             }
9822             
9823         }
9824         
9825         this.fireEvent('valid', this);
9826     },
9827     
9828      /**
9829      * Mark this field as invalid
9830      * @param {String} msg The validation message
9831      */
9832     markInvalid : function(msg)
9833     {
9834         if(!this.el  || this.preventMark){ // not rendered
9835             return;
9836         }
9837         
9838         this.el.removeClass([this.invalidClass, this.validClass]);
9839         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9840         
9841         var feedback = this.el.select('.form-control-feedback', true).first();
9842             
9843         if(feedback){
9844             this.el.select('.form-control-feedback', true).first().removeClass(
9845                     [this.invalidFeedbackClass, this.validFeedbackClass]);
9846         }
9847
9848         if(this.disabled){
9849             return;
9850         }
9851         
9852         if(this.allowBlank && !this.getRawValue().length){
9853             return;
9854         }
9855         
9856         if(this.indicator){
9857             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9858             this.indicator.addClass('visible');
9859         }
9860         if (Roo.bootstrap.version == 3) {
9861             this.el.addClass(this.invalidClass);
9862         } else {
9863             this.inputEl().addClass('is-invalid');
9864         }
9865         
9866         
9867         
9868         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9869             
9870             var feedback = this.el.select('.form-control-feedback', true).first();
9871             
9872             if(feedback){
9873                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9874                 
9875                 if(this.getValue().length || this.forceFeedback){
9876                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9877                 }
9878                 
9879             }
9880             
9881         }
9882         
9883         this.fireEvent('invalid', this, msg);
9884     },
9885     // private
9886     SafariOnKeyDown : function(event)
9887     {
9888         // this is a workaround for a password hang bug on chrome/ webkit.
9889         if (this.inputEl().dom.type != 'password') {
9890             return;
9891         }
9892         
9893         var isSelectAll = false;
9894         
9895         if(this.inputEl().dom.selectionEnd > 0){
9896             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9897         }
9898         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9899             event.preventDefault();
9900             this.setValue('');
9901             return;
9902         }
9903         
9904         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9905             
9906             event.preventDefault();
9907             // this is very hacky as keydown always get's upper case.
9908             //
9909             var cc = String.fromCharCode(event.getCharCode());
9910             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9911             
9912         }
9913     },
9914     adjustWidth : function(tag, w){
9915         tag = tag.toLowerCase();
9916         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9917             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9918                 if(tag == 'input'){
9919                     return w + 2;
9920                 }
9921                 if(tag == 'textarea'){
9922                     return w-2;
9923                 }
9924             }else if(Roo.isOpera){
9925                 if(tag == 'input'){
9926                     return w + 2;
9927                 }
9928                 if(tag == 'textarea'){
9929                     return w-2;
9930                 }
9931             }
9932         }
9933         return w;
9934     },
9935     
9936     setFieldLabel : function(v)
9937     {
9938         if(!this.rendered){
9939             return;
9940         }
9941         
9942         if(this.indicatorEl()){
9943             var ar = this.el.select('label > span',true);
9944             
9945             if (ar.elements.length) {
9946                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9947                 this.fieldLabel = v;
9948                 return;
9949             }
9950             
9951             var br = this.el.select('label',true);
9952             
9953             if(br.elements.length) {
9954                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9955                 this.fieldLabel = v;
9956                 return;
9957             }
9958             
9959             Roo.log('Cannot Found any of label > span || label in input');
9960             return;
9961         }
9962         
9963         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9964         this.fieldLabel = v;
9965         
9966         
9967     }
9968 });
9969
9970  
9971 /*
9972  * - LGPL
9973  *
9974  * Input
9975  * 
9976  */
9977
9978 /**
9979  * @class Roo.bootstrap.TextArea
9980  * @extends Roo.bootstrap.Input
9981  * Bootstrap TextArea class
9982  * @cfg {Number} cols Specifies the visible width of a text area
9983  * @cfg {Number} rows Specifies the visible number of lines in a text area
9984  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9985  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9986  * @cfg {string} html text
9987  * 
9988  * @constructor
9989  * Create a new TextArea
9990  * @param {Object} config The config object
9991  */
9992
9993 Roo.bootstrap.TextArea = function(config){
9994     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9995    
9996 };
9997
9998 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9999      
10000     cols : false,
10001     rows : 5,
10002     readOnly : false,
10003     warp : 'soft',
10004     resize : false,
10005     value: false,
10006     html: false,
10007     
10008     getAutoCreate : function(){
10009         
10010         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10011         
10012         var id = Roo.id();
10013         
10014         var cfg = {};
10015         
10016         if(this.inputType != 'hidden'){
10017             cfg.cls = 'form-group' //input-group
10018         }
10019         
10020         var input =  {
10021             tag: 'textarea',
10022             id : id,
10023             warp : this.warp,
10024             rows : this.rows,
10025             value : this.value || '',
10026             html: this.html || '',
10027             cls : 'form-control',
10028             placeholder : this.placeholder || '' 
10029             
10030         };
10031         
10032         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10033             input.maxLength = this.maxLength;
10034         }
10035         
10036         if(this.resize){
10037             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10038         }
10039         
10040         if(this.cols){
10041             input.cols = this.cols;
10042         }
10043         
10044         if (this.readOnly) {
10045             input.readonly = true;
10046         }
10047         
10048         if (this.name) {
10049             input.name = this.name;
10050         }
10051         
10052         if (this.size) {
10053             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10054         }
10055         
10056         var settings=this;
10057         ['xs','sm','md','lg'].map(function(size){
10058             if (settings[size]) {
10059                 cfg.cls += ' col-' + size + '-' + settings[size];
10060             }
10061         });
10062         
10063         var inputblock = input;
10064         
10065         if(this.hasFeedback && !this.allowBlank){
10066             
10067             var feedback = {
10068                 tag: 'span',
10069                 cls: 'glyphicon form-control-feedback'
10070             };
10071
10072             inputblock = {
10073                 cls : 'has-feedback',
10074                 cn :  [
10075                     input,
10076                     feedback
10077                 ] 
10078             };  
10079         }
10080         
10081         
10082         if (this.before || this.after) {
10083             
10084             inputblock = {
10085                 cls : 'input-group',
10086                 cn :  [] 
10087             };
10088             if (this.before) {
10089                 inputblock.cn.push({
10090                     tag :'span',
10091                     cls : 'input-group-addon',
10092                     html : this.before
10093                 });
10094             }
10095             
10096             inputblock.cn.push(input);
10097             
10098             if(this.hasFeedback && !this.allowBlank){
10099                 inputblock.cls += ' has-feedback';
10100                 inputblock.cn.push(feedback);
10101             }
10102             
10103             if (this.after) {
10104                 inputblock.cn.push({
10105                     tag :'span',
10106                     cls : 'input-group-addon',
10107                     html : this.after
10108                 });
10109             }
10110             
10111         }
10112         
10113         if (align ==='left' && this.fieldLabel.length) {
10114             cfg.cn = [
10115                 {
10116                     tag: 'label',
10117                     'for' :  id,
10118                     cls : 'control-label',
10119                     html : this.fieldLabel
10120                 },
10121                 {
10122                     cls : "",
10123                     cn: [
10124                         inputblock
10125                     ]
10126                 }
10127
10128             ];
10129             
10130             if(this.labelWidth > 12){
10131                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10132             }
10133
10134             if(this.labelWidth < 13 && this.labelmd == 0){
10135                 this.labelmd = this.labelWidth;
10136             }
10137
10138             if(this.labellg > 0){
10139                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10140                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10141             }
10142
10143             if(this.labelmd > 0){
10144                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10145                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10146             }
10147
10148             if(this.labelsm > 0){
10149                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10150                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10151             }
10152
10153             if(this.labelxs > 0){
10154                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10155                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10156             }
10157             
10158         } else if ( this.fieldLabel.length) {
10159             cfg.cn = [
10160
10161                {
10162                    tag: 'label',
10163                    //cls : 'input-group-addon',
10164                    html : this.fieldLabel
10165
10166                },
10167
10168                inputblock
10169
10170            ];
10171
10172         } else {
10173
10174             cfg.cn = [
10175
10176                 inputblock
10177
10178             ];
10179                 
10180         }
10181         
10182         if (this.disabled) {
10183             input.disabled=true;
10184         }
10185         
10186         return cfg;
10187         
10188     },
10189     /**
10190      * return the real textarea element.
10191      */
10192     inputEl: function ()
10193     {
10194         return this.el.select('textarea.form-control',true).first();
10195     },
10196     
10197     /**
10198      * Clear any invalid styles/messages for this field
10199      */
10200     clearInvalid : function()
10201     {
10202         
10203         if(!this.el || this.preventMark){ // not rendered
10204             return;
10205         }
10206         
10207         var label = this.el.select('label', true).first();
10208         var icon = this.el.select('i.fa-star', true).first();
10209         
10210         if(label && icon){
10211             icon.remove();
10212         }
10213         this.el.removeClass( this.validClass);
10214         this.inputEl().removeClass('is-invalid');
10215          
10216         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10217             
10218             var feedback = this.el.select('.form-control-feedback', true).first();
10219             
10220             if(feedback){
10221                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10222             }
10223             
10224         }
10225         
10226         this.fireEvent('valid', this);
10227     },
10228     
10229      /**
10230      * Mark this field as valid
10231      */
10232     markValid : function()
10233     {
10234         if(!this.el  || this.preventMark){ // not rendered
10235             return;
10236         }
10237         
10238         this.el.removeClass([this.invalidClass, this.validClass]);
10239         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10240         
10241         var feedback = this.el.select('.form-control-feedback', true).first();
10242             
10243         if(feedback){
10244             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10245         }
10246
10247         if(this.disabled || this.allowBlank){
10248             return;
10249         }
10250         
10251         var label = this.el.select('label', true).first();
10252         var icon = this.el.select('i.fa-star', true).first();
10253         
10254         if(label && icon){
10255             icon.remove();
10256         }
10257         if (Roo.bootstrap.version == 3) {
10258             this.el.addClass(this.validClass);
10259         } else {
10260             this.inputEl().addClass('is-valid');
10261         }
10262         
10263         
10264         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10265             
10266             var feedback = this.el.select('.form-control-feedback', true).first();
10267             
10268             if(feedback){
10269                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10270                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10271             }
10272             
10273         }
10274         
10275         this.fireEvent('valid', this);
10276     },
10277     
10278      /**
10279      * Mark this field as invalid
10280      * @param {String} msg The validation message
10281      */
10282     markInvalid : function(msg)
10283     {
10284         if(!this.el  || this.preventMark){ // not rendered
10285             return;
10286         }
10287         
10288         this.el.removeClass([this.invalidClass, this.validClass]);
10289         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10290         
10291         var feedback = this.el.select('.form-control-feedback', true).first();
10292             
10293         if(feedback){
10294             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10295         }
10296
10297         if(this.disabled || this.allowBlank){
10298             return;
10299         }
10300         
10301         var label = this.el.select('label', true).first();
10302         var icon = this.el.select('i.fa-star', true).first();
10303         
10304         if(!this.getValue().length && label && !icon){
10305             this.el.createChild({
10306                 tag : 'i',
10307                 cls : 'text-danger fa fa-lg fa-star',
10308                 tooltip : 'This field is required',
10309                 style : 'margin-right:5px;'
10310             }, label, true);
10311         }
10312         
10313         if (Roo.bootstrap.version == 3) {
10314             this.el.addClass(this.invalidClass);
10315         } else {
10316             this.inputEl().addClass('is-invalid');
10317         }
10318         
10319         // fixme ... this may be depricated need to test..
10320         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10321             
10322             var feedback = this.el.select('.form-control-feedback', true).first();
10323             
10324             if(feedback){
10325                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10326                 
10327                 if(this.getValue().length || this.forceFeedback){
10328                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10329                 }
10330                 
10331             }
10332             
10333         }
10334         
10335         this.fireEvent('invalid', this, msg);
10336     }
10337 });
10338
10339  
10340 /*
10341  * - LGPL
10342  *
10343  * trigger field - base class for combo..
10344  * 
10345  */
10346  
10347 /**
10348  * @class Roo.bootstrap.TriggerField
10349  * @extends Roo.bootstrap.Input
10350  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10351  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10352  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10353  * for which you can provide a custom implementation.  For example:
10354  * <pre><code>
10355 var trigger = new Roo.bootstrap.TriggerField();
10356 trigger.onTriggerClick = myTriggerFn;
10357 trigger.applyTo('my-field');
10358 </code></pre>
10359  *
10360  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10361  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10362  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10363  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10364  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10365
10366  * @constructor
10367  * Create a new TriggerField.
10368  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10369  * to the base TextField)
10370  */
10371 Roo.bootstrap.TriggerField = function(config){
10372     this.mimicing = false;
10373     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10374 };
10375
10376 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10377     /**
10378      * @cfg {String} triggerClass A CSS class to apply to the trigger
10379      */
10380      /**
10381      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10382      */
10383     hideTrigger:false,
10384
10385     /**
10386      * @cfg {Boolean} removable (true|false) special filter default false
10387      */
10388     removable : false,
10389     
10390     /** @cfg {Boolean} grow @hide */
10391     /** @cfg {Number} growMin @hide */
10392     /** @cfg {Number} growMax @hide */
10393
10394     /**
10395      * @hide 
10396      * @method
10397      */
10398     autoSize: Roo.emptyFn,
10399     // private
10400     monitorTab : true,
10401     // private
10402     deferHeight : true,
10403
10404     
10405     actionMode : 'wrap',
10406     
10407     caret : false,
10408     
10409     
10410     getAutoCreate : function(){
10411        
10412         var align = this.labelAlign || this.parentLabelAlign();
10413         
10414         var id = Roo.id();
10415         
10416         var cfg = {
10417             cls: 'form-group' //input-group
10418         };
10419         
10420         
10421         var input =  {
10422             tag: 'input',
10423             id : id,
10424             type : this.inputType,
10425             cls : 'form-control',
10426             autocomplete: 'new-password',
10427             placeholder : this.placeholder || '' 
10428             
10429         };
10430         if (this.name) {
10431             input.name = this.name;
10432         }
10433         if (this.size) {
10434             input.cls += ' input-' + this.size;
10435         }
10436         
10437         if (this.disabled) {
10438             input.disabled=true;
10439         }
10440         
10441         var inputblock = input;
10442         
10443         if(this.hasFeedback && !this.allowBlank){
10444             
10445             var feedback = {
10446                 tag: 'span',
10447                 cls: 'glyphicon form-control-feedback'
10448             };
10449             
10450             if(this.removable && !this.editable && !this.tickable){
10451                 inputblock = {
10452                     cls : 'has-feedback',
10453                     cn :  [
10454                         inputblock,
10455                         {
10456                             tag: 'button',
10457                             html : 'x',
10458                             cls : 'roo-combo-removable-btn close'
10459                         },
10460                         feedback
10461                     ] 
10462                 };
10463             } else {
10464                 inputblock = {
10465                     cls : 'has-feedback',
10466                     cn :  [
10467                         inputblock,
10468                         feedback
10469                     ] 
10470                 };
10471             }
10472
10473         } else {
10474             if(this.removable && !this.editable && !this.tickable){
10475                 inputblock = {
10476                     cls : 'roo-removable',
10477                     cn :  [
10478                         inputblock,
10479                         {
10480                             tag: 'button',
10481                             html : 'x',
10482                             cls : 'roo-combo-removable-btn close'
10483                         }
10484                     ] 
10485                 };
10486             }
10487         }
10488         
10489         if (this.before || this.after) {
10490             
10491             inputblock = {
10492                 cls : 'input-group',
10493                 cn :  [] 
10494             };
10495             if (this.before) {
10496                 inputblock.cn.push({
10497                     tag :'span',
10498                     cls : 'input-group-addon input-group-prepend input-group-text',
10499                     html : this.before
10500                 });
10501             }
10502             
10503             inputblock.cn.push(input);
10504             
10505             if(this.hasFeedback && !this.allowBlank){
10506                 inputblock.cls += ' has-feedback';
10507                 inputblock.cn.push(feedback);
10508             }
10509             
10510             if (this.after) {
10511                 inputblock.cn.push({
10512                     tag :'span',
10513                     cls : 'input-group-addon input-group-append input-group-text',
10514                     html : this.after
10515                 });
10516             }
10517             
10518         };
10519         
10520       
10521         
10522         var ibwrap = inputblock;
10523         
10524         if(this.multiple){
10525             ibwrap = {
10526                 tag: 'ul',
10527                 cls: 'roo-select2-choices',
10528                 cn:[
10529                     {
10530                         tag: 'li',
10531                         cls: 'roo-select2-search-field',
10532                         cn: [
10533
10534                             inputblock
10535                         ]
10536                     }
10537                 ]
10538             };
10539                 
10540         }
10541         
10542         var combobox = {
10543             cls: 'roo-select2-container input-group',
10544             cn: [
10545                  {
10546                     tag: 'input',
10547                     type : 'hidden',
10548                     cls: 'form-hidden-field'
10549                 },
10550                 ibwrap
10551             ]
10552         };
10553         
10554         if(!this.multiple && this.showToggleBtn){
10555             
10556             var caret = {
10557                         tag: 'span',
10558                         cls: 'caret'
10559              };
10560             if (this.caret != false) {
10561                 caret = {
10562                      tag: 'i',
10563                      cls: 'fa fa-' + this.caret
10564                 };
10565                 
10566             }
10567             
10568             combobox.cn.push({
10569                 tag :'span',
10570                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10571                 cn : [
10572                     caret,
10573                     {
10574                         tag: 'span',
10575                         cls: 'combobox-clear',
10576                         cn  : [
10577                             {
10578                                 tag : 'i',
10579                                 cls: 'icon-remove'
10580                             }
10581                         ]
10582                     }
10583                 ]
10584
10585             })
10586         }
10587         
10588         if(this.multiple){
10589             combobox.cls += ' roo-select2-container-multi';
10590         }
10591          var indicator = {
10592             tag : 'i',
10593             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10594             tooltip : 'This field is required'
10595         };
10596         if (Roo.bootstrap.version == 4) {
10597             indicator = {
10598                 tag : 'i',
10599                 style : 'display:none'
10600             };
10601         }
10602         
10603         
10604         if (align ==='left' && this.fieldLabel.length) {
10605             
10606             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10607
10608             cfg.cn = [
10609                 indicator,
10610                 {
10611                     tag: 'label',
10612                     'for' :  id,
10613                     cls : 'control-label',
10614                     html : this.fieldLabel
10615
10616                 },
10617                 {
10618                     cls : "", 
10619                     cn: [
10620                         combobox
10621                     ]
10622                 }
10623
10624             ];
10625             
10626             var labelCfg = cfg.cn[1];
10627             var contentCfg = cfg.cn[2];
10628             
10629             if(this.indicatorpos == 'right'){
10630                 cfg.cn = [
10631                     {
10632                         tag: 'label',
10633                         'for' :  id,
10634                         cls : 'control-label',
10635                         cn : [
10636                             {
10637                                 tag : 'span',
10638                                 html : this.fieldLabel
10639                             },
10640                             indicator
10641                         ]
10642                     },
10643                     {
10644                         cls : "", 
10645                         cn: [
10646                             combobox
10647                         ]
10648                     }
10649
10650                 ];
10651                 
10652                 labelCfg = cfg.cn[0];
10653                 contentCfg = cfg.cn[1];
10654             }
10655             
10656             if(this.labelWidth > 12){
10657                 labelCfg.style = "width: " + this.labelWidth + 'px';
10658             }
10659             
10660             if(this.labelWidth < 13 && this.labelmd == 0){
10661                 this.labelmd = this.labelWidth;
10662             }
10663             
10664             if(this.labellg > 0){
10665                 labelCfg.cls += ' col-lg-' + this.labellg;
10666                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10667             }
10668             
10669             if(this.labelmd > 0){
10670                 labelCfg.cls += ' col-md-' + this.labelmd;
10671                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10672             }
10673             
10674             if(this.labelsm > 0){
10675                 labelCfg.cls += ' col-sm-' + this.labelsm;
10676                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10677             }
10678             
10679             if(this.labelxs > 0){
10680                 labelCfg.cls += ' col-xs-' + this.labelxs;
10681                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10682             }
10683             
10684         } else if ( this.fieldLabel.length) {
10685 //                Roo.log(" label");
10686             cfg.cn = [
10687                 indicator,
10688                {
10689                    tag: 'label',
10690                    //cls : 'input-group-addon',
10691                    html : this.fieldLabel
10692
10693                },
10694
10695                combobox
10696
10697             ];
10698             
10699             if(this.indicatorpos == 'right'){
10700                 
10701                 cfg.cn = [
10702                     {
10703                        tag: 'label',
10704                        cn : [
10705                            {
10706                                tag : 'span',
10707                                html : this.fieldLabel
10708                            },
10709                            indicator
10710                        ]
10711
10712                     },
10713                     combobox
10714
10715                 ];
10716
10717             }
10718
10719         } else {
10720             
10721 //                Roo.log(" no label && no align");
10722                 cfg = combobox
10723                      
10724                 
10725         }
10726         
10727         var settings=this;
10728         ['xs','sm','md','lg'].map(function(size){
10729             if (settings[size]) {
10730                 cfg.cls += ' col-' + size + '-' + settings[size];
10731             }
10732         });
10733         
10734         return cfg;
10735         
10736     },
10737     
10738     
10739     
10740     // private
10741     onResize : function(w, h){
10742 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10743 //        if(typeof w == 'number'){
10744 //            var x = w - this.trigger.getWidth();
10745 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10746 //            this.trigger.setStyle('left', x+'px');
10747 //        }
10748     },
10749
10750     // private
10751     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10752
10753     // private
10754     getResizeEl : function(){
10755         return this.inputEl();
10756     },
10757
10758     // private
10759     getPositionEl : function(){
10760         return this.inputEl();
10761     },
10762
10763     // private
10764     alignErrorIcon : function(){
10765         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10766     },
10767
10768     // private
10769     initEvents : function(){
10770         
10771         this.createList();
10772         
10773         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10774         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10775         if(!this.multiple && this.showToggleBtn){
10776             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10777             if(this.hideTrigger){
10778                 this.trigger.setDisplayed(false);
10779             }
10780             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10781         }
10782         
10783         if(this.multiple){
10784             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10785         }
10786         
10787         if(this.removable && !this.editable && !this.tickable){
10788             var close = this.closeTriggerEl();
10789             
10790             if(close){
10791                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10792                 close.on('click', this.removeBtnClick, this, close);
10793             }
10794         }
10795         
10796         //this.trigger.addClassOnOver('x-form-trigger-over');
10797         //this.trigger.addClassOnClick('x-form-trigger-click');
10798         
10799         //if(!this.width){
10800         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10801         //}
10802     },
10803     
10804     closeTriggerEl : function()
10805     {
10806         var close = this.el.select('.roo-combo-removable-btn', true).first();
10807         return close ? close : false;
10808     },
10809     
10810     removeBtnClick : function(e, h, el)
10811     {
10812         e.preventDefault();
10813         
10814         if(this.fireEvent("remove", this) !== false){
10815             this.reset();
10816             this.fireEvent("afterremove", this)
10817         }
10818     },
10819     
10820     createList : function()
10821     {
10822         this.list = Roo.get(document.body).createChild({
10823             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10824             cls: 'typeahead typeahead-long dropdown-menu',
10825             style: 'display:none'
10826         });
10827         
10828         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10829         
10830     },
10831
10832     // private
10833     initTrigger : function(){
10834        
10835     },
10836
10837     // private
10838     onDestroy : function(){
10839         if(this.trigger){
10840             this.trigger.removeAllListeners();
10841           //  this.trigger.remove();
10842         }
10843         //if(this.wrap){
10844         //    this.wrap.remove();
10845         //}
10846         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10847     },
10848
10849     // private
10850     onFocus : function(){
10851         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10852         /*
10853         if(!this.mimicing){
10854             this.wrap.addClass('x-trigger-wrap-focus');
10855             this.mimicing = true;
10856             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10857             if(this.monitorTab){
10858                 this.el.on("keydown", this.checkTab, this);
10859             }
10860         }
10861         */
10862     },
10863
10864     // private
10865     checkTab : function(e){
10866         if(e.getKey() == e.TAB){
10867             this.triggerBlur();
10868         }
10869     },
10870
10871     // private
10872     onBlur : function(){
10873         // do nothing
10874     },
10875
10876     // private
10877     mimicBlur : function(e, t){
10878         /*
10879         if(!this.wrap.contains(t) && this.validateBlur()){
10880             this.triggerBlur();
10881         }
10882         */
10883     },
10884
10885     // private
10886     triggerBlur : function(){
10887         this.mimicing = false;
10888         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10889         if(this.monitorTab){
10890             this.el.un("keydown", this.checkTab, this);
10891         }
10892         //this.wrap.removeClass('x-trigger-wrap-focus');
10893         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10894     },
10895
10896     // private
10897     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10898     validateBlur : function(e, t){
10899         return true;
10900     },
10901
10902     // private
10903     onDisable : function(){
10904         this.inputEl().dom.disabled = true;
10905         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10906         //if(this.wrap){
10907         //    this.wrap.addClass('x-item-disabled');
10908         //}
10909     },
10910
10911     // private
10912     onEnable : function(){
10913         this.inputEl().dom.disabled = false;
10914         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10915         //if(this.wrap){
10916         //    this.el.removeClass('x-item-disabled');
10917         //}
10918     },
10919
10920     // private
10921     onShow : function(){
10922         var ae = this.getActionEl();
10923         
10924         if(ae){
10925             ae.dom.style.display = '';
10926             ae.dom.style.visibility = 'visible';
10927         }
10928     },
10929
10930     // private
10931     
10932     onHide : function(){
10933         var ae = this.getActionEl();
10934         ae.dom.style.display = 'none';
10935     },
10936
10937     /**
10938      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10939      * by an implementing function.
10940      * @method
10941      * @param {EventObject} e
10942      */
10943     onTriggerClick : Roo.emptyFn
10944 });
10945  /*
10946  * Based on:
10947  * Ext JS Library 1.1.1
10948  * Copyright(c) 2006-2007, Ext JS, LLC.
10949  *
10950  * Originally Released Under LGPL - original licence link has changed is not relivant.
10951  *
10952  * Fork - LGPL
10953  * <script type="text/javascript">
10954  */
10955
10956
10957 /**
10958  * @class Roo.data.SortTypes
10959  * @singleton
10960  * Defines the default sorting (casting?) comparison functions used when sorting data.
10961  */
10962 Roo.data.SortTypes = {
10963     /**
10964      * Default sort that does nothing
10965      * @param {Mixed} s The value being converted
10966      * @return {Mixed} The comparison value
10967      */
10968     none : function(s){
10969         return s;
10970     },
10971     
10972     /**
10973      * The regular expression used to strip tags
10974      * @type {RegExp}
10975      * @property
10976      */
10977     stripTagsRE : /<\/?[^>]+>/gi,
10978     
10979     /**
10980      * Strips all HTML tags to sort on text only
10981      * @param {Mixed} s The value being converted
10982      * @return {String} The comparison value
10983      */
10984     asText : function(s){
10985         return String(s).replace(this.stripTagsRE, "");
10986     },
10987     
10988     /**
10989      * Strips all HTML tags to sort on text only - Case insensitive
10990      * @param {Mixed} s The value being converted
10991      * @return {String} The comparison value
10992      */
10993     asUCText : function(s){
10994         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10995     },
10996     
10997     /**
10998      * Case insensitive string
10999      * @param {Mixed} s The value being converted
11000      * @return {String} The comparison value
11001      */
11002     asUCString : function(s) {
11003         return String(s).toUpperCase();
11004     },
11005     
11006     /**
11007      * Date sorting
11008      * @param {Mixed} s The value being converted
11009      * @return {Number} The comparison value
11010      */
11011     asDate : function(s) {
11012         if(!s){
11013             return 0;
11014         }
11015         if(s instanceof Date){
11016             return s.getTime();
11017         }
11018         return Date.parse(String(s));
11019     },
11020     
11021     /**
11022      * Float sorting
11023      * @param {Mixed} s The value being converted
11024      * @return {Float} The comparison value
11025      */
11026     asFloat : function(s) {
11027         var val = parseFloat(String(s).replace(/,/g, ""));
11028         if(isNaN(val)) {
11029             val = 0;
11030         }
11031         return val;
11032     },
11033     
11034     /**
11035      * Integer sorting
11036      * @param {Mixed} s The value being converted
11037      * @return {Number} The comparison value
11038      */
11039     asInt : function(s) {
11040         var val = parseInt(String(s).replace(/,/g, ""));
11041         if(isNaN(val)) {
11042             val = 0;
11043         }
11044         return val;
11045     }
11046 };/*
11047  * Based on:
11048  * Ext JS Library 1.1.1
11049  * Copyright(c) 2006-2007, Ext JS, LLC.
11050  *
11051  * Originally Released Under LGPL - original licence link has changed is not relivant.
11052  *
11053  * Fork - LGPL
11054  * <script type="text/javascript">
11055  */
11056
11057 /**
11058 * @class Roo.data.Record
11059  * Instances of this class encapsulate both record <em>definition</em> information, and record
11060  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11061  * to access Records cached in an {@link Roo.data.Store} object.<br>
11062  * <p>
11063  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11064  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11065  * objects.<br>
11066  * <p>
11067  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11068  * @constructor
11069  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11070  * {@link #create}. The parameters are the same.
11071  * @param {Array} data An associative Array of data values keyed by the field name.
11072  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11073  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11074  * not specified an integer id is generated.
11075  */
11076 Roo.data.Record = function(data, id){
11077     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11078     this.data = data;
11079 };
11080
11081 /**
11082  * Generate a constructor for a specific record layout.
11083  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11084  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11085  * Each field definition object may contain the following properties: <ul>
11086  * <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,
11087  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11088  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11089  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11090  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11091  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11092  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11093  * this may be omitted.</p></li>
11094  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11095  * <ul><li>auto (Default, implies no conversion)</li>
11096  * <li>string</li>
11097  * <li>int</li>
11098  * <li>float</li>
11099  * <li>boolean</li>
11100  * <li>date</li></ul></p></li>
11101  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11102  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11103  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11104  * by the Reader into an object that will be stored in the Record. It is passed the
11105  * following parameters:<ul>
11106  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11107  * </ul></p></li>
11108  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11109  * </ul>
11110  * <br>usage:<br><pre><code>
11111 var TopicRecord = Roo.data.Record.create(
11112     {name: 'title', mapping: 'topic_title'},
11113     {name: 'author', mapping: 'username'},
11114     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11115     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11116     {name: 'lastPoster', mapping: 'user2'},
11117     {name: 'excerpt', mapping: 'post_text'}
11118 );
11119
11120 var myNewRecord = new TopicRecord({
11121     title: 'Do my job please',
11122     author: 'noobie',
11123     totalPosts: 1,
11124     lastPost: new Date(),
11125     lastPoster: 'Animal',
11126     excerpt: 'No way dude!'
11127 });
11128 myStore.add(myNewRecord);
11129 </code></pre>
11130  * @method create
11131  * @static
11132  */
11133 Roo.data.Record.create = function(o){
11134     var f = function(){
11135         f.superclass.constructor.apply(this, arguments);
11136     };
11137     Roo.extend(f, Roo.data.Record);
11138     var p = f.prototype;
11139     p.fields = new Roo.util.MixedCollection(false, function(field){
11140         return field.name;
11141     });
11142     for(var i = 0, len = o.length; i < len; i++){
11143         p.fields.add(new Roo.data.Field(o[i]));
11144     }
11145     f.getField = function(name){
11146         return p.fields.get(name);  
11147     };
11148     return f;
11149 };
11150
11151 Roo.data.Record.AUTO_ID = 1000;
11152 Roo.data.Record.EDIT = 'edit';
11153 Roo.data.Record.REJECT = 'reject';
11154 Roo.data.Record.COMMIT = 'commit';
11155
11156 Roo.data.Record.prototype = {
11157     /**
11158      * Readonly flag - true if this record has been modified.
11159      * @type Boolean
11160      */
11161     dirty : false,
11162     editing : false,
11163     error: null,
11164     modified: null,
11165
11166     // private
11167     join : function(store){
11168         this.store = store;
11169     },
11170
11171     /**
11172      * Set the named field to the specified value.
11173      * @param {String} name The name of the field to set.
11174      * @param {Object} value The value to set the field to.
11175      */
11176     set : function(name, value){
11177         if(this.data[name] == value){
11178             return;
11179         }
11180         this.dirty = true;
11181         if(!this.modified){
11182             this.modified = {};
11183         }
11184         if(typeof this.modified[name] == 'undefined'){
11185             this.modified[name] = this.data[name];
11186         }
11187         this.data[name] = value;
11188         if(!this.editing && this.store){
11189             this.store.afterEdit(this);
11190         }       
11191     },
11192
11193     /**
11194      * Get the value of the named field.
11195      * @param {String} name The name of the field to get the value of.
11196      * @return {Object} The value of the field.
11197      */
11198     get : function(name){
11199         return this.data[name]; 
11200     },
11201
11202     // private
11203     beginEdit : function(){
11204         this.editing = true;
11205         this.modified = {}; 
11206     },
11207
11208     // private
11209     cancelEdit : function(){
11210         this.editing = false;
11211         delete this.modified;
11212     },
11213
11214     // private
11215     endEdit : function(){
11216         this.editing = false;
11217         if(this.dirty && this.store){
11218             this.store.afterEdit(this);
11219         }
11220     },
11221
11222     /**
11223      * Usually called by the {@link Roo.data.Store} which owns the Record.
11224      * Rejects all changes made to the Record since either creation, or the last commit operation.
11225      * Modified fields are reverted to their original values.
11226      * <p>
11227      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11228      * of reject operations.
11229      */
11230     reject : function(){
11231         var m = this.modified;
11232         for(var n in m){
11233             if(typeof m[n] != "function"){
11234                 this.data[n] = m[n];
11235             }
11236         }
11237         this.dirty = false;
11238         delete this.modified;
11239         this.editing = false;
11240         if(this.store){
11241             this.store.afterReject(this);
11242         }
11243     },
11244
11245     /**
11246      * Usually called by the {@link Roo.data.Store} which owns the Record.
11247      * Commits all changes made to the Record since either creation, or the last commit operation.
11248      * <p>
11249      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11250      * of commit operations.
11251      */
11252     commit : function(){
11253         this.dirty = false;
11254         delete this.modified;
11255         this.editing = false;
11256         if(this.store){
11257             this.store.afterCommit(this);
11258         }
11259     },
11260
11261     // private
11262     hasError : function(){
11263         return this.error != null;
11264     },
11265
11266     // private
11267     clearError : function(){
11268         this.error = null;
11269     },
11270
11271     /**
11272      * Creates a copy of this record.
11273      * @param {String} id (optional) A new record id if you don't want to use this record's id
11274      * @return {Record}
11275      */
11276     copy : function(newId) {
11277         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11278     }
11279 };/*
11280  * Based on:
11281  * Ext JS Library 1.1.1
11282  * Copyright(c) 2006-2007, Ext JS, LLC.
11283  *
11284  * Originally Released Under LGPL - original licence link has changed is not relivant.
11285  *
11286  * Fork - LGPL
11287  * <script type="text/javascript">
11288  */
11289
11290
11291
11292 /**
11293  * @class Roo.data.Store
11294  * @extends Roo.util.Observable
11295  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11296  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11297  * <p>
11298  * 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
11299  * has no knowledge of the format of the data returned by the Proxy.<br>
11300  * <p>
11301  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11302  * instances from the data object. These records are cached and made available through accessor functions.
11303  * @constructor
11304  * Creates a new Store.
11305  * @param {Object} config A config object containing the objects needed for the Store to access data,
11306  * and read the data into Records.
11307  */
11308 Roo.data.Store = function(config){
11309     this.data = new Roo.util.MixedCollection(false);
11310     this.data.getKey = function(o){
11311         return o.id;
11312     };
11313     this.baseParams = {};
11314     // private
11315     this.paramNames = {
11316         "start" : "start",
11317         "limit" : "limit",
11318         "sort" : "sort",
11319         "dir" : "dir",
11320         "multisort" : "_multisort"
11321     };
11322
11323     if(config && config.data){
11324         this.inlineData = config.data;
11325         delete config.data;
11326     }
11327
11328     Roo.apply(this, config);
11329     
11330     if(this.reader){ // reader passed
11331         this.reader = Roo.factory(this.reader, Roo.data);
11332         this.reader.xmodule = this.xmodule || false;
11333         if(!this.recordType){
11334             this.recordType = this.reader.recordType;
11335         }
11336         if(this.reader.onMetaChange){
11337             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11338         }
11339     }
11340
11341     if(this.recordType){
11342         this.fields = this.recordType.prototype.fields;
11343     }
11344     this.modified = [];
11345
11346     this.addEvents({
11347         /**
11348          * @event datachanged
11349          * Fires when the data cache has changed, and a widget which is using this Store
11350          * as a Record cache should refresh its view.
11351          * @param {Store} this
11352          */
11353         datachanged : true,
11354         /**
11355          * @event metachange
11356          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11357          * @param {Store} this
11358          * @param {Object} meta The JSON metadata
11359          */
11360         metachange : true,
11361         /**
11362          * @event add
11363          * Fires when Records have been added to the Store
11364          * @param {Store} this
11365          * @param {Roo.data.Record[]} records The array of Records added
11366          * @param {Number} index The index at which the record(s) were added
11367          */
11368         add : true,
11369         /**
11370          * @event remove
11371          * Fires when a Record has been removed from the Store
11372          * @param {Store} this
11373          * @param {Roo.data.Record} record The Record that was removed
11374          * @param {Number} index The index at which the record was removed
11375          */
11376         remove : true,
11377         /**
11378          * @event update
11379          * Fires when a Record has been updated
11380          * @param {Store} this
11381          * @param {Roo.data.Record} record The Record that was updated
11382          * @param {String} operation The update operation being performed.  Value may be one of:
11383          * <pre><code>
11384  Roo.data.Record.EDIT
11385  Roo.data.Record.REJECT
11386  Roo.data.Record.COMMIT
11387          * </code></pre>
11388          */
11389         update : true,
11390         /**
11391          * @event clear
11392          * Fires when the data cache has been cleared.
11393          * @param {Store} this
11394          */
11395         clear : true,
11396         /**
11397          * @event beforeload
11398          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11399          * the load action will be canceled.
11400          * @param {Store} this
11401          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11402          */
11403         beforeload : true,
11404         /**
11405          * @event beforeloadadd
11406          * Fires after a new set of Records has been loaded.
11407          * @param {Store} this
11408          * @param {Roo.data.Record[]} records The Records that were loaded
11409          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11410          */
11411         beforeloadadd : true,
11412         /**
11413          * @event load
11414          * Fires after a new set of Records has been loaded, before they are added to the store.
11415          * @param {Store} this
11416          * @param {Roo.data.Record[]} records The Records that were loaded
11417          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11418          * @params {Object} return from reader
11419          */
11420         load : true,
11421         /**
11422          * @event loadexception
11423          * Fires if an exception occurs in the Proxy during loading.
11424          * Called with the signature of the Proxy's "loadexception" event.
11425          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11426          * 
11427          * @param {Proxy} 
11428          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11429          * @param {Object} load options 
11430          * @param {Object} jsonData from your request (normally this contains the Exception)
11431          */
11432         loadexception : true
11433     });
11434     
11435     if(this.proxy){
11436         this.proxy = Roo.factory(this.proxy, Roo.data);
11437         this.proxy.xmodule = this.xmodule || false;
11438         this.relayEvents(this.proxy,  ["loadexception"]);
11439     }
11440     this.sortToggle = {};
11441     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11442
11443     Roo.data.Store.superclass.constructor.call(this);
11444
11445     if(this.inlineData){
11446         this.loadData(this.inlineData);
11447         delete this.inlineData;
11448     }
11449 };
11450
11451 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11452      /**
11453     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11454     * without a remote query - used by combo/forms at present.
11455     */
11456     
11457     /**
11458     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11459     */
11460     /**
11461     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11462     */
11463     /**
11464     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11465     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11466     */
11467     /**
11468     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11469     * on any HTTP request
11470     */
11471     /**
11472     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11473     */
11474     /**
11475     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11476     */
11477     multiSort: false,
11478     /**
11479     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11480     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11481     */
11482     remoteSort : false,
11483
11484     /**
11485     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11486      * loaded or when a record is removed. (defaults to false).
11487     */
11488     pruneModifiedRecords : false,
11489
11490     // private
11491     lastOptions : null,
11492
11493     /**
11494      * Add Records to the Store and fires the add event.
11495      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11496      */
11497     add : function(records){
11498         records = [].concat(records);
11499         for(var i = 0, len = records.length; i < len; i++){
11500             records[i].join(this);
11501         }
11502         var index = this.data.length;
11503         this.data.addAll(records);
11504         this.fireEvent("add", this, records, index);
11505     },
11506
11507     /**
11508      * Remove a Record from the Store and fires the remove event.
11509      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11510      */
11511     remove : function(record){
11512         var index = this.data.indexOf(record);
11513         this.data.removeAt(index);
11514  
11515         if(this.pruneModifiedRecords){
11516             this.modified.remove(record);
11517         }
11518         this.fireEvent("remove", this, record, index);
11519     },
11520
11521     /**
11522      * Remove all Records from the Store and fires the clear event.
11523      */
11524     removeAll : function(){
11525         this.data.clear();
11526         if(this.pruneModifiedRecords){
11527             this.modified = [];
11528         }
11529         this.fireEvent("clear", this);
11530     },
11531
11532     /**
11533      * Inserts Records to the Store at the given index and fires the add event.
11534      * @param {Number} index The start index at which to insert the passed Records.
11535      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11536      */
11537     insert : function(index, records){
11538         records = [].concat(records);
11539         for(var i = 0, len = records.length; i < len; i++){
11540             this.data.insert(index, records[i]);
11541             records[i].join(this);
11542         }
11543         this.fireEvent("add", this, records, index);
11544     },
11545
11546     /**
11547      * Get the index within the cache of the passed Record.
11548      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11549      * @return {Number} The index of the passed Record. Returns -1 if not found.
11550      */
11551     indexOf : function(record){
11552         return this.data.indexOf(record);
11553     },
11554
11555     /**
11556      * Get the index within the cache of the Record with the passed id.
11557      * @param {String} id The id of the Record to find.
11558      * @return {Number} The index of the Record. Returns -1 if not found.
11559      */
11560     indexOfId : function(id){
11561         return this.data.indexOfKey(id);
11562     },
11563
11564     /**
11565      * Get the Record with the specified id.
11566      * @param {String} id The id of the Record to find.
11567      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11568      */
11569     getById : function(id){
11570         return this.data.key(id);
11571     },
11572
11573     /**
11574      * Get the Record at the specified index.
11575      * @param {Number} index The index of the Record to find.
11576      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11577      */
11578     getAt : function(index){
11579         return this.data.itemAt(index);
11580     },
11581
11582     /**
11583      * Returns a range of Records between specified indices.
11584      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11585      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11586      * @return {Roo.data.Record[]} An array of Records
11587      */
11588     getRange : function(start, end){
11589         return this.data.getRange(start, end);
11590     },
11591
11592     // private
11593     storeOptions : function(o){
11594         o = Roo.apply({}, o);
11595         delete o.callback;
11596         delete o.scope;
11597         this.lastOptions = o;
11598     },
11599
11600     /**
11601      * Loads the Record cache from the configured Proxy using the configured Reader.
11602      * <p>
11603      * If using remote paging, then the first load call must specify the <em>start</em>
11604      * and <em>limit</em> properties in the options.params property to establish the initial
11605      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11606      * <p>
11607      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11608      * and this call will return before the new data has been loaded. Perform any post-processing
11609      * in a callback function, or in a "load" event handler.</strong>
11610      * <p>
11611      * @param {Object} options An object containing properties which control loading options:<ul>
11612      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11613      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11614      * passed the following arguments:<ul>
11615      * <li>r : Roo.data.Record[]</li>
11616      * <li>options: Options object from the load call</li>
11617      * <li>success: Boolean success indicator</li></ul></li>
11618      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11619      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11620      * </ul>
11621      */
11622     load : function(options){
11623         options = options || {};
11624         if(this.fireEvent("beforeload", this, options) !== false){
11625             this.storeOptions(options);
11626             var p = Roo.apply(options.params || {}, this.baseParams);
11627             // if meta was not loaded from remote source.. try requesting it.
11628             if (!this.reader.metaFromRemote) {
11629                 p._requestMeta = 1;
11630             }
11631             if(this.sortInfo && this.remoteSort){
11632                 var pn = this.paramNames;
11633                 p[pn["sort"]] = this.sortInfo.field;
11634                 p[pn["dir"]] = this.sortInfo.direction;
11635             }
11636             if (this.multiSort) {
11637                 var pn = this.paramNames;
11638                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11639             }
11640             
11641             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11642         }
11643     },
11644
11645     /**
11646      * Reloads the Record cache from the configured Proxy using the configured Reader and
11647      * the options from the last load operation performed.
11648      * @param {Object} options (optional) An object containing properties which may override the options
11649      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11650      * the most recently used options are reused).
11651      */
11652     reload : function(options){
11653         this.load(Roo.applyIf(options||{}, this.lastOptions));
11654     },
11655
11656     // private
11657     // Called as a callback by the Reader during a load operation.
11658     loadRecords : function(o, options, success){
11659         if(!o || success === false){
11660             if(success !== false){
11661                 this.fireEvent("load", this, [], options, o);
11662             }
11663             if(options.callback){
11664                 options.callback.call(options.scope || this, [], options, false);
11665             }
11666             return;
11667         }
11668         // if data returned failure - throw an exception.
11669         if (o.success === false) {
11670             // show a message if no listener is registered.
11671             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11672                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11673             }
11674             // loadmask wil be hooked into this..
11675             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11676             return;
11677         }
11678         var r = o.records, t = o.totalRecords || r.length;
11679         
11680         this.fireEvent("beforeloadadd", this, r, options, o);
11681         
11682         if(!options || options.add !== true){
11683             if(this.pruneModifiedRecords){
11684                 this.modified = [];
11685             }
11686             for(var i = 0, len = r.length; i < len; i++){
11687                 r[i].join(this);
11688             }
11689             if(this.snapshot){
11690                 this.data = this.snapshot;
11691                 delete this.snapshot;
11692             }
11693             this.data.clear();
11694             this.data.addAll(r);
11695             this.totalLength = t;
11696             this.applySort();
11697             this.fireEvent("datachanged", this);
11698         }else{
11699             this.totalLength = Math.max(t, this.data.length+r.length);
11700             this.add(r);
11701         }
11702         
11703         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11704                 
11705             var e = new Roo.data.Record({});
11706
11707             e.set(this.parent.displayField, this.parent.emptyTitle);
11708             e.set(this.parent.valueField, '');
11709
11710             this.insert(0, e);
11711         }
11712             
11713         this.fireEvent("load", this, r, options, o);
11714         if(options.callback){
11715             options.callback.call(options.scope || this, r, options, true);
11716         }
11717     },
11718
11719
11720     /**
11721      * Loads data from a passed data block. A Reader which understands the format of the data
11722      * must have been configured in the constructor.
11723      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11724      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11725      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11726      */
11727     loadData : function(o, append){
11728         var r = this.reader.readRecords(o);
11729         this.loadRecords(r, {add: append}, true);
11730     },
11731
11732     /**
11733      * Gets the number of cached records.
11734      * <p>
11735      * <em>If using paging, this may not be the total size of the dataset. If the data object
11736      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11737      * the data set size</em>
11738      */
11739     getCount : function(){
11740         return this.data.length || 0;
11741     },
11742
11743     /**
11744      * Gets the total number of records in the dataset as returned by the server.
11745      * <p>
11746      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11747      * the dataset size</em>
11748      */
11749     getTotalCount : function(){
11750         return this.totalLength || 0;
11751     },
11752
11753     /**
11754      * Returns the sort state of the Store as an object with two properties:
11755      * <pre><code>
11756  field {String} The name of the field by which the Records are sorted
11757  direction {String} The sort order, "ASC" or "DESC"
11758      * </code></pre>
11759      */
11760     getSortState : function(){
11761         return this.sortInfo;
11762     },
11763
11764     // private
11765     applySort : function(){
11766         if(this.sortInfo && !this.remoteSort){
11767             var s = this.sortInfo, f = s.field;
11768             var st = this.fields.get(f).sortType;
11769             var fn = function(r1, r2){
11770                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11771                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11772             };
11773             this.data.sort(s.direction, fn);
11774             if(this.snapshot && this.snapshot != this.data){
11775                 this.snapshot.sort(s.direction, fn);
11776             }
11777         }
11778     },
11779
11780     /**
11781      * Sets the default sort column and order to be used by the next load operation.
11782      * @param {String} fieldName The name of the field to sort by.
11783      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11784      */
11785     setDefaultSort : function(field, dir){
11786         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11787     },
11788
11789     /**
11790      * Sort the Records.
11791      * If remote sorting is used, the sort is performed on the server, and the cache is
11792      * reloaded. If local sorting is used, the cache is sorted internally.
11793      * @param {String} fieldName The name of the field to sort by.
11794      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11795      */
11796     sort : function(fieldName, dir){
11797         var f = this.fields.get(fieldName);
11798         if(!dir){
11799             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11800             
11801             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11802                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11803             }else{
11804                 dir = f.sortDir;
11805             }
11806         }
11807         this.sortToggle[f.name] = dir;
11808         this.sortInfo = {field: f.name, direction: dir};
11809         if(!this.remoteSort){
11810             this.applySort();
11811             this.fireEvent("datachanged", this);
11812         }else{
11813             this.load(this.lastOptions);
11814         }
11815     },
11816
11817     /**
11818      * Calls the specified function for each of the Records in the cache.
11819      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11820      * Returning <em>false</em> aborts and exits the iteration.
11821      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11822      */
11823     each : function(fn, scope){
11824         this.data.each(fn, scope);
11825     },
11826
11827     /**
11828      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11829      * (e.g., during paging).
11830      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11831      */
11832     getModifiedRecords : function(){
11833         return this.modified;
11834     },
11835
11836     // private
11837     createFilterFn : function(property, value, anyMatch){
11838         if(!value.exec){ // not a regex
11839             value = String(value);
11840             if(value.length == 0){
11841                 return false;
11842             }
11843             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11844         }
11845         return function(r){
11846             return value.test(r.data[property]);
11847         };
11848     },
11849
11850     /**
11851      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11852      * @param {String} property A field on your records
11853      * @param {Number} start The record index to start at (defaults to 0)
11854      * @param {Number} end The last record index to include (defaults to length - 1)
11855      * @return {Number} The sum
11856      */
11857     sum : function(property, start, end){
11858         var rs = this.data.items, v = 0;
11859         start = start || 0;
11860         end = (end || end === 0) ? end : rs.length-1;
11861
11862         for(var i = start; i <= end; i++){
11863             v += (rs[i].data[property] || 0);
11864         }
11865         return v;
11866     },
11867
11868     /**
11869      * Filter the records by a specified property.
11870      * @param {String} field A field on your records
11871      * @param {String/RegExp} value Either a string that the field
11872      * should start with or a RegExp to test against the field
11873      * @param {Boolean} anyMatch True to match any part not just the beginning
11874      */
11875     filter : function(property, value, anyMatch){
11876         var fn = this.createFilterFn(property, value, anyMatch);
11877         return fn ? this.filterBy(fn) : this.clearFilter();
11878     },
11879
11880     /**
11881      * Filter by a function. The specified function will be called with each
11882      * record in this data source. If the function returns true the record is included,
11883      * otherwise it is filtered.
11884      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11885      * @param {Object} scope (optional) The scope of the function (defaults to this)
11886      */
11887     filterBy : function(fn, scope){
11888         this.snapshot = this.snapshot || this.data;
11889         this.data = this.queryBy(fn, scope||this);
11890         this.fireEvent("datachanged", this);
11891     },
11892
11893     /**
11894      * Query the records by a specified property.
11895      * @param {String} field A field on your records
11896      * @param {String/RegExp} value Either a string that the field
11897      * should start with or a RegExp to test against the field
11898      * @param {Boolean} anyMatch True to match any part not just the beginning
11899      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11900      */
11901     query : function(property, value, anyMatch){
11902         var fn = this.createFilterFn(property, value, anyMatch);
11903         return fn ? this.queryBy(fn) : this.data.clone();
11904     },
11905
11906     /**
11907      * Query by a function. The specified function will be called with each
11908      * record in this data source. If the function returns true the record is included
11909      * in the results.
11910      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11911      * @param {Object} scope (optional) The scope of the function (defaults to this)
11912       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11913      **/
11914     queryBy : function(fn, scope){
11915         var data = this.snapshot || this.data;
11916         return data.filterBy(fn, scope||this);
11917     },
11918
11919     /**
11920      * Collects unique values for a particular dataIndex from this store.
11921      * @param {String} dataIndex The property to collect
11922      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11923      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11924      * @return {Array} An array of the unique values
11925      **/
11926     collect : function(dataIndex, allowNull, bypassFilter){
11927         var d = (bypassFilter === true && this.snapshot) ?
11928                 this.snapshot.items : this.data.items;
11929         var v, sv, r = [], l = {};
11930         for(var i = 0, len = d.length; i < len; i++){
11931             v = d[i].data[dataIndex];
11932             sv = String(v);
11933             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11934                 l[sv] = true;
11935                 r[r.length] = v;
11936             }
11937         }
11938         return r;
11939     },
11940
11941     /**
11942      * Revert to a view of the Record cache with no filtering applied.
11943      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11944      */
11945     clearFilter : function(suppressEvent){
11946         if(this.snapshot && this.snapshot != this.data){
11947             this.data = this.snapshot;
11948             delete this.snapshot;
11949             if(suppressEvent !== true){
11950                 this.fireEvent("datachanged", this);
11951             }
11952         }
11953     },
11954
11955     // private
11956     afterEdit : function(record){
11957         if(this.modified.indexOf(record) == -1){
11958             this.modified.push(record);
11959         }
11960         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11961     },
11962     
11963     // private
11964     afterReject : function(record){
11965         this.modified.remove(record);
11966         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11967     },
11968
11969     // private
11970     afterCommit : function(record){
11971         this.modified.remove(record);
11972         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11973     },
11974
11975     /**
11976      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11977      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11978      */
11979     commitChanges : function(){
11980         var m = this.modified.slice(0);
11981         this.modified = [];
11982         for(var i = 0, len = m.length; i < len; i++){
11983             m[i].commit();
11984         }
11985     },
11986
11987     /**
11988      * Cancel outstanding changes on all changed records.
11989      */
11990     rejectChanges : function(){
11991         var m = this.modified.slice(0);
11992         this.modified = [];
11993         for(var i = 0, len = m.length; i < len; i++){
11994             m[i].reject();
11995         }
11996     },
11997
11998     onMetaChange : function(meta, rtype, o){
11999         this.recordType = rtype;
12000         this.fields = rtype.prototype.fields;
12001         delete this.snapshot;
12002         this.sortInfo = meta.sortInfo || this.sortInfo;
12003         this.modified = [];
12004         this.fireEvent('metachange', this, this.reader.meta);
12005     },
12006     
12007     moveIndex : function(data, type)
12008     {
12009         var index = this.indexOf(data);
12010         
12011         var newIndex = index + type;
12012         
12013         this.remove(data);
12014         
12015         this.insert(newIndex, data);
12016         
12017     }
12018 });/*
12019  * Based on:
12020  * Ext JS Library 1.1.1
12021  * Copyright(c) 2006-2007, Ext JS, LLC.
12022  *
12023  * Originally Released Under LGPL - original licence link has changed is not relivant.
12024  *
12025  * Fork - LGPL
12026  * <script type="text/javascript">
12027  */
12028
12029 /**
12030  * @class Roo.data.SimpleStore
12031  * @extends Roo.data.Store
12032  * Small helper class to make creating Stores from Array data easier.
12033  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12034  * @cfg {Array} fields An array of field definition objects, or field name strings.
12035  * @cfg {Array} data The multi-dimensional array of data
12036  * @constructor
12037  * @param {Object} config
12038  */
12039 Roo.data.SimpleStore = function(config){
12040     Roo.data.SimpleStore.superclass.constructor.call(this, {
12041         isLocal : true,
12042         reader: new Roo.data.ArrayReader({
12043                 id: config.id
12044             },
12045             Roo.data.Record.create(config.fields)
12046         ),
12047         proxy : new Roo.data.MemoryProxy(config.data)
12048     });
12049     this.load();
12050 };
12051 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12052  * Based on:
12053  * Ext JS Library 1.1.1
12054  * Copyright(c) 2006-2007, Ext JS, LLC.
12055  *
12056  * Originally Released Under LGPL - original licence link has changed is not relivant.
12057  *
12058  * Fork - LGPL
12059  * <script type="text/javascript">
12060  */
12061
12062 /**
12063 /**
12064  * @extends Roo.data.Store
12065  * @class Roo.data.JsonStore
12066  * Small helper class to make creating Stores for JSON data easier. <br/>
12067 <pre><code>
12068 var store = new Roo.data.JsonStore({
12069     url: 'get-images.php',
12070     root: 'images',
12071     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12072 });
12073 </code></pre>
12074  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12075  * JsonReader and HttpProxy (unless inline data is provided).</b>
12076  * @cfg {Array} fields An array of field definition objects, or field name strings.
12077  * @constructor
12078  * @param {Object} config
12079  */
12080 Roo.data.JsonStore = function(c){
12081     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12082         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12083         reader: new Roo.data.JsonReader(c, c.fields)
12084     }));
12085 };
12086 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12087  * Based on:
12088  * Ext JS Library 1.1.1
12089  * Copyright(c) 2006-2007, Ext JS, LLC.
12090  *
12091  * Originally Released Under LGPL - original licence link has changed is not relivant.
12092  *
12093  * Fork - LGPL
12094  * <script type="text/javascript">
12095  */
12096
12097  
12098 Roo.data.Field = function(config){
12099     if(typeof config == "string"){
12100         config = {name: config};
12101     }
12102     Roo.apply(this, config);
12103     
12104     if(!this.type){
12105         this.type = "auto";
12106     }
12107     
12108     var st = Roo.data.SortTypes;
12109     // named sortTypes are supported, here we look them up
12110     if(typeof this.sortType == "string"){
12111         this.sortType = st[this.sortType];
12112     }
12113     
12114     // set default sortType for strings and dates
12115     if(!this.sortType){
12116         switch(this.type){
12117             case "string":
12118                 this.sortType = st.asUCString;
12119                 break;
12120             case "date":
12121                 this.sortType = st.asDate;
12122                 break;
12123             default:
12124                 this.sortType = st.none;
12125         }
12126     }
12127
12128     // define once
12129     var stripRe = /[\$,%]/g;
12130
12131     // prebuilt conversion function for this field, instead of
12132     // switching every time we're reading a value
12133     if(!this.convert){
12134         var cv, dateFormat = this.dateFormat;
12135         switch(this.type){
12136             case "":
12137             case "auto":
12138             case undefined:
12139                 cv = function(v){ return v; };
12140                 break;
12141             case "string":
12142                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12143                 break;
12144             case "int":
12145                 cv = function(v){
12146                     return v !== undefined && v !== null && v !== '' ?
12147                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12148                     };
12149                 break;
12150             case "float":
12151                 cv = function(v){
12152                     return v !== undefined && v !== null && v !== '' ?
12153                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12154                     };
12155                 break;
12156             case "bool":
12157             case "boolean":
12158                 cv = function(v){ return v === true || v === "true" || v == 1; };
12159                 break;
12160             case "date":
12161                 cv = function(v){
12162                     if(!v){
12163                         return '';
12164                     }
12165                     if(v instanceof Date){
12166                         return v;
12167                     }
12168                     if(dateFormat){
12169                         if(dateFormat == "timestamp"){
12170                             return new Date(v*1000);
12171                         }
12172                         return Date.parseDate(v, dateFormat);
12173                     }
12174                     var parsed = Date.parse(v);
12175                     return parsed ? new Date(parsed) : null;
12176                 };
12177              break;
12178             
12179         }
12180         this.convert = cv;
12181     }
12182 };
12183
12184 Roo.data.Field.prototype = {
12185     dateFormat: null,
12186     defaultValue: "",
12187     mapping: null,
12188     sortType : null,
12189     sortDir : "ASC"
12190 };/*
12191  * Based on:
12192  * Ext JS Library 1.1.1
12193  * Copyright(c) 2006-2007, Ext JS, LLC.
12194  *
12195  * Originally Released Under LGPL - original licence link has changed is not relivant.
12196  *
12197  * Fork - LGPL
12198  * <script type="text/javascript">
12199  */
12200  
12201 // Base class for reading structured data from a data source.  This class is intended to be
12202 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12203
12204 /**
12205  * @class Roo.data.DataReader
12206  * Base class for reading structured data from a data source.  This class is intended to be
12207  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12208  */
12209
12210 Roo.data.DataReader = function(meta, recordType){
12211     
12212     this.meta = meta;
12213     
12214     this.recordType = recordType instanceof Array ? 
12215         Roo.data.Record.create(recordType) : recordType;
12216 };
12217
12218 Roo.data.DataReader.prototype = {
12219      /**
12220      * Create an empty record
12221      * @param {Object} data (optional) - overlay some values
12222      * @return {Roo.data.Record} record created.
12223      */
12224     newRow :  function(d) {
12225         var da =  {};
12226         this.recordType.prototype.fields.each(function(c) {
12227             switch( c.type) {
12228                 case 'int' : da[c.name] = 0; break;
12229                 case 'date' : da[c.name] = new Date(); break;
12230                 case 'float' : da[c.name] = 0.0; break;
12231                 case 'boolean' : da[c.name] = false; break;
12232                 default : da[c.name] = ""; break;
12233             }
12234             
12235         });
12236         return new this.recordType(Roo.apply(da, d));
12237     }
12238     
12239 };/*
12240  * Based on:
12241  * Ext JS Library 1.1.1
12242  * Copyright(c) 2006-2007, Ext JS, LLC.
12243  *
12244  * Originally Released Under LGPL - original licence link has changed is not relivant.
12245  *
12246  * Fork - LGPL
12247  * <script type="text/javascript">
12248  */
12249
12250 /**
12251  * @class Roo.data.DataProxy
12252  * @extends Roo.data.Observable
12253  * This class is an abstract base class for implementations which provide retrieval of
12254  * unformatted data objects.<br>
12255  * <p>
12256  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12257  * (of the appropriate type which knows how to parse the data object) to provide a block of
12258  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12259  * <p>
12260  * Custom implementations must implement the load method as described in
12261  * {@link Roo.data.HttpProxy#load}.
12262  */
12263 Roo.data.DataProxy = function(){
12264     this.addEvents({
12265         /**
12266          * @event beforeload
12267          * Fires before a network request is made to retrieve a data object.
12268          * @param {Object} This DataProxy object.
12269          * @param {Object} params The params parameter to the load function.
12270          */
12271         beforeload : true,
12272         /**
12273          * @event load
12274          * Fires before the load method's callback is called.
12275          * @param {Object} This DataProxy object.
12276          * @param {Object} o The data object.
12277          * @param {Object} arg The callback argument object passed to the load function.
12278          */
12279         load : true,
12280         /**
12281          * @event loadexception
12282          * Fires if an Exception occurs during data retrieval.
12283          * @param {Object} This DataProxy object.
12284          * @param {Object} o The data object.
12285          * @param {Object} arg The callback argument object passed to the load function.
12286          * @param {Object} e The Exception.
12287          */
12288         loadexception : true
12289     });
12290     Roo.data.DataProxy.superclass.constructor.call(this);
12291 };
12292
12293 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12294
12295     /**
12296      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12297      */
12298 /*
12299  * Based on:
12300  * Ext JS Library 1.1.1
12301  * Copyright(c) 2006-2007, Ext JS, LLC.
12302  *
12303  * Originally Released Under LGPL - original licence link has changed is not relivant.
12304  *
12305  * Fork - LGPL
12306  * <script type="text/javascript">
12307  */
12308 /**
12309  * @class Roo.data.MemoryProxy
12310  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12311  * to the Reader when its load method is called.
12312  * @constructor
12313  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12314  */
12315 Roo.data.MemoryProxy = function(data){
12316     if (data.data) {
12317         data = data.data;
12318     }
12319     Roo.data.MemoryProxy.superclass.constructor.call(this);
12320     this.data = data;
12321 };
12322
12323 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12324     
12325     /**
12326      * Load data from the requested source (in this case an in-memory
12327      * data object passed to the constructor), read the data object into
12328      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12329      * process that block using the passed callback.
12330      * @param {Object} params This parameter is not used by the MemoryProxy class.
12331      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12332      * object into a block of Roo.data.Records.
12333      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12334      * The function must be passed <ul>
12335      * <li>The Record block object</li>
12336      * <li>The "arg" argument from the load function</li>
12337      * <li>A boolean success indicator</li>
12338      * </ul>
12339      * @param {Object} scope The scope in which to call the callback
12340      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12341      */
12342     load : function(params, reader, callback, scope, arg){
12343         params = params || {};
12344         var result;
12345         try {
12346             result = reader.readRecords(this.data);
12347         }catch(e){
12348             this.fireEvent("loadexception", this, arg, null, e);
12349             callback.call(scope, null, arg, false);
12350             return;
12351         }
12352         callback.call(scope, result, arg, true);
12353     },
12354     
12355     // private
12356     update : function(params, records){
12357         
12358     }
12359 });/*
12360  * Based on:
12361  * Ext JS Library 1.1.1
12362  * Copyright(c) 2006-2007, Ext JS, LLC.
12363  *
12364  * Originally Released Under LGPL - original licence link has changed is not relivant.
12365  *
12366  * Fork - LGPL
12367  * <script type="text/javascript">
12368  */
12369 /**
12370  * @class Roo.data.HttpProxy
12371  * @extends Roo.data.DataProxy
12372  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12373  * configured to reference a certain URL.<br><br>
12374  * <p>
12375  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12376  * from which the running page was served.<br><br>
12377  * <p>
12378  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12379  * <p>
12380  * Be aware that to enable the browser to parse an XML document, the server must set
12381  * the Content-Type header in the HTTP response to "text/xml".
12382  * @constructor
12383  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12384  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12385  * will be used to make the request.
12386  */
12387 Roo.data.HttpProxy = function(conn){
12388     Roo.data.HttpProxy.superclass.constructor.call(this);
12389     // is conn a conn config or a real conn?
12390     this.conn = conn;
12391     this.useAjax = !conn || !conn.events;
12392   
12393 };
12394
12395 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12396     // thse are take from connection...
12397     
12398     /**
12399      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12400      */
12401     /**
12402      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12403      * extra parameters to each request made by this object. (defaults to undefined)
12404      */
12405     /**
12406      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12407      *  to each request made by this object. (defaults to undefined)
12408      */
12409     /**
12410      * @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)
12411      */
12412     /**
12413      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12414      */
12415      /**
12416      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12417      * @type Boolean
12418      */
12419   
12420
12421     /**
12422      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12423      * @type Boolean
12424      */
12425     /**
12426      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12427      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12428      * a finer-grained basis than the DataProxy events.
12429      */
12430     getConnection : function(){
12431         return this.useAjax ? Roo.Ajax : this.conn;
12432     },
12433
12434     /**
12435      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12436      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12437      * process that block using the passed callback.
12438      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12439      * for the request to the remote server.
12440      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12441      * object into a block of Roo.data.Records.
12442      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12443      * The function must be passed <ul>
12444      * <li>The Record block object</li>
12445      * <li>The "arg" argument from the load function</li>
12446      * <li>A boolean success indicator</li>
12447      * </ul>
12448      * @param {Object} scope The scope in which to call the callback
12449      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12450      */
12451     load : function(params, reader, callback, scope, arg){
12452         if(this.fireEvent("beforeload", this, params) !== false){
12453             var  o = {
12454                 params : params || {},
12455                 request: {
12456                     callback : callback,
12457                     scope : scope,
12458                     arg : arg
12459                 },
12460                 reader: reader,
12461                 callback : this.loadResponse,
12462                 scope: this
12463             };
12464             if(this.useAjax){
12465                 Roo.applyIf(o, this.conn);
12466                 if(this.activeRequest){
12467                     Roo.Ajax.abort(this.activeRequest);
12468                 }
12469                 this.activeRequest = Roo.Ajax.request(o);
12470             }else{
12471                 this.conn.request(o);
12472             }
12473         }else{
12474             callback.call(scope||this, null, arg, false);
12475         }
12476     },
12477
12478     // private
12479     loadResponse : function(o, success, response){
12480         delete this.activeRequest;
12481         if(!success){
12482             this.fireEvent("loadexception", this, o, response);
12483             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12484             return;
12485         }
12486         var result;
12487         try {
12488             result = o.reader.read(response);
12489         }catch(e){
12490             this.fireEvent("loadexception", this, o, response, e);
12491             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12492             return;
12493         }
12494         
12495         this.fireEvent("load", this, o, o.request.arg);
12496         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12497     },
12498
12499     // private
12500     update : function(dataSet){
12501
12502     },
12503
12504     // private
12505     updateResponse : function(dataSet){
12506
12507     }
12508 });/*
12509  * Based on:
12510  * Ext JS Library 1.1.1
12511  * Copyright(c) 2006-2007, Ext JS, LLC.
12512  *
12513  * Originally Released Under LGPL - original licence link has changed is not relivant.
12514  *
12515  * Fork - LGPL
12516  * <script type="text/javascript">
12517  */
12518
12519 /**
12520  * @class Roo.data.ScriptTagProxy
12521  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12522  * other than the originating domain of the running page.<br><br>
12523  * <p>
12524  * <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
12525  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12526  * <p>
12527  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12528  * source code that is used as the source inside a &lt;script> tag.<br><br>
12529  * <p>
12530  * In order for the browser to process the returned data, the server must wrap the data object
12531  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12532  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12533  * depending on whether the callback name was passed:
12534  * <p>
12535  * <pre><code>
12536 boolean scriptTag = false;
12537 String cb = request.getParameter("callback");
12538 if (cb != null) {
12539     scriptTag = true;
12540     response.setContentType("text/javascript");
12541 } else {
12542     response.setContentType("application/x-json");
12543 }
12544 Writer out = response.getWriter();
12545 if (scriptTag) {
12546     out.write(cb + "(");
12547 }
12548 out.print(dataBlock.toJsonString());
12549 if (scriptTag) {
12550     out.write(");");
12551 }
12552 </pre></code>
12553  *
12554  * @constructor
12555  * @param {Object} config A configuration object.
12556  */
12557 Roo.data.ScriptTagProxy = function(config){
12558     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12559     Roo.apply(this, config);
12560     this.head = document.getElementsByTagName("head")[0];
12561 };
12562
12563 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12564
12565 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12566     /**
12567      * @cfg {String} url The URL from which to request the data object.
12568      */
12569     /**
12570      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12571      */
12572     timeout : 30000,
12573     /**
12574      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12575      * the server the name of the callback function set up by the load call to process the returned data object.
12576      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12577      * javascript output which calls this named function passing the data object as its only parameter.
12578      */
12579     callbackParam : "callback",
12580     /**
12581      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12582      * name to the request.
12583      */
12584     nocache : true,
12585
12586     /**
12587      * Load data from the configured URL, read the data object into
12588      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12589      * process that block using the passed callback.
12590      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12591      * for the request to the remote server.
12592      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12593      * object into a block of Roo.data.Records.
12594      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12595      * The function must be passed <ul>
12596      * <li>The Record block object</li>
12597      * <li>The "arg" argument from the load function</li>
12598      * <li>A boolean success indicator</li>
12599      * </ul>
12600      * @param {Object} scope The scope in which to call the callback
12601      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12602      */
12603     load : function(params, reader, callback, scope, arg){
12604         if(this.fireEvent("beforeload", this, params) !== false){
12605
12606             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12607
12608             var url = this.url;
12609             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12610             if(this.nocache){
12611                 url += "&_dc=" + (new Date().getTime());
12612             }
12613             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12614             var trans = {
12615                 id : transId,
12616                 cb : "stcCallback"+transId,
12617                 scriptId : "stcScript"+transId,
12618                 params : params,
12619                 arg : arg,
12620                 url : url,
12621                 callback : callback,
12622                 scope : scope,
12623                 reader : reader
12624             };
12625             var conn = this;
12626
12627             window[trans.cb] = function(o){
12628                 conn.handleResponse(o, trans);
12629             };
12630
12631             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12632
12633             if(this.autoAbort !== false){
12634                 this.abort();
12635             }
12636
12637             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12638
12639             var script = document.createElement("script");
12640             script.setAttribute("src", url);
12641             script.setAttribute("type", "text/javascript");
12642             script.setAttribute("id", trans.scriptId);
12643             this.head.appendChild(script);
12644
12645             this.trans = trans;
12646         }else{
12647             callback.call(scope||this, null, arg, false);
12648         }
12649     },
12650
12651     // private
12652     isLoading : function(){
12653         return this.trans ? true : false;
12654     },
12655
12656     /**
12657      * Abort the current server request.
12658      */
12659     abort : function(){
12660         if(this.isLoading()){
12661             this.destroyTrans(this.trans);
12662         }
12663     },
12664
12665     // private
12666     destroyTrans : function(trans, isLoaded){
12667         this.head.removeChild(document.getElementById(trans.scriptId));
12668         clearTimeout(trans.timeoutId);
12669         if(isLoaded){
12670             window[trans.cb] = undefined;
12671             try{
12672                 delete window[trans.cb];
12673             }catch(e){}
12674         }else{
12675             // if hasn't been loaded, wait for load to remove it to prevent script error
12676             window[trans.cb] = function(){
12677                 window[trans.cb] = undefined;
12678                 try{
12679                     delete window[trans.cb];
12680                 }catch(e){}
12681             };
12682         }
12683     },
12684
12685     // private
12686     handleResponse : function(o, trans){
12687         this.trans = false;
12688         this.destroyTrans(trans, true);
12689         var result;
12690         try {
12691             result = trans.reader.readRecords(o);
12692         }catch(e){
12693             this.fireEvent("loadexception", this, o, trans.arg, e);
12694             trans.callback.call(trans.scope||window, null, trans.arg, false);
12695             return;
12696         }
12697         this.fireEvent("load", this, o, trans.arg);
12698         trans.callback.call(trans.scope||window, result, trans.arg, true);
12699     },
12700
12701     // private
12702     handleFailure : function(trans){
12703         this.trans = false;
12704         this.destroyTrans(trans, false);
12705         this.fireEvent("loadexception", this, null, trans.arg);
12706         trans.callback.call(trans.scope||window, null, trans.arg, false);
12707     }
12708 });/*
12709  * Based on:
12710  * Ext JS Library 1.1.1
12711  * Copyright(c) 2006-2007, Ext JS, LLC.
12712  *
12713  * Originally Released Under LGPL - original licence link has changed is not relivant.
12714  *
12715  * Fork - LGPL
12716  * <script type="text/javascript">
12717  */
12718
12719 /**
12720  * @class Roo.data.JsonReader
12721  * @extends Roo.data.DataReader
12722  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12723  * based on mappings in a provided Roo.data.Record constructor.
12724  * 
12725  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12726  * in the reply previously. 
12727  * 
12728  * <p>
12729  * Example code:
12730  * <pre><code>
12731 var RecordDef = Roo.data.Record.create([
12732     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12733     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12734 ]);
12735 var myReader = new Roo.data.JsonReader({
12736     totalProperty: "results",    // The property which contains the total dataset size (optional)
12737     root: "rows",                // The property which contains an Array of row objects
12738     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12739 }, RecordDef);
12740 </code></pre>
12741  * <p>
12742  * This would consume a JSON file like this:
12743  * <pre><code>
12744 { 'results': 2, 'rows': [
12745     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12746     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12747 }
12748 </code></pre>
12749  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12750  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12751  * paged from the remote server.
12752  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12753  * @cfg {String} root name of the property which contains the Array of row objects.
12754  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12755  * @cfg {Array} fields Array of field definition objects
12756  * @constructor
12757  * Create a new JsonReader
12758  * @param {Object} meta Metadata configuration options
12759  * @param {Object} recordType Either an Array of field definition objects,
12760  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12761  */
12762 Roo.data.JsonReader = function(meta, recordType){
12763     
12764     meta = meta || {};
12765     // set some defaults:
12766     Roo.applyIf(meta, {
12767         totalProperty: 'total',
12768         successProperty : 'success',
12769         root : 'data',
12770         id : 'id'
12771     });
12772     
12773     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12774 };
12775 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12776     
12777     /**
12778      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12779      * Used by Store query builder to append _requestMeta to params.
12780      * 
12781      */
12782     metaFromRemote : false,
12783     /**
12784      * This method is only used by a DataProxy which has retrieved data from a remote server.
12785      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12786      * @return {Object} data A data block which is used by an Roo.data.Store object as
12787      * a cache of Roo.data.Records.
12788      */
12789     read : function(response){
12790         var json = response.responseText;
12791        
12792         var o = /* eval:var:o */ eval("("+json+")");
12793         if(!o) {
12794             throw {message: "JsonReader.read: Json object not found"};
12795         }
12796         
12797         if(o.metaData){
12798             
12799             delete this.ef;
12800             this.metaFromRemote = true;
12801             this.meta = o.metaData;
12802             this.recordType = Roo.data.Record.create(o.metaData.fields);
12803             this.onMetaChange(this.meta, this.recordType, o);
12804         }
12805         return this.readRecords(o);
12806     },
12807
12808     // private function a store will implement
12809     onMetaChange : function(meta, recordType, o){
12810
12811     },
12812
12813     /**
12814          * @ignore
12815          */
12816     simpleAccess: function(obj, subsc) {
12817         return obj[subsc];
12818     },
12819
12820         /**
12821          * @ignore
12822          */
12823     getJsonAccessor: function(){
12824         var re = /[\[\.]/;
12825         return function(expr) {
12826             try {
12827                 return(re.test(expr))
12828                     ? new Function("obj", "return obj." + expr)
12829                     : function(obj){
12830                         return obj[expr];
12831                     };
12832             } catch(e){}
12833             return Roo.emptyFn;
12834         };
12835     }(),
12836
12837     /**
12838      * Create a data block containing Roo.data.Records from an XML document.
12839      * @param {Object} o An object which contains an Array of row objects in the property specified
12840      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12841      * which contains the total size of the dataset.
12842      * @return {Object} data A data block which is used by an Roo.data.Store object as
12843      * a cache of Roo.data.Records.
12844      */
12845     readRecords : function(o){
12846         /**
12847          * After any data loads, the raw JSON data is available for further custom processing.
12848          * @type Object
12849          */
12850         this.o = o;
12851         var s = this.meta, Record = this.recordType,
12852             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12853
12854 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12855         if (!this.ef) {
12856             if(s.totalProperty) {
12857                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12858                 }
12859                 if(s.successProperty) {
12860                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12861                 }
12862                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12863                 if (s.id) {
12864                         var g = this.getJsonAccessor(s.id);
12865                         this.getId = function(rec) {
12866                                 var r = g(rec);  
12867                                 return (r === undefined || r === "") ? null : r;
12868                         };
12869                 } else {
12870                         this.getId = function(){return null;};
12871                 }
12872             this.ef = [];
12873             for(var jj = 0; jj < fl; jj++){
12874                 f = fi[jj];
12875                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12876                 this.ef[jj] = this.getJsonAccessor(map);
12877             }
12878         }
12879
12880         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12881         if(s.totalProperty){
12882             var vt = parseInt(this.getTotal(o), 10);
12883             if(!isNaN(vt)){
12884                 totalRecords = vt;
12885             }
12886         }
12887         if(s.successProperty){
12888             var vs = this.getSuccess(o);
12889             if(vs === false || vs === 'false'){
12890                 success = false;
12891             }
12892         }
12893         var records = [];
12894         for(var i = 0; i < c; i++){
12895                 var n = root[i];
12896             var values = {};
12897             var id = this.getId(n);
12898             for(var j = 0; j < fl; j++){
12899                 f = fi[j];
12900             var v = this.ef[j](n);
12901             if (!f.convert) {
12902                 Roo.log('missing convert for ' + f.name);
12903                 Roo.log(f);
12904                 continue;
12905             }
12906             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12907             }
12908             var record = new Record(values, id);
12909             record.json = n;
12910             records[i] = record;
12911         }
12912         return {
12913             raw : o,
12914             success : success,
12915             records : records,
12916             totalRecords : totalRecords
12917         };
12918     }
12919 });/*
12920  * Based on:
12921  * Ext JS Library 1.1.1
12922  * Copyright(c) 2006-2007, Ext JS, LLC.
12923  *
12924  * Originally Released Under LGPL - original licence link has changed is not relivant.
12925  *
12926  * Fork - LGPL
12927  * <script type="text/javascript">
12928  */
12929
12930 /**
12931  * @class Roo.data.ArrayReader
12932  * @extends Roo.data.DataReader
12933  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12934  * Each element of that Array represents a row of data fields. The
12935  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12936  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12937  * <p>
12938  * Example code:.
12939  * <pre><code>
12940 var RecordDef = Roo.data.Record.create([
12941     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12942     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12943 ]);
12944 var myReader = new Roo.data.ArrayReader({
12945     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12946 }, RecordDef);
12947 </code></pre>
12948  * <p>
12949  * This would consume an Array like this:
12950  * <pre><code>
12951 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12952   </code></pre>
12953  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12954  * @constructor
12955  * Create a new JsonReader
12956  * @param {Object} meta Metadata configuration options.
12957  * @param {Object} recordType Either an Array of field definition objects
12958  * as specified to {@link Roo.data.Record#create},
12959  * or an {@link Roo.data.Record} object
12960  * created using {@link Roo.data.Record#create}.
12961  */
12962 Roo.data.ArrayReader = function(meta, recordType){
12963     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12964 };
12965
12966 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12967     /**
12968      * Create a data block containing Roo.data.Records from an XML document.
12969      * @param {Object} o An Array of row objects which represents the dataset.
12970      * @return {Object} data A data block which is used by an Roo.data.Store object as
12971      * a cache of Roo.data.Records.
12972      */
12973     readRecords : function(o){
12974         var sid = this.meta ? this.meta.id : null;
12975         var recordType = this.recordType, fields = recordType.prototype.fields;
12976         var records = [];
12977         var root = o;
12978             for(var i = 0; i < root.length; i++){
12979                     var n = root[i];
12980                 var values = {};
12981                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12982                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12983                 var f = fields.items[j];
12984                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12985                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12986                 v = f.convert(v);
12987                 values[f.name] = v;
12988             }
12989                 var record = new recordType(values, id);
12990                 record.json = n;
12991                 records[records.length] = record;
12992             }
12993             return {
12994                 records : records,
12995                 totalRecords : records.length
12996             };
12997     }
12998 });/*
12999  * - LGPL
13000  * * 
13001  */
13002
13003 /**
13004  * @class Roo.bootstrap.ComboBox
13005  * @extends Roo.bootstrap.TriggerField
13006  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13007  * @cfg {Boolean} append (true|false) default false
13008  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13009  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13010  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13011  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13012  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13013  * @cfg {Boolean} animate default true
13014  * @cfg {Boolean} emptyResultText only for touch device
13015  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13016  * @cfg {String} emptyTitle default ''
13017  * @constructor
13018  * Create a new ComboBox.
13019  * @param {Object} config Configuration options
13020  */
13021 Roo.bootstrap.ComboBox = function(config){
13022     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13023     this.addEvents({
13024         /**
13025          * @event expand
13026          * Fires when the dropdown list is expanded
13027         * @param {Roo.bootstrap.ComboBox} combo This combo box
13028         */
13029         'expand' : true,
13030         /**
13031          * @event collapse
13032          * Fires when the dropdown list is collapsed
13033         * @param {Roo.bootstrap.ComboBox} combo This combo box
13034         */
13035         'collapse' : true,
13036         /**
13037          * @event beforeselect
13038          * Fires before a list item is selected. Return false to cancel the selection.
13039         * @param {Roo.bootstrap.ComboBox} combo This combo box
13040         * @param {Roo.data.Record} record The data record returned from the underlying store
13041         * @param {Number} index The index of the selected item in the dropdown list
13042         */
13043         'beforeselect' : true,
13044         /**
13045          * @event select
13046          * Fires when a list item is selected
13047         * @param {Roo.bootstrap.ComboBox} combo This combo box
13048         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13049         * @param {Number} index The index of the selected item in the dropdown list
13050         */
13051         'select' : true,
13052         /**
13053          * @event beforequery
13054          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13055          * The event object passed has these properties:
13056         * @param {Roo.bootstrap.ComboBox} combo This combo box
13057         * @param {String} query The query
13058         * @param {Boolean} forceAll true to force "all" query
13059         * @param {Boolean} cancel true to cancel the query
13060         * @param {Object} e The query event object
13061         */
13062         'beforequery': true,
13063          /**
13064          * @event add
13065          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13066         * @param {Roo.bootstrap.ComboBox} combo This combo box
13067         */
13068         'add' : true,
13069         /**
13070          * @event edit
13071          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13072         * @param {Roo.bootstrap.ComboBox} combo This combo box
13073         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13074         */
13075         'edit' : true,
13076         /**
13077          * @event remove
13078          * Fires when the remove value from the combobox array
13079         * @param {Roo.bootstrap.ComboBox} combo This combo box
13080         */
13081         'remove' : true,
13082         /**
13083          * @event afterremove
13084          * Fires when the remove value from the combobox array
13085         * @param {Roo.bootstrap.ComboBox} combo This combo box
13086         */
13087         'afterremove' : true,
13088         /**
13089          * @event specialfilter
13090          * Fires when specialfilter
13091             * @param {Roo.bootstrap.ComboBox} combo This combo box
13092             */
13093         'specialfilter' : true,
13094         /**
13095          * @event tick
13096          * Fires when tick the element
13097             * @param {Roo.bootstrap.ComboBox} combo This combo box
13098             */
13099         'tick' : true,
13100         /**
13101          * @event touchviewdisplay
13102          * Fires when touch view require special display (default is using displayField)
13103             * @param {Roo.bootstrap.ComboBox} combo This combo box
13104             * @param {Object} cfg set html .
13105             */
13106         'touchviewdisplay' : true
13107         
13108     });
13109     
13110     this.item = [];
13111     this.tickItems = [];
13112     
13113     this.selectedIndex = -1;
13114     if(this.mode == 'local'){
13115         if(config.queryDelay === undefined){
13116             this.queryDelay = 10;
13117         }
13118         if(config.minChars === undefined){
13119             this.minChars = 0;
13120         }
13121     }
13122 };
13123
13124 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13125      
13126     /**
13127      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13128      * rendering into an Roo.Editor, defaults to false)
13129      */
13130     /**
13131      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13132      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13133      */
13134     /**
13135      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13136      */
13137     /**
13138      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13139      * the dropdown list (defaults to undefined, with no header element)
13140      */
13141
13142      /**
13143      * @cfg {String/Roo.Template} tpl The template to use to render the output
13144      */
13145      
13146      /**
13147      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13148      */
13149     listWidth: undefined,
13150     /**
13151      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13152      * mode = 'remote' or 'text' if mode = 'local')
13153      */
13154     displayField: undefined,
13155     
13156     /**
13157      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13158      * mode = 'remote' or 'value' if mode = 'local'). 
13159      * Note: use of a valueField requires the user make a selection
13160      * in order for a value to be mapped.
13161      */
13162     valueField: undefined,
13163     /**
13164      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13165      */
13166     modalTitle : '',
13167     
13168     /**
13169      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13170      * field's data value (defaults to the underlying DOM element's name)
13171      */
13172     hiddenName: undefined,
13173     /**
13174      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13175      */
13176     listClass: '',
13177     /**
13178      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13179      */
13180     selectedClass: 'active',
13181     
13182     /**
13183      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13184      */
13185     shadow:'sides',
13186     /**
13187      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13188      * anchor positions (defaults to 'tl-bl')
13189      */
13190     listAlign: 'tl-bl?',
13191     /**
13192      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13193      */
13194     maxHeight: 300,
13195     /**
13196      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13197      * query specified by the allQuery config option (defaults to 'query')
13198      */
13199     triggerAction: 'query',
13200     /**
13201      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13202      * (defaults to 4, does not apply if editable = false)
13203      */
13204     minChars : 4,
13205     /**
13206      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13207      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13208      */
13209     typeAhead: false,
13210     /**
13211      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13212      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13213      */
13214     queryDelay: 500,
13215     /**
13216      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13217      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13218      */
13219     pageSize: 0,
13220     /**
13221      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13222      * when editable = true (defaults to false)
13223      */
13224     selectOnFocus:false,
13225     /**
13226      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13227      */
13228     queryParam: 'query',
13229     /**
13230      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13231      * when mode = 'remote' (defaults to 'Loading...')
13232      */
13233     loadingText: 'Loading...',
13234     /**
13235      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13236      */
13237     resizable: false,
13238     /**
13239      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13240      */
13241     handleHeight : 8,
13242     /**
13243      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13244      * traditional select (defaults to true)
13245      */
13246     editable: true,
13247     /**
13248      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13249      */
13250     allQuery: '',
13251     /**
13252      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13253      */
13254     mode: 'remote',
13255     /**
13256      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13257      * listWidth has a higher value)
13258      */
13259     minListWidth : 70,
13260     /**
13261      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13262      * allow the user to set arbitrary text into the field (defaults to false)
13263      */
13264     forceSelection:false,
13265     /**
13266      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13267      * if typeAhead = true (defaults to 250)
13268      */
13269     typeAheadDelay : 250,
13270     /**
13271      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13272      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13273      */
13274     valueNotFoundText : undefined,
13275     /**
13276      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13277      */
13278     blockFocus : false,
13279     
13280     /**
13281      * @cfg {Boolean} disableClear Disable showing of clear button.
13282      */
13283     disableClear : false,
13284     /**
13285      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13286      */
13287     alwaysQuery : false,
13288     
13289     /**
13290      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13291      */
13292     multiple : false,
13293     
13294     /**
13295      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13296      */
13297     invalidClass : "has-warning",
13298     
13299     /**
13300      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13301      */
13302     validClass : "has-success",
13303     
13304     /**
13305      * @cfg {Boolean} specialFilter (true|false) special filter default false
13306      */
13307     specialFilter : false,
13308     
13309     /**
13310      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13311      */
13312     mobileTouchView : true,
13313     
13314     /**
13315      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13316      */
13317     useNativeIOS : false,
13318     
13319     /**
13320      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13321      */
13322     mobile_restrict_height : false,
13323     
13324     ios_options : false,
13325     
13326     //private
13327     addicon : false,
13328     editicon: false,
13329     
13330     page: 0,
13331     hasQuery: false,
13332     append: false,
13333     loadNext: false,
13334     autoFocus : true,
13335     tickable : false,
13336     btnPosition : 'right',
13337     triggerList : true,
13338     showToggleBtn : true,
13339     animate : true,
13340     emptyResultText: 'Empty',
13341     triggerText : 'Select',
13342     emptyTitle : '',
13343     
13344     // element that contains real text value.. (when hidden is used..)
13345     
13346     getAutoCreate : function()
13347     {   
13348         var cfg = false;
13349         //render
13350         /*
13351          * Render classic select for iso
13352          */
13353         
13354         if(Roo.isIOS && this.useNativeIOS){
13355             cfg = this.getAutoCreateNativeIOS();
13356             return cfg;
13357         }
13358         
13359         /*
13360          * Touch Devices
13361          */
13362         
13363         if(Roo.isTouch && this.mobileTouchView){
13364             cfg = this.getAutoCreateTouchView();
13365             return cfg;;
13366         }
13367         
13368         /*
13369          *  Normal ComboBox
13370          */
13371         if(!this.tickable){
13372             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13373             return cfg;
13374         }
13375         
13376         /*
13377          *  ComboBox with tickable selections
13378          */
13379              
13380         var align = this.labelAlign || this.parentLabelAlign();
13381         
13382         cfg = {
13383             cls : 'form-group roo-combobox-tickable' //input-group
13384         };
13385         
13386         var btn_text_select = '';
13387         var btn_text_done = '';
13388         var btn_text_cancel = '';
13389         
13390         if (this.btn_text_show) {
13391             btn_text_select = 'Select';
13392             btn_text_done = 'Done';
13393             btn_text_cancel = 'Cancel'; 
13394         }
13395         
13396         var buttons = {
13397             tag : 'div',
13398             cls : 'tickable-buttons',
13399             cn : [
13400                 {
13401                     tag : 'button',
13402                     type : 'button',
13403                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13404                     //html : this.triggerText
13405                     html: btn_text_select
13406                 },
13407                 {
13408                     tag : 'button',
13409                     type : 'button',
13410                     name : 'ok',
13411                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13412                     //html : 'Done'
13413                     html: btn_text_done
13414                 },
13415                 {
13416                     tag : 'button',
13417                     type : 'button',
13418                     name : 'cancel',
13419                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13420                     //html : 'Cancel'
13421                     html: btn_text_cancel
13422                 }
13423             ]
13424         };
13425         
13426         if(this.editable){
13427             buttons.cn.unshift({
13428                 tag: 'input',
13429                 cls: 'roo-select2-search-field-input'
13430             });
13431         }
13432         
13433         var _this = this;
13434         
13435         Roo.each(buttons.cn, function(c){
13436             if (_this.size) {
13437                 c.cls += ' btn-' + _this.size;
13438             }
13439
13440             if (_this.disabled) {
13441                 c.disabled = true;
13442             }
13443         });
13444         
13445         var box = {
13446             tag: 'div',
13447             style : 'display: contents',
13448             cn: [
13449                 {
13450                     tag: 'input',
13451                     type : 'hidden',
13452                     cls: 'form-hidden-field'
13453                 },
13454                 {
13455                     tag: 'ul',
13456                     cls: 'roo-select2-choices',
13457                     cn:[
13458                         {
13459                             tag: 'li',
13460                             cls: 'roo-select2-search-field',
13461                             cn: [
13462                                 buttons
13463                             ]
13464                         }
13465                     ]
13466                 }
13467             ]
13468         };
13469         
13470         var combobox = {
13471             cls: 'roo-select2-container input-group roo-select2-container-multi',
13472             cn: [
13473                 
13474                 box
13475 //                {
13476 //                    tag: 'ul',
13477 //                    cls: 'typeahead typeahead-long dropdown-menu',
13478 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13479 //                }
13480             ]
13481         };
13482         
13483         if(this.hasFeedback && !this.allowBlank){
13484             
13485             var feedback = {
13486                 tag: 'span',
13487                 cls: 'glyphicon form-control-feedback'
13488             };
13489
13490             combobox.cn.push(feedback);
13491         }
13492         
13493         var indicator = {
13494             tag : 'i',
13495             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13496             tooltip : 'This field is required'
13497         };
13498         if (Roo.bootstrap.version == 4) {
13499             indicator = {
13500                 tag : 'i',
13501                 style : 'display:none'
13502             };
13503         }
13504         if (align ==='left' && this.fieldLabel.length) {
13505             
13506             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13507             
13508             cfg.cn = [
13509                 indicator,
13510                 {
13511                     tag: 'label',
13512                     'for' :  id,
13513                     cls : 'control-label col-form-label',
13514                     html : this.fieldLabel
13515
13516                 },
13517                 {
13518                     cls : "", 
13519                     cn: [
13520                         combobox
13521                     ]
13522                 }
13523
13524             ];
13525             
13526             var labelCfg = cfg.cn[1];
13527             var contentCfg = cfg.cn[2];
13528             
13529
13530             if(this.indicatorpos == 'right'){
13531                 
13532                 cfg.cn = [
13533                     {
13534                         tag: 'label',
13535                         'for' :  id,
13536                         cls : 'control-label col-form-label',
13537                         cn : [
13538                             {
13539                                 tag : 'span',
13540                                 html : this.fieldLabel
13541                             },
13542                             indicator
13543                         ]
13544                     },
13545                     {
13546                         cls : "",
13547                         cn: [
13548                             combobox
13549                         ]
13550                     }
13551
13552                 ];
13553                 
13554                 
13555                 
13556                 labelCfg = cfg.cn[0];
13557                 contentCfg = cfg.cn[1];
13558             
13559             }
13560             
13561             if(this.labelWidth > 12){
13562                 labelCfg.style = "width: " + this.labelWidth + 'px';
13563             }
13564             
13565             if(this.labelWidth < 13 && this.labelmd == 0){
13566                 this.labelmd = this.labelWidth;
13567             }
13568             
13569             if(this.labellg > 0){
13570                 labelCfg.cls += ' col-lg-' + this.labellg;
13571                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13572             }
13573             
13574             if(this.labelmd > 0){
13575                 labelCfg.cls += ' col-md-' + this.labelmd;
13576                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13577             }
13578             
13579             if(this.labelsm > 0){
13580                 labelCfg.cls += ' col-sm-' + this.labelsm;
13581                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13582             }
13583             
13584             if(this.labelxs > 0){
13585                 labelCfg.cls += ' col-xs-' + this.labelxs;
13586                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13587             }
13588                 
13589                 
13590         } else if ( this.fieldLabel.length) {
13591 //                Roo.log(" label");
13592                  cfg.cn = [
13593                    indicator,
13594                     {
13595                         tag: 'label',
13596                         //cls : 'input-group-addon',
13597                         html : this.fieldLabel
13598                     },
13599                     combobox
13600                 ];
13601                 
13602                 if(this.indicatorpos == 'right'){
13603                     cfg.cn = [
13604                         {
13605                             tag: 'label',
13606                             //cls : 'input-group-addon',
13607                             html : this.fieldLabel
13608                         },
13609                         indicator,
13610                         combobox
13611                     ];
13612                     
13613                 }
13614
13615         } else {
13616             
13617 //                Roo.log(" no label && no align");
13618                 cfg = combobox
13619                      
13620                 
13621         }
13622          
13623         var settings=this;
13624         ['xs','sm','md','lg'].map(function(size){
13625             if (settings[size]) {
13626                 cfg.cls += ' col-' + size + '-' + settings[size];
13627             }
13628         });
13629         
13630         return cfg;
13631         
13632     },
13633     
13634     _initEventsCalled : false,
13635     
13636     // private
13637     initEvents: function()
13638     {   
13639         if (this._initEventsCalled) { // as we call render... prevent looping...
13640             return;
13641         }
13642         this._initEventsCalled = true;
13643         
13644         if (!this.store) {
13645             throw "can not find store for combo";
13646         }
13647         
13648         this.indicator = this.indicatorEl();
13649         
13650         this.store = Roo.factory(this.store, Roo.data);
13651         this.store.parent = this;
13652         
13653         // if we are building from html. then this element is so complex, that we can not really
13654         // use the rendered HTML.
13655         // so we have to trash and replace the previous code.
13656         if (Roo.XComponent.build_from_html) {
13657             // remove this element....
13658             var e = this.el.dom, k=0;
13659             while (e ) { e = e.previousSibling;  ++k;}
13660
13661             this.el.remove();
13662             
13663             this.el=false;
13664             this.rendered = false;
13665             
13666             this.render(this.parent().getChildContainer(true), k);
13667         }
13668         
13669         if(Roo.isIOS && this.useNativeIOS){
13670             this.initIOSView();
13671             return;
13672         }
13673         
13674         /*
13675          * Touch Devices
13676          */
13677         
13678         if(Roo.isTouch && this.mobileTouchView){
13679             this.initTouchView();
13680             return;
13681         }
13682         
13683         if(this.tickable){
13684             this.initTickableEvents();
13685             return;
13686         }
13687         
13688         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13689         
13690         if(this.hiddenName){
13691             
13692             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13693             
13694             this.hiddenField.dom.value =
13695                 this.hiddenValue !== undefined ? this.hiddenValue :
13696                 this.value !== undefined ? this.value : '';
13697
13698             // prevent input submission
13699             this.el.dom.removeAttribute('name');
13700             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13701              
13702              
13703         }
13704         //if(Roo.isGecko){
13705         //    this.el.dom.setAttribute('autocomplete', 'off');
13706         //}
13707         
13708         var cls = 'x-combo-list';
13709         
13710         //this.list = new Roo.Layer({
13711         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13712         //});
13713         
13714         var _this = this;
13715         
13716         (function(){
13717             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13718             _this.list.setWidth(lw);
13719         }).defer(100);
13720         
13721         this.list.on('mouseover', this.onViewOver, this);
13722         this.list.on('mousemove', this.onViewMove, this);
13723         this.list.on('scroll', this.onViewScroll, this);
13724         
13725         /*
13726         this.list.swallowEvent('mousewheel');
13727         this.assetHeight = 0;
13728
13729         if(this.title){
13730             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13731             this.assetHeight += this.header.getHeight();
13732         }
13733
13734         this.innerList = this.list.createChild({cls:cls+'-inner'});
13735         this.innerList.on('mouseover', this.onViewOver, this);
13736         this.innerList.on('mousemove', this.onViewMove, this);
13737         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13738         
13739         if(this.allowBlank && !this.pageSize && !this.disableClear){
13740             this.footer = this.list.createChild({cls:cls+'-ft'});
13741             this.pageTb = new Roo.Toolbar(this.footer);
13742            
13743         }
13744         if(this.pageSize){
13745             this.footer = this.list.createChild({cls:cls+'-ft'});
13746             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13747                     {pageSize: this.pageSize});
13748             
13749         }
13750         
13751         if (this.pageTb && this.allowBlank && !this.disableClear) {
13752             var _this = this;
13753             this.pageTb.add(new Roo.Toolbar.Fill(), {
13754                 cls: 'x-btn-icon x-btn-clear',
13755                 text: '&#160;',
13756                 handler: function()
13757                 {
13758                     _this.collapse();
13759                     _this.clearValue();
13760                     _this.onSelect(false, -1);
13761                 }
13762             });
13763         }
13764         if (this.footer) {
13765             this.assetHeight += this.footer.getHeight();
13766         }
13767         */
13768             
13769         if(!this.tpl){
13770             this.tpl = Roo.bootstrap.version == 4 ?
13771                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13772                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13773         }
13774
13775         this.view = new Roo.View(this.list, this.tpl, {
13776             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13777         });
13778         //this.view.wrapEl.setDisplayed(false);
13779         this.view.on('click', this.onViewClick, this);
13780         
13781         
13782         this.store.on('beforeload', this.onBeforeLoad, this);
13783         this.store.on('load', this.onLoad, this);
13784         this.store.on('loadexception', this.onLoadException, this);
13785         /*
13786         if(this.resizable){
13787             this.resizer = new Roo.Resizable(this.list,  {
13788                pinned:true, handles:'se'
13789             });
13790             this.resizer.on('resize', function(r, w, h){
13791                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13792                 this.listWidth = w;
13793                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13794                 this.restrictHeight();
13795             }, this);
13796             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13797         }
13798         */
13799         if(!this.editable){
13800             this.editable = true;
13801             this.setEditable(false);
13802         }
13803         
13804         /*
13805         
13806         if (typeof(this.events.add.listeners) != 'undefined') {
13807             
13808             this.addicon = this.wrap.createChild(
13809                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13810        
13811             this.addicon.on('click', function(e) {
13812                 this.fireEvent('add', this);
13813             }, this);
13814         }
13815         if (typeof(this.events.edit.listeners) != 'undefined') {
13816             
13817             this.editicon = this.wrap.createChild(
13818                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13819             if (this.addicon) {
13820                 this.editicon.setStyle('margin-left', '40px');
13821             }
13822             this.editicon.on('click', function(e) {
13823                 
13824                 // we fire even  if inothing is selected..
13825                 this.fireEvent('edit', this, this.lastData );
13826                 
13827             }, this);
13828         }
13829         */
13830         
13831         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13832             "up" : function(e){
13833                 this.inKeyMode = true;
13834                 this.selectPrev();
13835             },
13836
13837             "down" : function(e){
13838                 if(!this.isExpanded()){
13839                     this.onTriggerClick();
13840                 }else{
13841                     this.inKeyMode = true;
13842                     this.selectNext();
13843                 }
13844             },
13845
13846             "enter" : function(e){
13847 //                this.onViewClick();
13848                 //return true;
13849                 this.collapse();
13850                 
13851                 if(this.fireEvent("specialkey", this, e)){
13852                     this.onViewClick(false);
13853                 }
13854                 
13855                 return true;
13856             },
13857
13858             "esc" : function(e){
13859                 this.collapse();
13860             },
13861
13862             "tab" : function(e){
13863                 this.collapse();
13864                 
13865                 if(this.fireEvent("specialkey", this, e)){
13866                     this.onViewClick(false);
13867                 }
13868                 
13869                 return true;
13870             },
13871
13872             scope : this,
13873
13874             doRelay : function(foo, bar, hname){
13875                 if(hname == 'down' || this.scope.isExpanded()){
13876                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13877                 }
13878                 return true;
13879             },
13880
13881             forceKeyDown: true
13882         });
13883         
13884         
13885         this.queryDelay = Math.max(this.queryDelay || 10,
13886                 this.mode == 'local' ? 10 : 250);
13887         
13888         
13889         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13890         
13891         if(this.typeAhead){
13892             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13893         }
13894         if(this.editable !== false){
13895             this.inputEl().on("keyup", this.onKeyUp, this);
13896         }
13897         if(this.forceSelection){
13898             this.inputEl().on('blur', this.doForce, this);
13899         }
13900         
13901         if(this.multiple){
13902             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13903             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13904         }
13905     },
13906     
13907     initTickableEvents: function()
13908     {   
13909         this.createList();
13910         
13911         if(this.hiddenName){
13912             
13913             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13914             
13915             this.hiddenField.dom.value =
13916                 this.hiddenValue !== undefined ? this.hiddenValue :
13917                 this.value !== undefined ? this.value : '';
13918
13919             // prevent input submission
13920             this.el.dom.removeAttribute('name');
13921             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13922              
13923              
13924         }
13925         
13926 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13927         
13928         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13929         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13930         if(this.triggerList){
13931             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13932         }
13933          
13934         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13935         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13936         
13937         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13938         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13939         
13940         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13941         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13942         
13943         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13944         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13945         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13946         
13947         this.okBtn.hide();
13948         this.cancelBtn.hide();
13949         
13950         var _this = this;
13951         
13952         (function(){
13953             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13954             _this.list.setWidth(lw);
13955         }).defer(100);
13956         
13957         this.list.on('mouseover', this.onViewOver, this);
13958         this.list.on('mousemove', this.onViewMove, this);
13959         
13960         this.list.on('scroll', this.onViewScroll, this);
13961         
13962         if(!this.tpl){
13963             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13964                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13965         }
13966
13967         this.view = new Roo.View(this.list, this.tpl, {
13968             singleSelect:true,
13969             tickable:true,
13970             parent:this,
13971             store: this.store,
13972             selectedClass: this.selectedClass
13973         });
13974         
13975         //this.view.wrapEl.setDisplayed(false);
13976         this.view.on('click', this.onViewClick, this);
13977         
13978         
13979         
13980         this.store.on('beforeload', this.onBeforeLoad, this);
13981         this.store.on('load', this.onLoad, this);
13982         this.store.on('loadexception', this.onLoadException, this);
13983         
13984         if(this.editable){
13985             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13986                 "up" : function(e){
13987                     this.inKeyMode = true;
13988                     this.selectPrev();
13989                 },
13990
13991                 "down" : function(e){
13992                     this.inKeyMode = true;
13993                     this.selectNext();
13994                 },
13995
13996                 "enter" : function(e){
13997                     if(this.fireEvent("specialkey", this, e)){
13998                         this.onViewClick(false);
13999                     }
14000                     
14001                     return true;
14002                 },
14003
14004                 "esc" : function(e){
14005                     this.onTickableFooterButtonClick(e, false, false);
14006                 },
14007
14008                 "tab" : function(e){
14009                     this.fireEvent("specialkey", this, e);
14010                     
14011                     this.onTickableFooterButtonClick(e, false, false);
14012                     
14013                     return true;
14014                 },
14015
14016                 scope : this,
14017
14018                 doRelay : function(e, fn, key){
14019                     if(this.scope.isExpanded()){
14020                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14021                     }
14022                     return true;
14023                 },
14024
14025                 forceKeyDown: true
14026             });
14027         }
14028         
14029         this.queryDelay = Math.max(this.queryDelay || 10,
14030                 this.mode == 'local' ? 10 : 250);
14031         
14032         
14033         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14034         
14035         if(this.typeAhead){
14036             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14037         }
14038         
14039         if(this.editable !== false){
14040             this.tickableInputEl().on("keyup", this.onKeyUp, this);
14041         }
14042         
14043         this.indicator = this.indicatorEl();
14044         
14045         if(this.indicator){
14046             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14047             this.indicator.hide();
14048         }
14049         
14050     },
14051
14052     onDestroy : function(){
14053         if(this.view){
14054             this.view.setStore(null);
14055             this.view.el.removeAllListeners();
14056             this.view.el.remove();
14057             this.view.purgeListeners();
14058         }
14059         if(this.list){
14060             this.list.dom.innerHTML  = '';
14061         }
14062         
14063         if(this.store){
14064             this.store.un('beforeload', this.onBeforeLoad, this);
14065             this.store.un('load', this.onLoad, this);
14066             this.store.un('loadexception', this.onLoadException, this);
14067         }
14068         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14069     },
14070
14071     // private
14072     fireKey : function(e){
14073         if(e.isNavKeyPress() && !this.list.isVisible()){
14074             this.fireEvent("specialkey", this, e);
14075         }
14076     },
14077
14078     // private
14079     onResize: function(w, h){
14080 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14081 //        
14082 //        if(typeof w != 'number'){
14083 //            // we do not handle it!?!?
14084 //            return;
14085 //        }
14086 //        var tw = this.trigger.getWidth();
14087 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14088 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14089 //        var x = w - tw;
14090 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14091 //            
14092 //        //this.trigger.setStyle('left', x+'px');
14093 //        
14094 //        if(this.list && this.listWidth === undefined){
14095 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14096 //            this.list.setWidth(lw);
14097 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14098 //        }
14099         
14100     
14101         
14102     },
14103
14104     /**
14105      * Allow or prevent the user from directly editing the field text.  If false is passed,
14106      * the user will only be able to select from the items defined in the dropdown list.  This method
14107      * is the runtime equivalent of setting the 'editable' config option at config time.
14108      * @param {Boolean} value True to allow the user to directly edit the field text
14109      */
14110     setEditable : function(value){
14111         if(value == this.editable){
14112             return;
14113         }
14114         this.editable = value;
14115         if(!value){
14116             this.inputEl().dom.setAttribute('readOnly', true);
14117             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14118             this.inputEl().addClass('x-combo-noedit');
14119         }else{
14120             this.inputEl().dom.setAttribute('readOnly', false);
14121             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14122             this.inputEl().removeClass('x-combo-noedit');
14123         }
14124     },
14125
14126     // private
14127     
14128     onBeforeLoad : function(combo,opts){
14129         if(!this.hasFocus){
14130             return;
14131         }
14132          if (!opts.add) {
14133             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14134          }
14135         this.restrictHeight();
14136         this.selectedIndex = -1;
14137     },
14138
14139     // private
14140     onLoad : function(){
14141         
14142         this.hasQuery = false;
14143         
14144         if(!this.hasFocus){
14145             return;
14146         }
14147         
14148         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14149             this.loading.hide();
14150         }
14151         
14152         if(this.store.getCount() > 0){
14153             
14154             this.expand();
14155             this.restrictHeight();
14156             if(this.lastQuery == this.allQuery){
14157                 if(this.editable && !this.tickable){
14158                     this.inputEl().dom.select();
14159                 }
14160                 
14161                 if(
14162                     !this.selectByValue(this.value, true) &&
14163                     this.autoFocus && 
14164                     (
14165                         !this.store.lastOptions ||
14166                         typeof(this.store.lastOptions.add) == 'undefined' || 
14167                         this.store.lastOptions.add != true
14168                     )
14169                 ){
14170                     this.select(0, true);
14171                 }
14172             }else{
14173                 if(this.autoFocus){
14174                     this.selectNext();
14175                 }
14176                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14177                     this.taTask.delay(this.typeAheadDelay);
14178                 }
14179             }
14180         }else{
14181             this.onEmptyResults();
14182         }
14183         
14184         //this.el.focus();
14185     },
14186     // private
14187     onLoadException : function()
14188     {
14189         this.hasQuery = false;
14190         
14191         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14192             this.loading.hide();
14193         }
14194         
14195         if(this.tickable && this.editable){
14196             return;
14197         }
14198         
14199         this.collapse();
14200         // only causes errors at present
14201         //Roo.log(this.store.reader.jsonData);
14202         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14203             // fixme
14204             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14205         //}
14206         
14207         
14208     },
14209     // private
14210     onTypeAhead : function(){
14211         if(this.store.getCount() > 0){
14212             var r = this.store.getAt(0);
14213             var newValue = r.data[this.displayField];
14214             var len = newValue.length;
14215             var selStart = this.getRawValue().length;
14216             
14217             if(selStart != len){
14218                 this.setRawValue(newValue);
14219                 this.selectText(selStart, newValue.length);
14220             }
14221         }
14222     },
14223
14224     // private
14225     onSelect : function(record, index){
14226         
14227         if(this.fireEvent('beforeselect', this, record, index) !== false){
14228         
14229             this.setFromData(index > -1 ? record.data : false);
14230             
14231             this.collapse();
14232             this.fireEvent('select', this, record, index);
14233         }
14234     },
14235
14236     /**
14237      * Returns the currently selected field value or empty string if no value is set.
14238      * @return {String} value The selected value
14239      */
14240     getValue : function()
14241     {
14242         if(Roo.isIOS && this.useNativeIOS){
14243             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14244         }
14245         
14246         if(this.multiple){
14247             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14248         }
14249         
14250         if(this.valueField){
14251             return typeof this.value != 'undefined' ? this.value : '';
14252         }else{
14253             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14254         }
14255     },
14256     
14257     getRawValue : function()
14258     {
14259         if(Roo.isIOS && this.useNativeIOS){
14260             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14261         }
14262         
14263         var v = this.inputEl().getValue();
14264         
14265         return v;
14266     },
14267
14268     /**
14269      * Clears any text/value currently set in the field
14270      */
14271     clearValue : function(){
14272         
14273         if(this.hiddenField){
14274             this.hiddenField.dom.value = '';
14275         }
14276         this.value = '';
14277         this.setRawValue('');
14278         this.lastSelectionText = '';
14279         this.lastData = false;
14280         
14281         var close = this.closeTriggerEl();
14282         
14283         if(close){
14284             close.hide();
14285         }
14286         
14287         this.validate();
14288         
14289     },
14290
14291     /**
14292      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14293      * will be displayed in the field.  If the value does not match the data value of an existing item,
14294      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14295      * Otherwise the field will be blank (although the value will still be set).
14296      * @param {String} value The value to match
14297      */
14298     setValue : function(v)
14299     {
14300         if(Roo.isIOS && this.useNativeIOS){
14301             this.setIOSValue(v);
14302             return;
14303         }
14304         
14305         if(this.multiple){
14306             this.syncValue();
14307             return;
14308         }
14309         
14310         var text = v;
14311         if(this.valueField){
14312             var r = this.findRecord(this.valueField, v);
14313             if(r){
14314                 text = r.data[this.displayField];
14315             }else if(this.valueNotFoundText !== undefined){
14316                 text = this.valueNotFoundText;
14317             }
14318         }
14319         this.lastSelectionText = text;
14320         if(this.hiddenField){
14321             this.hiddenField.dom.value = v;
14322         }
14323         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14324         this.value = v;
14325         
14326         var close = this.closeTriggerEl();
14327         
14328         if(close){
14329             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14330         }
14331         
14332         this.validate();
14333     },
14334     /**
14335      * @property {Object} the last set data for the element
14336      */
14337     
14338     lastData : false,
14339     /**
14340      * Sets the value of the field based on a object which is related to the record format for the store.
14341      * @param {Object} value the value to set as. or false on reset?
14342      */
14343     setFromData : function(o){
14344         
14345         if(this.multiple){
14346             this.addItem(o);
14347             return;
14348         }
14349             
14350         var dv = ''; // display value
14351         var vv = ''; // value value..
14352         this.lastData = o;
14353         if (this.displayField) {
14354             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14355         } else {
14356             // this is an error condition!!!
14357             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14358         }
14359         
14360         if(this.valueField){
14361             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14362         }
14363         
14364         var close = this.closeTriggerEl();
14365         
14366         if(close){
14367             if(dv.length || vv * 1 > 0){
14368                 close.show() ;
14369                 this.blockFocus=true;
14370             } else {
14371                 close.hide();
14372             }             
14373         }
14374         
14375         if(this.hiddenField){
14376             this.hiddenField.dom.value = vv;
14377             
14378             this.lastSelectionText = dv;
14379             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14380             this.value = vv;
14381             return;
14382         }
14383         // no hidden field.. - we store the value in 'value', but still display
14384         // display field!!!!
14385         this.lastSelectionText = dv;
14386         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14387         this.value = vv;
14388         
14389         
14390         
14391     },
14392     // private
14393     reset : function(){
14394         // overridden so that last data is reset..
14395         
14396         if(this.multiple){
14397             this.clearItem();
14398             return;
14399         }
14400         
14401         this.setValue(this.originalValue);
14402         //this.clearInvalid();
14403         this.lastData = false;
14404         if (this.view) {
14405             this.view.clearSelections();
14406         }
14407         
14408         this.validate();
14409     },
14410     // private
14411     findRecord : function(prop, value){
14412         var record;
14413         if(this.store.getCount() > 0){
14414             this.store.each(function(r){
14415                 if(r.data[prop] == value){
14416                     record = r;
14417                     return false;
14418                 }
14419                 return true;
14420             });
14421         }
14422         return record;
14423     },
14424     
14425     getName: function()
14426     {
14427         // returns hidden if it's set..
14428         if (!this.rendered) {return ''};
14429         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14430         
14431     },
14432     // private
14433     onViewMove : function(e, t){
14434         this.inKeyMode = false;
14435     },
14436
14437     // private
14438     onViewOver : function(e, t){
14439         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14440             return;
14441         }
14442         var item = this.view.findItemFromChild(t);
14443         
14444         if(item){
14445             var index = this.view.indexOf(item);
14446             this.select(index, false);
14447         }
14448     },
14449
14450     // private
14451     onViewClick : function(view, doFocus, el, e)
14452     {
14453         var index = this.view.getSelectedIndexes()[0];
14454         
14455         var r = this.store.getAt(index);
14456         
14457         if(this.tickable){
14458             
14459             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14460                 return;
14461             }
14462             
14463             var rm = false;
14464             var _this = this;
14465             
14466             Roo.each(this.tickItems, function(v,k){
14467                 
14468                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14469                     Roo.log(v);
14470                     _this.tickItems.splice(k, 1);
14471                     
14472                     if(typeof(e) == 'undefined' && view == false){
14473                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14474                     }
14475                     
14476                     rm = true;
14477                     return;
14478                 }
14479             });
14480             
14481             if(rm){
14482                 return;
14483             }
14484             
14485             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14486                 this.tickItems.push(r.data);
14487             }
14488             
14489             if(typeof(e) == 'undefined' && view == false){
14490                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14491             }
14492                     
14493             return;
14494         }
14495         
14496         if(r){
14497             this.onSelect(r, index);
14498         }
14499         if(doFocus !== false && !this.blockFocus){
14500             this.inputEl().focus();
14501         }
14502     },
14503
14504     // private
14505     restrictHeight : function(){
14506         //this.innerList.dom.style.height = '';
14507         //var inner = this.innerList.dom;
14508         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14509         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14510         //this.list.beginUpdate();
14511         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14512         this.list.alignTo(this.inputEl(), this.listAlign);
14513         this.list.alignTo(this.inputEl(), this.listAlign);
14514         //this.list.endUpdate();
14515     },
14516
14517     // private
14518     onEmptyResults : function(){
14519         
14520         if(this.tickable && this.editable){
14521             this.hasFocus = false;
14522             this.restrictHeight();
14523             return;
14524         }
14525         
14526         this.collapse();
14527     },
14528
14529     /**
14530      * Returns true if the dropdown list is expanded, else false.
14531      */
14532     isExpanded : function(){
14533         return this.list.isVisible();
14534     },
14535
14536     /**
14537      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14538      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14539      * @param {String} value The data value of the item to select
14540      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14541      * selected item if it is not currently in view (defaults to true)
14542      * @return {Boolean} True if the value matched an item in the list, else false
14543      */
14544     selectByValue : function(v, scrollIntoView){
14545         if(v !== undefined && v !== null){
14546             var r = this.findRecord(this.valueField || this.displayField, v);
14547             if(r){
14548                 this.select(this.store.indexOf(r), scrollIntoView);
14549                 return true;
14550             }
14551         }
14552         return false;
14553     },
14554
14555     /**
14556      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14557      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14558      * @param {Number} index The zero-based index of the list item to select
14559      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14560      * selected item if it is not currently in view (defaults to true)
14561      */
14562     select : function(index, scrollIntoView){
14563         this.selectedIndex = index;
14564         this.view.select(index);
14565         if(scrollIntoView !== false){
14566             var el = this.view.getNode(index);
14567             /*
14568              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14569              */
14570             if(el){
14571                 this.list.scrollChildIntoView(el, false);
14572             }
14573         }
14574     },
14575
14576     // private
14577     selectNext : function(){
14578         var ct = this.store.getCount();
14579         if(ct > 0){
14580             if(this.selectedIndex == -1){
14581                 this.select(0);
14582             }else if(this.selectedIndex < ct-1){
14583                 this.select(this.selectedIndex+1);
14584             }
14585         }
14586     },
14587
14588     // private
14589     selectPrev : function(){
14590         var ct = this.store.getCount();
14591         if(ct > 0){
14592             if(this.selectedIndex == -1){
14593                 this.select(0);
14594             }else if(this.selectedIndex != 0){
14595                 this.select(this.selectedIndex-1);
14596             }
14597         }
14598     },
14599
14600     // private
14601     onKeyUp : function(e){
14602         if(this.editable !== false && !e.isSpecialKey()){
14603             this.lastKey = e.getKey();
14604             this.dqTask.delay(this.queryDelay);
14605         }
14606     },
14607
14608     // private
14609     validateBlur : function(){
14610         return !this.list || !this.list.isVisible();   
14611     },
14612
14613     // private
14614     initQuery : function(){
14615         
14616         var v = this.getRawValue();
14617         
14618         if(this.tickable && this.editable){
14619             v = this.tickableInputEl().getValue();
14620         }
14621         
14622         this.doQuery(v);
14623     },
14624
14625     // private
14626     doForce : function(){
14627         if(this.inputEl().dom.value.length > 0){
14628             this.inputEl().dom.value =
14629                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14630              
14631         }
14632     },
14633
14634     /**
14635      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14636      * query allowing the query action to be canceled if needed.
14637      * @param {String} query The SQL query to execute
14638      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14639      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14640      * saved in the current store (defaults to false)
14641      */
14642     doQuery : function(q, forceAll){
14643         
14644         if(q === undefined || q === null){
14645             q = '';
14646         }
14647         var qe = {
14648             query: q,
14649             forceAll: forceAll,
14650             combo: this,
14651             cancel:false
14652         };
14653         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14654             return false;
14655         }
14656         q = qe.query;
14657         
14658         forceAll = qe.forceAll;
14659         if(forceAll === true || (q.length >= this.minChars)){
14660             
14661             this.hasQuery = true;
14662             
14663             if(this.lastQuery != q || this.alwaysQuery){
14664                 this.lastQuery = q;
14665                 if(this.mode == 'local'){
14666                     this.selectedIndex = -1;
14667                     if(forceAll){
14668                         this.store.clearFilter();
14669                     }else{
14670                         
14671                         if(this.specialFilter){
14672                             this.fireEvent('specialfilter', this);
14673                             this.onLoad();
14674                             return;
14675                         }
14676                         
14677                         this.store.filter(this.displayField, q);
14678                     }
14679                     
14680                     this.store.fireEvent("datachanged", this.store);
14681                     
14682                     this.onLoad();
14683                     
14684                     
14685                 }else{
14686                     
14687                     this.store.baseParams[this.queryParam] = q;
14688                     
14689                     var options = {params : this.getParams(q)};
14690                     
14691                     if(this.loadNext){
14692                         options.add = true;
14693                         options.params.start = this.page * this.pageSize;
14694                     }
14695                     
14696                     this.store.load(options);
14697                     
14698                     /*
14699                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14700                      *  we should expand the list on onLoad
14701                      *  so command out it
14702                      */
14703 //                    this.expand();
14704                 }
14705             }else{
14706                 this.selectedIndex = -1;
14707                 this.onLoad();   
14708             }
14709         }
14710         
14711         this.loadNext = false;
14712     },
14713     
14714     // private
14715     getParams : function(q){
14716         var p = {};
14717         //p[this.queryParam] = q;
14718         
14719         if(this.pageSize){
14720             p.start = 0;
14721             p.limit = this.pageSize;
14722         }
14723         return p;
14724     },
14725
14726     /**
14727      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14728      */
14729     collapse : function(){
14730         if(!this.isExpanded()){
14731             return;
14732         }
14733         
14734         this.list.hide();
14735         
14736         this.hasFocus = false;
14737         
14738         if(this.tickable){
14739             this.okBtn.hide();
14740             this.cancelBtn.hide();
14741             this.trigger.show();
14742             
14743             if(this.editable){
14744                 this.tickableInputEl().dom.value = '';
14745                 this.tickableInputEl().blur();
14746             }
14747             
14748         }
14749         
14750         Roo.get(document).un('mousedown', this.collapseIf, this);
14751         Roo.get(document).un('mousewheel', this.collapseIf, this);
14752         if (!this.editable) {
14753             Roo.get(document).un('keydown', this.listKeyPress, this);
14754         }
14755         this.fireEvent('collapse', this);
14756         
14757         this.validate();
14758     },
14759
14760     // private
14761     collapseIf : function(e){
14762         var in_combo  = e.within(this.el);
14763         var in_list =  e.within(this.list);
14764         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14765         
14766         if (in_combo || in_list || is_list) {
14767             //e.stopPropagation();
14768             return;
14769         }
14770         
14771         if(this.tickable){
14772             this.onTickableFooterButtonClick(e, false, false);
14773         }
14774
14775         this.collapse();
14776         
14777     },
14778
14779     /**
14780      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14781      */
14782     expand : function(){
14783        
14784         if(this.isExpanded() || !this.hasFocus){
14785             return;
14786         }
14787         
14788         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14789         this.list.setWidth(lw);
14790         
14791         Roo.log('expand');
14792         
14793         this.list.show();
14794         
14795         this.restrictHeight();
14796         
14797         if(this.tickable){
14798             
14799             this.tickItems = Roo.apply([], this.item);
14800             
14801             this.okBtn.show();
14802             this.cancelBtn.show();
14803             this.trigger.hide();
14804             
14805             if(this.editable){
14806                 this.tickableInputEl().focus();
14807             }
14808             
14809         }
14810         
14811         Roo.get(document).on('mousedown', this.collapseIf, this);
14812         Roo.get(document).on('mousewheel', this.collapseIf, this);
14813         if (!this.editable) {
14814             Roo.get(document).on('keydown', this.listKeyPress, this);
14815         }
14816         
14817         this.fireEvent('expand', this);
14818     },
14819
14820     // private
14821     // Implements the default empty TriggerField.onTriggerClick function
14822     onTriggerClick : function(e)
14823     {
14824         Roo.log('trigger click');
14825         
14826         if(this.disabled || !this.triggerList){
14827             return;
14828         }
14829         
14830         this.page = 0;
14831         this.loadNext = false;
14832         
14833         if(this.isExpanded()){
14834             this.collapse();
14835             if (!this.blockFocus) {
14836                 this.inputEl().focus();
14837             }
14838             
14839         }else {
14840             this.hasFocus = true;
14841             if(this.triggerAction == 'all') {
14842                 this.doQuery(this.allQuery, true);
14843             } else {
14844                 this.doQuery(this.getRawValue());
14845             }
14846             if (!this.blockFocus) {
14847                 this.inputEl().focus();
14848             }
14849         }
14850     },
14851     
14852     onTickableTriggerClick : function(e)
14853     {
14854         if(this.disabled){
14855             return;
14856         }
14857         
14858         this.page = 0;
14859         this.loadNext = false;
14860         this.hasFocus = true;
14861         
14862         if(this.triggerAction == 'all') {
14863             this.doQuery(this.allQuery, true);
14864         } else {
14865             this.doQuery(this.getRawValue());
14866         }
14867     },
14868     
14869     onSearchFieldClick : function(e)
14870     {
14871         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14872             this.onTickableFooterButtonClick(e, false, false);
14873             return;
14874         }
14875         
14876         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14877             return;
14878         }
14879         
14880         this.page = 0;
14881         this.loadNext = false;
14882         this.hasFocus = true;
14883         
14884         if(this.triggerAction == 'all') {
14885             this.doQuery(this.allQuery, true);
14886         } else {
14887             this.doQuery(this.getRawValue());
14888         }
14889     },
14890     
14891     listKeyPress : function(e)
14892     {
14893         //Roo.log('listkeypress');
14894         // scroll to first matching element based on key pres..
14895         if (e.isSpecialKey()) {
14896             return false;
14897         }
14898         var k = String.fromCharCode(e.getKey()).toUpperCase();
14899         //Roo.log(k);
14900         var match  = false;
14901         var csel = this.view.getSelectedNodes();
14902         var cselitem = false;
14903         if (csel.length) {
14904             var ix = this.view.indexOf(csel[0]);
14905             cselitem  = this.store.getAt(ix);
14906             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14907                 cselitem = false;
14908             }
14909             
14910         }
14911         
14912         this.store.each(function(v) { 
14913             if (cselitem) {
14914                 // start at existing selection.
14915                 if (cselitem.id == v.id) {
14916                     cselitem = false;
14917                 }
14918                 return true;
14919             }
14920                 
14921             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14922                 match = this.store.indexOf(v);
14923                 return false;
14924             }
14925             return true;
14926         }, this);
14927         
14928         if (match === false) {
14929             return true; // no more action?
14930         }
14931         // scroll to?
14932         this.view.select(match);
14933         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14934         sn.scrollIntoView(sn.dom.parentNode, false);
14935     },
14936     
14937     onViewScroll : function(e, t){
14938         
14939         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){
14940             return;
14941         }
14942         
14943         this.hasQuery = true;
14944         
14945         this.loading = this.list.select('.loading', true).first();
14946         
14947         if(this.loading === null){
14948             this.list.createChild({
14949                 tag: 'div',
14950                 cls: 'loading roo-select2-more-results roo-select2-active',
14951                 html: 'Loading more results...'
14952             });
14953             
14954             this.loading = this.list.select('.loading', true).first();
14955             
14956             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14957             
14958             this.loading.hide();
14959         }
14960         
14961         this.loading.show();
14962         
14963         var _combo = this;
14964         
14965         this.page++;
14966         this.loadNext = true;
14967         
14968         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14969         
14970         return;
14971     },
14972     
14973     addItem : function(o)
14974     {   
14975         var dv = ''; // display value
14976         
14977         if (this.displayField) {
14978             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14979         } else {
14980             // this is an error condition!!!
14981             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14982         }
14983         
14984         if(!dv.length){
14985             return;
14986         }
14987         
14988         var choice = this.choices.createChild({
14989             tag: 'li',
14990             cls: 'roo-select2-search-choice',
14991             cn: [
14992                 {
14993                     tag: 'div',
14994                     html: dv
14995                 },
14996                 {
14997                     tag: 'a',
14998                     href: '#',
14999                     cls: 'roo-select2-search-choice-close fa fa-times',
15000                     tabindex: '-1'
15001                 }
15002             ]
15003             
15004         }, this.searchField);
15005         
15006         var close = choice.select('a.roo-select2-search-choice-close', true).first();
15007         
15008         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15009         
15010         this.item.push(o);
15011         
15012         this.lastData = o;
15013         
15014         this.syncValue();
15015         
15016         this.inputEl().dom.value = '';
15017         
15018         this.validate();
15019     },
15020     
15021     onRemoveItem : function(e, _self, o)
15022     {
15023         e.preventDefault();
15024         
15025         this.lastItem = Roo.apply([], this.item);
15026         
15027         var index = this.item.indexOf(o.data) * 1;
15028         
15029         if( index < 0){
15030             Roo.log('not this item?!');
15031             return;
15032         }
15033         
15034         this.item.splice(index, 1);
15035         o.item.remove();
15036         
15037         this.syncValue();
15038         
15039         this.fireEvent('remove', this, e);
15040         
15041         this.validate();
15042         
15043     },
15044     
15045     syncValue : function()
15046     {
15047         if(!this.item.length){
15048             this.clearValue();
15049             return;
15050         }
15051             
15052         var value = [];
15053         var _this = this;
15054         Roo.each(this.item, function(i){
15055             if(_this.valueField){
15056                 value.push(i[_this.valueField]);
15057                 return;
15058             }
15059
15060             value.push(i);
15061         });
15062
15063         this.value = value.join(',');
15064
15065         if(this.hiddenField){
15066             this.hiddenField.dom.value = this.value;
15067         }
15068         
15069         this.store.fireEvent("datachanged", this.store);
15070         
15071         this.validate();
15072     },
15073     
15074     clearItem : function()
15075     {
15076         if(!this.multiple){
15077             return;
15078         }
15079         
15080         this.item = [];
15081         
15082         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15083            c.remove();
15084         });
15085         
15086         this.syncValue();
15087         
15088         this.validate();
15089         
15090         if(this.tickable && !Roo.isTouch){
15091             this.view.refresh();
15092         }
15093     },
15094     
15095     inputEl: function ()
15096     {
15097         if(Roo.isIOS && this.useNativeIOS){
15098             return this.el.select('select.roo-ios-select', true).first();
15099         }
15100         
15101         if(Roo.isTouch && this.mobileTouchView){
15102             return this.el.select('input.form-control',true).first();
15103         }
15104         
15105         if(this.tickable){
15106             return this.searchField;
15107         }
15108         
15109         return this.el.select('input.form-control',true).first();
15110     },
15111     
15112     onTickableFooterButtonClick : function(e, btn, el)
15113     {
15114         e.preventDefault();
15115         
15116         this.lastItem = Roo.apply([], this.item);
15117         
15118         if(btn && btn.name == 'cancel'){
15119             this.tickItems = Roo.apply([], this.item);
15120             this.collapse();
15121             return;
15122         }
15123         
15124         this.clearItem();
15125         
15126         var _this = this;
15127         
15128         Roo.each(this.tickItems, function(o){
15129             _this.addItem(o);
15130         });
15131         
15132         this.collapse();
15133         
15134     },
15135     
15136     validate : function()
15137     {
15138         if(this.getVisibilityEl().hasClass('hidden')){
15139             return true;
15140         }
15141         
15142         var v = this.getRawValue();
15143         
15144         if(this.multiple){
15145             v = this.getValue();
15146         }
15147         
15148         if(this.disabled || this.allowBlank || v.length){
15149             this.markValid();
15150             return true;
15151         }
15152         
15153         this.markInvalid();
15154         return false;
15155     },
15156     
15157     tickableInputEl : function()
15158     {
15159         if(!this.tickable || !this.editable){
15160             return this.inputEl();
15161         }
15162         
15163         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15164     },
15165     
15166     
15167     getAutoCreateTouchView : function()
15168     {
15169         var id = Roo.id();
15170         
15171         var cfg = {
15172             cls: 'form-group' //input-group
15173         };
15174         
15175         var input =  {
15176             tag: 'input',
15177             id : id,
15178             type : this.inputType,
15179             cls : 'form-control x-combo-noedit',
15180             autocomplete: 'new-password',
15181             placeholder : this.placeholder || '',
15182             readonly : true
15183         };
15184         
15185         if (this.name) {
15186             input.name = this.name;
15187         }
15188         
15189         if (this.size) {
15190             input.cls += ' input-' + this.size;
15191         }
15192         
15193         if (this.disabled) {
15194             input.disabled = true;
15195         }
15196         
15197         var inputblock = {
15198             cls : '',
15199             cn : [
15200                 input
15201             ]
15202         };
15203         
15204         if(this.before){
15205             inputblock.cls += ' input-group';
15206             
15207             inputblock.cn.unshift({
15208                 tag :'span',
15209                 cls : 'input-group-addon input-group-prepend input-group-text',
15210                 html : this.before
15211             });
15212         }
15213         
15214         if(this.removable && !this.multiple){
15215             inputblock.cls += ' roo-removable';
15216             
15217             inputblock.cn.push({
15218                 tag: 'button',
15219                 html : 'x',
15220                 cls : 'roo-combo-removable-btn close'
15221             });
15222         }
15223
15224         if(this.hasFeedback && !this.allowBlank){
15225             
15226             inputblock.cls += ' has-feedback';
15227             
15228             inputblock.cn.push({
15229                 tag: 'span',
15230                 cls: 'glyphicon form-control-feedback'
15231             });
15232             
15233         }
15234         
15235         if (this.after) {
15236             
15237             inputblock.cls += (this.before) ? '' : ' input-group';
15238             
15239             inputblock.cn.push({
15240                 tag :'span',
15241                 cls : 'input-group-addon input-group-append input-group-text',
15242                 html : this.after
15243             });
15244         }
15245
15246         
15247         var ibwrap = inputblock;
15248         
15249         if(this.multiple){
15250             ibwrap = {
15251                 tag: 'ul',
15252                 cls: 'roo-select2-choices',
15253                 cn:[
15254                     {
15255                         tag: 'li',
15256                         cls: 'roo-select2-search-field',
15257                         cn: [
15258
15259                             inputblock
15260                         ]
15261                     }
15262                 ]
15263             };
15264         
15265             
15266         }
15267         
15268         var combobox = {
15269             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15270             cn: [
15271                 {
15272                     tag: 'input',
15273                     type : 'hidden',
15274                     cls: 'form-hidden-field'
15275                 },
15276                 ibwrap
15277             ]
15278         };
15279         
15280         if(!this.multiple && this.showToggleBtn){
15281             
15282             var caret = {
15283                         tag: 'span',
15284                         cls: 'caret'
15285             };
15286             
15287             if (this.caret != false) {
15288                 caret = {
15289                      tag: 'i',
15290                      cls: 'fa fa-' + this.caret
15291                 };
15292                 
15293             }
15294             
15295             combobox.cn.push({
15296                 tag :'span',
15297                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15298                 cn : [
15299                     caret,
15300                     {
15301                         tag: 'span',
15302                         cls: 'combobox-clear',
15303                         cn  : [
15304                             {
15305                                 tag : 'i',
15306                                 cls: 'icon-remove'
15307                             }
15308                         ]
15309                     }
15310                 ]
15311
15312             })
15313         }
15314         
15315         if(this.multiple){
15316             combobox.cls += ' roo-select2-container-multi';
15317         }
15318         
15319         var align = this.labelAlign || this.parentLabelAlign();
15320         
15321         if (align ==='left' && this.fieldLabel.length) {
15322
15323             cfg.cn = [
15324                 {
15325                    tag : 'i',
15326                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15327                    tooltip : 'This field is required'
15328                 },
15329                 {
15330                     tag: 'label',
15331                     cls : 'control-label col-form-label',
15332                     html : this.fieldLabel
15333
15334                 },
15335                 {
15336                     cls : '', 
15337                     cn: [
15338                         combobox
15339                     ]
15340                 }
15341             ];
15342             
15343             var labelCfg = cfg.cn[1];
15344             var contentCfg = cfg.cn[2];
15345             
15346
15347             if(this.indicatorpos == 'right'){
15348                 cfg.cn = [
15349                     {
15350                         tag: 'label',
15351                         'for' :  id,
15352                         cls : 'control-label col-form-label',
15353                         cn : [
15354                             {
15355                                 tag : 'span',
15356                                 html : this.fieldLabel
15357                             },
15358                             {
15359                                 tag : 'i',
15360                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15361                                 tooltip : 'This field is required'
15362                             }
15363                         ]
15364                     },
15365                     {
15366                         cls : "",
15367                         cn: [
15368                             combobox
15369                         ]
15370                     }
15371
15372                 ];
15373                 
15374                 labelCfg = cfg.cn[0];
15375                 contentCfg = cfg.cn[1];
15376             }
15377             
15378            
15379             
15380             if(this.labelWidth > 12){
15381                 labelCfg.style = "width: " + this.labelWidth + 'px';
15382             }
15383             
15384             if(this.labelWidth < 13 && this.labelmd == 0){
15385                 this.labelmd = this.labelWidth;
15386             }
15387             
15388             if(this.labellg > 0){
15389                 labelCfg.cls += ' col-lg-' + this.labellg;
15390                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15391             }
15392             
15393             if(this.labelmd > 0){
15394                 labelCfg.cls += ' col-md-' + this.labelmd;
15395                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15396             }
15397             
15398             if(this.labelsm > 0){
15399                 labelCfg.cls += ' col-sm-' + this.labelsm;
15400                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15401             }
15402             
15403             if(this.labelxs > 0){
15404                 labelCfg.cls += ' col-xs-' + this.labelxs;
15405                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15406             }
15407                 
15408                 
15409         } else if ( this.fieldLabel.length) {
15410             cfg.cn = [
15411                 {
15412                    tag : 'i',
15413                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15414                    tooltip : 'This field is required'
15415                 },
15416                 {
15417                     tag: 'label',
15418                     cls : 'control-label',
15419                     html : this.fieldLabel
15420
15421                 },
15422                 {
15423                     cls : '', 
15424                     cn: [
15425                         combobox
15426                     ]
15427                 }
15428             ];
15429             
15430             if(this.indicatorpos == 'right'){
15431                 cfg.cn = [
15432                     {
15433                         tag: 'label',
15434                         cls : 'control-label',
15435                         html : this.fieldLabel,
15436                         cn : [
15437                             {
15438                                tag : 'i',
15439                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15440                                tooltip : 'This field is required'
15441                             }
15442                         ]
15443                     },
15444                     {
15445                         cls : '', 
15446                         cn: [
15447                             combobox
15448                         ]
15449                     }
15450                 ];
15451             }
15452         } else {
15453             cfg.cn = combobox;    
15454         }
15455         
15456         
15457         var settings = this;
15458         
15459         ['xs','sm','md','lg'].map(function(size){
15460             if (settings[size]) {
15461                 cfg.cls += ' col-' + size + '-' + settings[size];
15462             }
15463         });
15464         
15465         return cfg;
15466     },
15467     
15468     initTouchView : function()
15469     {
15470         this.renderTouchView();
15471         
15472         this.touchViewEl.on('scroll', function(){
15473             this.el.dom.scrollTop = 0;
15474         }, this);
15475         
15476         this.originalValue = this.getValue();
15477         
15478         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15479         
15480         this.inputEl().on("click", this.showTouchView, this);
15481         if (this.triggerEl) {
15482             this.triggerEl.on("click", this.showTouchView, this);
15483         }
15484         
15485         
15486         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15487         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15488         
15489         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15490         
15491         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15492         this.store.on('load', this.onTouchViewLoad, this);
15493         this.store.on('loadexception', this.onTouchViewLoadException, this);
15494         
15495         if(this.hiddenName){
15496             
15497             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15498             
15499             this.hiddenField.dom.value =
15500                 this.hiddenValue !== undefined ? this.hiddenValue :
15501                 this.value !== undefined ? this.value : '';
15502         
15503             this.el.dom.removeAttribute('name');
15504             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15505         }
15506         
15507         if(this.multiple){
15508             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15509             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15510         }
15511         
15512         if(this.removable && !this.multiple){
15513             var close = this.closeTriggerEl();
15514             if(close){
15515                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15516                 close.on('click', this.removeBtnClick, this, close);
15517             }
15518         }
15519         /*
15520          * fix the bug in Safari iOS8
15521          */
15522         this.inputEl().on("focus", function(e){
15523             document.activeElement.blur();
15524         }, this);
15525         
15526         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15527         
15528         return;
15529         
15530         
15531     },
15532     
15533     renderTouchView : function()
15534     {
15535         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15536         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15537         
15538         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15539         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15540         
15541         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15542         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15543         this.touchViewBodyEl.setStyle('overflow', 'auto');
15544         
15545         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15546         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15547         
15548         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15549         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15550         
15551     },
15552     
15553     showTouchView : function()
15554     {
15555         if(this.disabled){
15556             return;
15557         }
15558         
15559         this.touchViewHeaderEl.hide();
15560
15561         if(this.modalTitle.length){
15562             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15563             this.touchViewHeaderEl.show();
15564         }
15565
15566         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15567         this.touchViewEl.show();
15568
15569         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15570         
15571         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15572         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15573
15574         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15575
15576         if(this.modalTitle.length){
15577             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15578         }
15579         
15580         this.touchViewBodyEl.setHeight(bodyHeight);
15581
15582         if(this.animate){
15583             var _this = this;
15584             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15585         }else{
15586             this.touchViewEl.addClass('in');
15587         }
15588         
15589         if(this._touchViewMask){
15590             Roo.get(document.body).addClass("x-body-masked");
15591             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15592             this._touchViewMask.setStyle('z-index', 10000);
15593             this._touchViewMask.addClass('show');
15594         }
15595         
15596         this.doTouchViewQuery();
15597         
15598     },
15599     
15600     hideTouchView : function()
15601     {
15602         this.touchViewEl.removeClass('in');
15603
15604         if(this.animate){
15605             var _this = this;
15606             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15607         }else{
15608             this.touchViewEl.setStyle('display', 'none');
15609         }
15610         
15611         if(this._touchViewMask){
15612             this._touchViewMask.removeClass('show');
15613             Roo.get(document.body).removeClass("x-body-masked");
15614         }
15615     },
15616     
15617     setTouchViewValue : function()
15618     {
15619         if(this.multiple){
15620             this.clearItem();
15621         
15622             var _this = this;
15623
15624             Roo.each(this.tickItems, function(o){
15625                 this.addItem(o);
15626             }, this);
15627         }
15628         
15629         this.hideTouchView();
15630     },
15631     
15632     doTouchViewQuery : function()
15633     {
15634         var qe = {
15635             query: '',
15636             forceAll: true,
15637             combo: this,
15638             cancel:false
15639         };
15640         
15641         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15642             return false;
15643         }
15644         
15645         if(!this.alwaysQuery || this.mode == 'local'){
15646             this.onTouchViewLoad();
15647             return;
15648         }
15649         
15650         this.store.load();
15651     },
15652     
15653     onTouchViewBeforeLoad : function(combo,opts)
15654     {
15655         return;
15656     },
15657
15658     // private
15659     onTouchViewLoad : function()
15660     {
15661         if(this.store.getCount() < 1){
15662             this.onTouchViewEmptyResults();
15663             return;
15664         }
15665         
15666         this.clearTouchView();
15667         
15668         var rawValue = this.getRawValue();
15669         
15670         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15671         
15672         this.tickItems = [];
15673         
15674         this.store.data.each(function(d, rowIndex){
15675             var row = this.touchViewListGroup.createChild(template);
15676             
15677             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15678                 row.addClass(d.data.cls);
15679             }
15680             
15681             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15682                 var cfg = {
15683                     data : d.data,
15684                     html : d.data[this.displayField]
15685                 };
15686                 
15687                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15688                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15689                 }
15690             }
15691             row.removeClass('selected');
15692             if(!this.multiple && this.valueField &&
15693                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15694             {
15695                 // radio buttons..
15696                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15697                 row.addClass('selected');
15698             }
15699             
15700             if(this.multiple && this.valueField &&
15701                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15702             {
15703                 
15704                 // checkboxes...
15705                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15706                 this.tickItems.push(d.data);
15707             }
15708             
15709             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15710             
15711         }, this);
15712         
15713         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15714         
15715         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15716
15717         if(this.modalTitle.length){
15718             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15719         }
15720
15721         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15722         
15723         if(this.mobile_restrict_height && listHeight < bodyHeight){
15724             this.touchViewBodyEl.setHeight(listHeight);
15725         }
15726         
15727         var _this = this;
15728         
15729         if(firstChecked && listHeight > bodyHeight){
15730             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15731         }
15732         
15733     },
15734     
15735     onTouchViewLoadException : function()
15736     {
15737         this.hideTouchView();
15738     },
15739     
15740     onTouchViewEmptyResults : function()
15741     {
15742         this.clearTouchView();
15743         
15744         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15745         
15746         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15747         
15748     },
15749     
15750     clearTouchView : function()
15751     {
15752         this.touchViewListGroup.dom.innerHTML = '';
15753     },
15754     
15755     onTouchViewClick : function(e, el, o)
15756     {
15757         e.preventDefault();
15758         
15759         var row = o.row;
15760         var rowIndex = o.rowIndex;
15761         
15762         var r = this.store.getAt(rowIndex);
15763         
15764         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15765             
15766             if(!this.multiple){
15767                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15768                     c.dom.removeAttribute('checked');
15769                 }, this);
15770
15771                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15772
15773                 this.setFromData(r.data);
15774
15775                 var close = this.closeTriggerEl();
15776
15777                 if(close){
15778                     close.show();
15779                 }
15780
15781                 this.hideTouchView();
15782
15783                 this.fireEvent('select', this, r, rowIndex);
15784
15785                 return;
15786             }
15787
15788             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15789                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15790                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15791                 return;
15792             }
15793
15794             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15795             this.addItem(r.data);
15796             this.tickItems.push(r.data);
15797         }
15798     },
15799     
15800     getAutoCreateNativeIOS : function()
15801     {
15802         var cfg = {
15803             cls: 'form-group' //input-group,
15804         };
15805         
15806         var combobox =  {
15807             tag: 'select',
15808             cls : 'roo-ios-select'
15809         };
15810         
15811         if (this.name) {
15812             combobox.name = this.name;
15813         }
15814         
15815         if (this.disabled) {
15816             combobox.disabled = true;
15817         }
15818         
15819         var settings = this;
15820         
15821         ['xs','sm','md','lg'].map(function(size){
15822             if (settings[size]) {
15823                 cfg.cls += ' col-' + size + '-' + settings[size];
15824             }
15825         });
15826         
15827         cfg.cn = combobox;
15828         
15829         return cfg;
15830         
15831     },
15832     
15833     initIOSView : function()
15834     {
15835         this.store.on('load', this.onIOSViewLoad, this);
15836         
15837         return;
15838     },
15839     
15840     onIOSViewLoad : function()
15841     {
15842         if(this.store.getCount() < 1){
15843             return;
15844         }
15845         
15846         this.clearIOSView();
15847         
15848         if(this.allowBlank) {
15849             
15850             var default_text = '-- SELECT --';
15851             
15852             if(this.placeholder.length){
15853                 default_text = this.placeholder;
15854             }
15855             
15856             if(this.emptyTitle.length){
15857                 default_text += ' - ' + this.emptyTitle + ' -';
15858             }
15859             
15860             var opt = this.inputEl().createChild({
15861                 tag: 'option',
15862                 value : 0,
15863                 html : default_text
15864             });
15865             
15866             var o = {};
15867             o[this.valueField] = 0;
15868             o[this.displayField] = default_text;
15869             
15870             this.ios_options.push({
15871                 data : o,
15872                 el : opt
15873             });
15874             
15875         }
15876         
15877         this.store.data.each(function(d, rowIndex){
15878             
15879             var html = '';
15880             
15881             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15882                 html = d.data[this.displayField];
15883             }
15884             
15885             var value = '';
15886             
15887             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15888                 value = d.data[this.valueField];
15889             }
15890             
15891             var option = {
15892                 tag: 'option',
15893                 value : value,
15894                 html : html
15895             };
15896             
15897             if(this.value == d.data[this.valueField]){
15898                 option['selected'] = true;
15899             }
15900             
15901             var opt = this.inputEl().createChild(option);
15902             
15903             this.ios_options.push({
15904                 data : d.data,
15905                 el : opt
15906             });
15907             
15908         }, this);
15909         
15910         this.inputEl().on('change', function(){
15911            this.fireEvent('select', this);
15912         }, this);
15913         
15914     },
15915     
15916     clearIOSView: function()
15917     {
15918         this.inputEl().dom.innerHTML = '';
15919         
15920         this.ios_options = [];
15921     },
15922     
15923     setIOSValue: function(v)
15924     {
15925         this.value = v;
15926         
15927         if(!this.ios_options){
15928             return;
15929         }
15930         
15931         Roo.each(this.ios_options, function(opts){
15932            
15933            opts.el.dom.removeAttribute('selected');
15934            
15935            if(opts.data[this.valueField] != v){
15936                return;
15937            }
15938            
15939            opts.el.dom.setAttribute('selected', true);
15940            
15941         }, this);
15942     }
15943
15944     /** 
15945     * @cfg {Boolean} grow 
15946     * @hide 
15947     */
15948     /** 
15949     * @cfg {Number} growMin 
15950     * @hide 
15951     */
15952     /** 
15953     * @cfg {Number} growMax 
15954     * @hide 
15955     */
15956     /**
15957      * @hide
15958      * @method autoSize
15959      */
15960 });
15961
15962 Roo.apply(Roo.bootstrap.ComboBox,  {
15963     
15964     header : {
15965         tag: 'div',
15966         cls: 'modal-header',
15967         cn: [
15968             {
15969                 tag: 'h4',
15970                 cls: 'modal-title'
15971             }
15972         ]
15973     },
15974     
15975     body : {
15976         tag: 'div',
15977         cls: 'modal-body',
15978         cn: [
15979             {
15980                 tag: 'ul',
15981                 cls: 'list-group'
15982             }
15983         ]
15984     },
15985     
15986     listItemRadio : {
15987         tag: 'li',
15988         cls: 'list-group-item',
15989         cn: [
15990             {
15991                 tag: 'span',
15992                 cls: 'roo-combobox-list-group-item-value'
15993             },
15994             {
15995                 tag: 'div',
15996                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15997                 cn: [
15998                     {
15999                         tag: 'input',
16000                         type: 'radio'
16001                     },
16002                     {
16003                         tag: 'label'
16004                     }
16005                 ]
16006             }
16007         ]
16008     },
16009     
16010     listItemCheckbox : {
16011         tag: 'li',
16012         cls: 'list-group-item',
16013         cn: [
16014             {
16015                 tag: 'span',
16016                 cls: 'roo-combobox-list-group-item-value'
16017             },
16018             {
16019                 tag: 'div',
16020                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16021                 cn: [
16022                     {
16023                         tag: 'input',
16024                         type: 'checkbox'
16025                     },
16026                     {
16027                         tag: 'label'
16028                     }
16029                 ]
16030             }
16031         ]
16032     },
16033     
16034     emptyResult : {
16035         tag: 'div',
16036         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16037     },
16038     
16039     footer : {
16040         tag: 'div',
16041         cls: 'modal-footer',
16042         cn: [
16043             {
16044                 tag: 'div',
16045                 cls: 'row',
16046                 cn: [
16047                     {
16048                         tag: 'div',
16049                         cls: 'col-xs-6 text-left',
16050                         cn: {
16051                             tag: 'button',
16052                             cls: 'btn btn-danger roo-touch-view-cancel',
16053                             html: 'Cancel'
16054                         }
16055                     },
16056                     {
16057                         tag: 'div',
16058                         cls: 'col-xs-6 text-right',
16059                         cn: {
16060                             tag: 'button',
16061                             cls: 'btn btn-success roo-touch-view-ok',
16062                             html: 'OK'
16063                         }
16064                     }
16065                 ]
16066             }
16067         ]
16068         
16069     }
16070 });
16071
16072 Roo.apply(Roo.bootstrap.ComboBox,  {
16073     
16074     touchViewTemplate : {
16075         tag: 'div',
16076         cls: 'modal fade roo-combobox-touch-view',
16077         cn: [
16078             {
16079                 tag: 'div',
16080                 cls: 'modal-dialog',
16081                 style : 'position:fixed', // we have to fix position....
16082                 cn: [
16083                     {
16084                         tag: 'div',
16085                         cls: 'modal-content',
16086                         cn: [
16087                             Roo.bootstrap.ComboBox.header,
16088                             Roo.bootstrap.ComboBox.body,
16089                             Roo.bootstrap.ComboBox.footer
16090                         ]
16091                     }
16092                 ]
16093             }
16094         ]
16095     }
16096 });/*
16097  * Based on:
16098  * Ext JS Library 1.1.1
16099  * Copyright(c) 2006-2007, Ext JS, LLC.
16100  *
16101  * Originally Released Under LGPL - original licence link has changed is not relivant.
16102  *
16103  * Fork - LGPL
16104  * <script type="text/javascript">
16105  */
16106
16107 /**
16108  * @class Roo.View
16109  * @extends Roo.util.Observable
16110  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16111  * This class also supports single and multi selection modes. <br>
16112  * Create a data model bound view:
16113  <pre><code>
16114  var store = new Roo.data.Store(...);
16115
16116  var view = new Roo.View({
16117     el : "my-element",
16118     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16119  
16120     singleSelect: true,
16121     selectedClass: "ydataview-selected",
16122     store: store
16123  });
16124
16125  // listen for node click?
16126  view.on("click", function(vw, index, node, e){
16127  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16128  });
16129
16130  // load XML data
16131  dataModel.load("foobar.xml");
16132  </code></pre>
16133  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16134  * <br><br>
16135  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16136  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16137  * 
16138  * Note: old style constructor is still suported (container, template, config)
16139  * 
16140  * @constructor
16141  * Create a new View
16142  * @param {Object} config The config object
16143  * 
16144  */
16145 Roo.View = function(config, depreciated_tpl, depreciated_config){
16146     
16147     this.parent = false;
16148     
16149     if (typeof(depreciated_tpl) == 'undefined') {
16150         // new way.. - universal constructor.
16151         Roo.apply(this, config);
16152         this.el  = Roo.get(this.el);
16153     } else {
16154         // old format..
16155         this.el  = Roo.get(config);
16156         this.tpl = depreciated_tpl;
16157         Roo.apply(this, depreciated_config);
16158     }
16159     this.wrapEl  = this.el.wrap().wrap();
16160     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16161     
16162     
16163     if(typeof(this.tpl) == "string"){
16164         this.tpl = new Roo.Template(this.tpl);
16165     } else {
16166         // support xtype ctors..
16167         this.tpl = new Roo.factory(this.tpl, Roo);
16168     }
16169     
16170     
16171     this.tpl.compile();
16172     
16173     /** @private */
16174     this.addEvents({
16175         /**
16176          * @event beforeclick
16177          * Fires before a click is processed. Returns false to cancel the default action.
16178          * @param {Roo.View} this
16179          * @param {Number} index The index of the target node
16180          * @param {HTMLElement} node The target node
16181          * @param {Roo.EventObject} e The raw event object
16182          */
16183             "beforeclick" : true,
16184         /**
16185          * @event click
16186          * Fires when a template node is clicked.
16187          * @param {Roo.View} this
16188          * @param {Number} index The index of the target node
16189          * @param {HTMLElement} node The target node
16190          * @param {Roo.EventObject} e The raw event object
16191          */
16192             "click" : true,
16193         /**
16194          * @event dblclick
16195          * Fires when a template node is double clicked.
16196          * @param {Roo.View} this
16197          * @param {Number} index The index of the target node
16198          * @param {HTMLElement} node The target node
16199          * @param {Roo.EventObject} e The raw event object
16200          */
16201             "dblclick" : true,
16202         /**
16203          * @event contextmenu
16204          * Fires when a template node is right clicked.
16205          * @param {Roo.View} this
16206          * @param {Number} index The index of the target node
16207          * @param {HTMLElement} node The target node
16208          * @param {Roo.EventObject} e The raw event object
16209          */
16210             "contextmenu" : true,
16211         /**
16212          * @event selectionchange
16213          * Fires when the selected nodes change.
16214          * @param {Roo.View} this
16215          * @param {Array} selections Array of the selected nodes
16216          */
16217             "selectionchange" : true,
16218     
16219         /**
16220          * @event beforeselect
16221          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16222          * @param {Roo.View} this
16223          * @param {HTMLElement} node The node to be selected
16224          * @param {Array} selections Array of currently selected nodes
16225          */
16226             "beforeselect" : true,
16227         /**
16228          * @event preparedata
16229          * Fires on every row to render, to allow you to change the data.
16230          * @param {Roo.View} this
16231          * @param {Object} data to be rendered (change this)
16232          */
16233           "preparedata" : true
16234           
16235           
16236         });
16237
16238
16239
16240     this.el.on({
16241         "click": this.onClick,
16242         "dblclick": this.onDblClick,
16243         "contextmenu": this.onContextMenu,
16244         scope:this
16245     });
16246
16247     this.selections = [];
16248     this.nodes = [];
16249     this.cmp = new Roo.CompositeElementLite([]);
16250     if(this.store){
16251         this.store = Roo.factory(this.store, Roo.data);
16252         this.setStore(this.store, true);
16253     }
16254     
16255     if ( this.footer && this.footer.xtype) {
16256            
16257          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16258         
16259         this.footer.dataSource = this.store;
16260         this.footer.container = fctr;
16261         this.footer = Roo.factory(this.footer, Roo);
16262         fctr.insertFirst(this.el);
16263         
16264         // this is a bit insane - as the paging toolbar seems to detach the el..
16265 //        dom.parentNode.parentNode.parentNode
16266          // they get detached?
16267     }
16268     
16269     
16270     Roo.View.superclass.constructor.call(this);
16271     
16272     
16273 };
16274
16275 Roo.extend(Roo.View, Roo.util.Observable, {
16276     
16277      /**
16278      * @cfg {Roo.data.Store} store Data store to load data from.
16279      */
16280     store : false,
16281     
16282     /**
16283      * @cfg {String|Roo.Element} el The container element.
16284      */
16285     el : '',
16286     
16287     /**
16288      * @cfg {String|Roo.Template} tpl The template used by this View 
16289      */
16290     tpl : false,
16291     /**
16292      * @cfg {String} dataName the named area of the template to use as the data area
16293      *                          Works with domtemplates roo-name="name"
16294      */
16295     dataName: false,
16296     /**
16297      * @cfg {String} selectedClass The css class to add to selected nodes
16298      */
16299     selectedClass : "x-view-selected",
16300      /**
16301      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16302      */
16303     emptyText : "",
16304     
16305     /**
16306      * @cfg {String} text to display on mask (default Loading)
16307      */
16308     mask : false,
16309     /**
16310      * @cfg {Boolean} multiSelect Allow multiple selection
16311      */
16312     multiSelect : false,
16313     /**
16314      * @cfg {Boolean} singleSelect Allow single selection
16315      */
16316     singleSelect:  false,
16317     
16318     /**
16319      * @cfg {Boolean} toggleSelect - selecting 
16320      */
16321     toggleSelect : false,
16322     
16323     /**
16324      * @cfg {Boolean} tickable - selecting 
16325      */
16326     tickable : false,
16327     
16328     /**
16329      * Returns the element this view is bound to.
16330      * @return {Roo.Element}
16331      */
16332     getEl : function(){
16333         return this.wrapEl;
16334     },
16335     
16336     
16337
16338     /**
16339      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16340      */
16341     refresh : function(){
16342         //Roo.log('refresh');
16343         var t = this.tpl;
16344         
16345         // if we are using something like 'domtemplate', then
16346         // the what gets used is:
16347         // t.applySubtemplate(NAME, data, wrapping data..)
16348         // the outer template then get' applied with
16349         //     the store 'extra data'
16350         // and the body get's added to the
16351         //      roo-name="data" node?
16352         //      <span class='roo-tpl-{name}'></span> ?????
16353         
16354         
16355         
16356         this.clearSelections();
16357         this.el.update("");
16358         var html = [];
16359         var records = this.store.getRange();
16360         if(records.length < 1) {
16361             
16362             // is this valid??  = should it render a template??
16363             
16364             this.el.update(this.emptyText);
16365             return;
16366         }
16367         var el = this.el;
16368         if (this.dataName) {
16369             this.el.update(t.apply(this.store.meta)); //????
16370             el = this.el.child('.roo-tpl-' + this.dataName);
16371         }
16372         
16373         for(var i = 0, len = records.length; i < len; i++){
16374             var data = this.prepareData(records[i].data, i, records[i]);
16375             this.fireEvent("preparedata", this, data, i, records[i]);
16376             
16377             var d = Roo.apply({}, data);
16378             
16379             if(this.tickable){
16380                 Roo.apply(d, {'roo-id' : Roo.id()});
16381                 
16382                 var _this = this;
16383             
16384                 Roo.each(this.parent.item, function(item){
16385                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16386                         return;
16387                     }
16388                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16389                 });
16390             }
16391             
16392             html[html.length] = Roo.util.Format.trim(
16393                 this.dataName ?
16394                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16395                     t.apply(d)
16396             );
16397         }
16398         
16399         
16400         
16401         el.update(html.join(""));
16402         this.nodes = el.dom.childNodes;
16403         this.updateIndexes(0);
16404     },
16405     
16406
16407     /**
16408      * Function to override to reformat the data that is sent to
16409      * the template for each node.
16410      * DEPRICATED - use the preparedata event handler.
16411      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16412      * a JSON object for an UpdateManager bound view).
16413      */
16414     prepareData : function(data, index, record)
16415     {
16416         this.fireEvent("preparedata", this, data, index, record);
16417         return data;
16418     },
16419
16420     onUpdate : function(ds, record){
16421         // Roo.log('on update');   
16422         this.clearSelections();
16423         var index = this.store.indexOf(record);
16424         var n = this.nodes[index];
16425         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16426         n.parentNode.removeChild(n);
16427         this.updateIndexes(index, index);
16428     },
16429
16430     
16431     
16432 // --------- FIXME     
16433     onAdd : function(ds, records, index)
16434     {
16435         //Roo.log(['on Add', ds, records, index] );        
16436         this.clearSelections();
16437         if(this.nodes.length == 0){
16438             this.refresh();
16439             return;
16440         }
16441         var n = this.nodes[index];
16442         for(var i = 0, len = records.length; i < len; i++){
16443             var d = this.prepareData(records[i].data, i, records[i]);
16444             if(n){
16445                 this.tpl.insertBefore(n, d);
16446             }else{
16447                 
16448                 this.tpl.append(this.el, d);
16449             }
16450         }
16451         this.updateIndexes(index);
16452     },
16453
16454     onRemove : function(ds, record, index){
16455        // Roo.log('onRemove');
16456         this.clearSelections();
16457         var el = this.dataName  ?
16458             this.el.child('.roo-tpl-' + this.dataName) :
16459             this.el; 
16460         
16461         el.dom.removeChild(this.nodes[index]);
16462         this.updateIndexes(index);
16463     },
16464
16465     /**
16466      * Refresh an individual node.
16467      * @param {Number} index
16468      */
16469     refreshNode : function(index){
16470         this.onUpdate(this.store, this.store.getAt(index));
16471     },
16472
16473     updateIndexes : function(startIndex, endIndex){
16474         var ns = this.nodes;
16475         startIndex = startIndex || 0;
16476         endIndex = endIndex || ns.length - 1;
16477         for(var i = startIndex; i <= endIndex; i++){
16478             ns[i].nodeIndex = i;
16479         }
16480     },
16481
16482     /**
16483      * Changes the data store this view uses and refresh the view.
16484      * @param {Store} store
16485      */
16486     setStore : function(store, initial){
16487         if(!initial && this.store){
16488             this.store.un("datachanged", this.refresh);
16489             this.store.un("add", this.onAdd);
16490             this.store.un("remove", this.onRemove);
16491             this.store.un("update", this.onUpdate);
16492             this.store.un("clear", this.refresh);
16493             this.store.un("beforeload", this.onBeforeLoad);
16494             this.store.un("load", this.onLoad);
16495             this.store.un("loadexception", this.onLoad);
16496         }
16497         if(store){
16498           
16499             store.on("datachanged", this.refresh, this);
16500             store.on("add", this.onAdd, this);
16501             store.on("remove", this.onRemove, this);
16502             store.on("update", this.onUpdate, this);
16503             store.on("clear", this.refresh, this);
16504             store.on("beforeload", this.onBeforeLoad, this);
16505             store.on("load", this.onLoad, this);
16506             store.on("loadexception", this.onLoad, this);
16507         }
16508         
16509         if(store){
16510             this.refresh();
16511         }
16512     },
16513     /**
16514      * onbeforeLoad - masks the loading area.
16515      *
16516      */
16517     onBeforeLoad : function(store,opts)
16518     {
16519          //Roo.log('onBeforeLoad');   
16520         if (!opts.add) {
16521             this.el.update("");
16522         }
16523         this.el.mask(this.mask ? this.mask : "Loading" ); 
16524     },
16525     onLoad : function ()
16526     {
16527         this.el.unmask();
16528     },
16529     
16530
16531     /**
16532      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16533      * @param {HTMLElement} node
16534      * @return {HTMLElement} The template node
16535      */
16536     findItemFromChild : function(node){
16537         var el = this.dataName  ?
16538             this.el.child('.roo-tpl-' + this.dataName,true) :
16539             this.el.dom; 
16540         
16541         if(!node || node.parentNode == el){
16542                     return node;
16543             }
16544             var p = node.parentNode;
16545             while(p && p != el){
16546             if(p.parentNode == el){
16547                 return p;
16548             }
16549             p = p.parentNode;
16550         }
16551             return null;
16552     },
16553
16554     /** @ignore */
16555     onClick : function(e){
16556         var item = this.findItemFromChild(e.getTarget());
16557         if(item){
16558             var index = this.indexOf(item);
16559             if(this.onItemClick(item, index, e) !== false){
16560                 this.fireEvent("click", this, index, item, e);
16561             }
16562         }else{
16563             this.clearSelections();
16564         }
16565     },
16566
16567     /** @ignore */
16568     onContextMenu : function(e){
16569         var item = this.findItemFromChild(e.getTarget());
16570         if(item){
16571             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16572         }
16573     },
16574
16575     /** @ignore */
16576     onDblClick : function(e){
16577         var item = this.findItemFromChild(e.getTarget());
16578         if(item){
16579             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16580         }
16581     },
16582
16583     onItemClick : function(item, index, e)
16584     {
16585         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16586             return false;
16587         }
16588         if (this.toggleSelect) {
16589             var m = this.isSelected(item) ? 'unselect' : 'select';
16590             //Roo.log(m);
16591             var _t = this;
16592             _t[m](item, true, false);
16593             return true;
16594         }
16595         if(this.multiSelect || this.singleSelect){
16596             if(this.multiSelect && e.shiftKey && this.lastSelection){
16597                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16598             }else{
16599                 this.select(item, this.multiSelect && e.ctrlKey);
16600                 this.lastSelection = item;
16601             }
16602             
16603             if(!this.tickable){
16604                 e.preventDefault();
16605             }
16606             
16607         }
16608         return true;
16609     },
16610
16611     /**
16612      * Get the number of selected nodes.
16613      * @return {Number}
16614      */
16615     getSelectionCount : function(){
16616         return this.selections.length;
16617     },
16618
16619     /**
16620      * Get the currently selected nodes.
16621      * @return {Array} An array of HTMLElements
16622      */
16623     getSelectedNodes : function(){
16624         return this.selections;
16625     },
16626
16627     /**
16628      * Get the indexes of the selected nodes.
16629      * @return {Array}
16630      */
16631     getSelectedIndexes : function(){
16632         var indexes = [], s = this.selections;
16633         for(var i = 0, len = s.length; i < len; i++){
16634             indexes.push(s[i].nodeIndex);
16635         }
16636         return indexes;
16637     },
16638
16639     /**
16640      * Clear all selections
16641      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16642      */
16643     clearSelections : function(suppressEvent){
16644         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16645             this.cmp.elements = this.selections;
16646             this.cmp.removeClass(this.selectedClass);
16647             this.selections = [];
16648             if(!suppressEvent){
16649                 this.fireEvent("selectionchange", this, this.selections);
16650             }
16651         }
16652     },
16653
16654     /**
16655      * Returns true if the passed node is selected
16656      * @param {HTMLElement/Number} node The node or node index
16657      * @return {Boolean}
16658      */
16659     isSelected : function(node){
16660         var s = this.selections;
16661         if(s.length < 1){
16662             return false;
16663         }
16664         node = this.getNode(node);
16665         return s.indexOf(node) !== -1;
16666     },
16667
16668     /**
16669      * Selects nodes.
16670      * @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
16671      * @param {Boolean} keepExisting (optional) true to keep existing selections
16672      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16673      */
16674     select : function(nodeInfo, keepExisting, suppressEvent){
16675         if(nodeInfo instanceof Array){
16676             if(!keepExisting){
16677                 this.clearSelections(true);
16678             }
16679             for(var i = 0, len = nodeInfo.length; i < len; i++){
16680                 this.select(nodeInfo[i], true, true);
16681             }
16682             return;
16683         } 
16684         var node = this.getNode(nodeInfo);
16685         if(!node || this.isSelected(node)){
16686             return; // already selected.
16687         }
16688         if(!keepExisting){
16689             this.clearSelections(true);
16690         }
16691         
16692         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16693             Roo.fly(node).addClass(this.selectedClass);
16694             this.selections.push(node);
16695             if(!suppressEvent){
16696                 this.fireEvent("selectionchange", this, this.selections);
16697             }
16698         }
16699         
16700         
16701     },
16702       /**
16703      * Unselects nodes.
16704      * @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
16705      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16706      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16707      */
16708     unselect : function(nodeInfo, keepExisting, suppressEvent)
16709     {
16710         if(nodeInfo instanceof Array){
16711             Roo.each(this.selections, function(s) {
16712                 this.unselect(s, nodeInfo);
16713             }, this);
16714             return;
16715         }
16716         var node = this.getNode(nodeInfo);
16717         if(!node || !this.isSelected(node)){
16718             //Roo.log("not selected");
16719             return; // not selected.
16720         }
16721         // fireevent???
16722         var ns = [];
16723         Roo.each(this.selections, function(s) {
16724             if (s == node ) {
16725                 Roo.fly(node).removeClass(this.selectedClass);
16726
16727                 return;
16728             }
16729             ns.push(s);
16730         },this);
16731         
16732         this.selections= ns;
16733         this.fireEvent("selectionchange", this, this.selections);
16734     },
16735
16736     /**
16737      * Gets a template node.
16738      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16739      * @return {HTMLElement} The node or null if it wasn't found
16740      */
16741     getNode : function(nodeInfo){
16742         if(typeof nodeInfo == "string"){
16743             return document.getElementById(nodeInfo);
16744         }else if(typeof nodeInfo == "number"){
16745             return this.nodes[nodeInfo];
16746         }
16747         return nodeInfo;
16748     },
16749
16750     /**
16751      * Gets a range template nodes.
16752      * @param {Number} startIndex
16753      * @param {Number} endIndex
16754      * @return {Array} An array of nodes
16755      */
16756     getNodes : function(start, end){
16757         var ns = this.nodes;
16758         start = start || 0;
16759         end = typeof end == "undefined" ? ns.length - 1 : end;
16760         var nodes = [];
16761         if(start <= end){
16762             for(var i = start; i <= end; i++){
16763                 nodes.push(ns[i]);
16764             }
16765         } else{
16766             for(var i = start; i >= end; i--){
16767                 nodes.push(ns[i]);
16768             }
16769         }
16770         return nodes;
16771     },
16772
16773     /**
16774      * Finds the index of the passed node
16775      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16776      * @return {Number} The index of the node or -1
16777      */
16778     indexOf : function(node){
16779         node = this.getNode(node);
16780         if(typeof node.nodeIndex == "number"){
16781             return node.nodeIndex;
16782         }
16783         var ns = this.nodes;
16784         for(var i = 0, len = ns.length; i < len; i++){
16785             if(ns[i] == node){
16786                 return i;
16787             }
16788         }
16789         return -1;
16790     }
16791 });
16792 /*
16793  * - LGPL
16794  *
16795  * based on jquery fullcalendar
16796  * 
16797  */
16798
16799 Roo.bootstrap = Roo.bootstrap || {};
16800 /**
16801  * @class Roo.bootstrap.Calendar
16802  * @extends Roo.bootstrap.Component
16803  * Bootstrap Calendar class
16804  * @cfg {Boolean} loadMask (true|false) default false
16805  * @cfg {Object} header generate the user specific header of the calendar, default false
16806
16807  * @constructor
16808  * Create a new Container
16809  * @param {Object} config The config object
16810  */
16811
16812
16813
16814 Roo.bootstrap.Calendar = function(config){
16815     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16816      this.addEvents({
16817         /**
16818              * @event select
16819              * Fires when a date is selected
16820              * @param {DatePicker} this
16821              * @param {Date} date The selected date
16822              */
16823         'select': true,
16824         /**
16825              * @event monthchange
16826              * Fires when the displayed month changes 
16827              * @param {DatePicker} this
16828              * @param {Date} date The selected month
16829              */
16830         'monthchange': true,
16831         /**
16832              * @event evententer
16833              * Fires when mouse over an event
16834              * @param {Calendar} this
16835              * @param {event} Event
16836              */
16837         'evententer': true,
16838         /**
16839              * @event eventleave
16840              * Fires when the mouse leaves an
16841              * @param {Calendar} this
16842              * @param {event}
16843              */
16844         'eventleave': true,
16845         /**
16846              * @event eventclick
16847              * Fires when the mouse click an
16848              * @param {Calendar} this
16849              * @param {event}
16850              */
16851         'eventclick': true
16852         
16853     });
16854
16855 };
16856
16857 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16858     
16859      /**
16860      * @cfg {Number} startDay
16861      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16862      */
16863     startDay : 0,
16864     
16865     loadMask : false,
16866     
16867     header : false,
16868       
16869     getAutoCreate : function(){
16870         
16871         
16872         var fc_button = function(name, corner, style, content ) {
16873             return Roo.apply({},{
16874                 tag : 'span',
16875                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16876                          (corner.length ?
16877                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16878                             ''
16879                         ),
16880                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16881                 unselectable: 'on'
16882             });
16883         };
16884         
16885         var header = {};
16886         
16887         if(!this.header){
16888             header = {
16889                 tag : 'table',
16890                 cls : 'fc-header',
16891                 style : 'width:100%',
16892                 cn : [
16893                     {
16894                         tag: 'tr',
16895                         cn : [
16896                             {
16897                                 tag : 'td',
16898                                 cls : 'fc-header-left',
16899                                 cn : [
16900                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16901                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16902                                     { tag: 'span', cls: 'fc-header-space' },
16903                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16904
16905
16906                                 ]
16907                             },
16908
16909                             {
16910                                 tag : 'td',
16911                                 cls : 'fc-header-center',
16912                                 cn : [
16913                                     {
16914                                         tag: 'span',
16915                                         cls: 'fc-header-title',
16916                                         cn : {
16917                                             tag: 'H2',
16918                                             html : 'month / year'
16919                                         }
16920                                     }
16921
16922                                 ]
16923                             },
16924                             {
16925                                 tag : 'td',
16926                                 cls : 'fc-header-right',
16927                                 cn : [
16928                               /*      fc_button('month', 'left', '', 'month' ),
16929                                     fc_button('week', '', '', 'week' ),
16930                                     fc_button('day', 'right', '', 'day' )
16931                                 */    
16932
16933                                 ]
16934                             }
16935
16936                         ]
16937                     }
16938                 ]
16939             };
16940         }
16941         
16942         header = this.header;
16943         
16944        
16945         var cal_heads = function() {
16946             var ret = [];
16947             // fixme - handle this.
16948             
16949             for (var i =0; i < Date.dayNames.length; i++) {
16950                 var d = Date.dayNames[i];
16951                 ret.push({
16952                     tag: 'th',
16953                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16954                     html : d.substring(0,3)
16955                 });
16956                 
16957             }
16958             ret[0].cls += ' fc-first';
16959             ret[6].cls += ' fc-last';
16960             return ret;
16961         };
16962         var cal_cell = function(n) {
16963             return  {
16964                 tag: 'td',
16965                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16966                 cn : [
16967                     {
16968                         cn : [
16969                             {
16970                                 cls: 'fc-day-number',
16971                                 html: 'D'
16972                             },
16973                             {
16974                                 cls: 'fc-day-content',
16975                              
16976                                 cn : [
16977                                      {
16978                                         style: 'position: relative;' // height: 17px;
16979                                     }
16980                                 ]
16981                             }
16982                             
16983                             
16984                         ]
16985                     }
16986                 ]
16987                 
16988             }
16989         };
16990         var cal_rows = function() {
16991             
16992             var ret = [];
16993             for (var r = 0; r < 6; r++) {
16994                 var row= {
16995                     tag : 'tr',
16996                     cls : 'fc-week',
16997                     cn : []
16998                 };
16999                 
17000                 for (var i =0; i < Date.dayNames.length; i++) {
17001                     var d = Date.dayNames[i];
17002                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17003
17004                 }
17005                 row.cn[0].cls+=' fc-first';
17006                 row.cn[0].cn[0].style = 'min-height:90px';
17007                 row.cn[6].cls+=' fc-last';
17008                 ret.push(row);
17009                 
17010             }
17011             ret[0].cls += ' fc-first';
17012             ret[4].cls += ' fc-prev-last';
17013             ret[5].cls += ' fc-last';
17014             return ret;
17015             
17016         };
17017         
17018         var cal_table = {
17019             tag: 'table',
17020             cls: 'fc-border-separate',
17021             style : 'width:100%',
17022             cellspacing  : 0,
17023             cn : [
17024                 { 
17025                     tag: 'thead',
17026                     cn : [
17027                         { 
17028                             tag: 'tr',
17029                             cls : 'fc-first fc-last',
17030                             cn : cal_heads()
17031                         }
17032                     ]
17033                 },
17034                 { 
17035                     tag: 'tbody',
17036                     cn : cal_rows()
17037                 }
17038                   
17039             ]
17040         };
17041          
17042          var cfg = {
17043             cls : 'fc fc-ltr',
17044             cn : [
17045                 header,
17046                 {
17047                     cls : 'fc-content',
17048                     style : "position: relative;",
17049                     cn : [
17050                         {
17051                             cls : 'fc-view fc-view-month fc-grid',
17052                             style : 'position: relative',
17053                             unselectable : 'on',
17054                             cn : [
17055                                 {
17056                                     cls : 'fc-event-container',
17057                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17058                                 },
17059                                 cal_table
17060                             ]
17061                         }
17062                     ]
17063     
17064                 }
17065            ] 
17066             
17067         };
17068         
17069          
17070         
17071         return cfg;
17072     },
17073     
17074     
17075     initEvents : function()
17076     {
17077         if(!this.store){
17078             throw "can not find store for calendar";
17079         }
17080         
17081         var mark = {
17082             tag: "div",
17083             cls:"x-dlg-mask",
17084             style: "text-align:center",
17085             cn: [
17086                 {
17087                     tag: "div",
17088                     style: "background-color:white;width:50%;margin:250 auto",
17089                     cn: [
17090                         {
17091                             tag: "img",
17092                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17093                         },
17094                         {
17095                             tag: "span",
17096                             html: "Loading"
17097                         }
17098                         
17099                     ]
17100                 }
17101             ]
17102         };
17103         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17104         
17105         var size = this.el.select('.fc-content', true).first().getSize();
17106         this.maskEl.setSize(size.width, size.height);
17107         this.maskEl.enableDisplayMode("block");
17108         if(!this.loadMask){
17109             this.maskEl.hide();
17110         }
17111         
17112         this.store = Roo.factory(this.store, Roo.data);
17113         this.store.on('load', this.onLoad, this);
17114         this.store.on('beforeload', this.onBeforeLoad, this);
17115         
17116         this.resize();
17117         
17118         this.cells = this.el.select('.fc-day',true);
17119         //Roo.log(this.cells);
17120         this.textNodes = this.el.query('.fc-day-number');
17121         this.cells.addClassOnOver('fc-state-hover');
17122         
17123         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17124         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17125         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17126         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17127         
17128         this.on('monthchange', this.onMonthChange, this);
17129         
17130         this.update(new Date().clearTime());
17131     },
17132     
17133     resize : function() {
17134         var sz  = this.el.getSize();
17135         
17136         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17137         this.el.select('.fc-day-content div',true).setHeight(34);
17138     },
17139     
17140     
17141     // private
17142     showPrevMonth : function(e){
17143         this.update(this.activeDate.add("mo", -1));
17144     },
17145     showToday : function(e){
17146         this.update(new Date().clearTime());
17147     },
17148     // private
17149     showNextMonth : function(e){
17150         this.update(this.activeDate.add("mo", 1));
17151     },
17152
17153     // private
17154     showPrevYear : function(){
17155         this.update(this.activeDate.add("y", -1));
17156     },
17157
17158     // private
17159     showNextYear : function(){
17160         this.update(this.activeDate.add("y", 1));
17161     },
17162
17163     
17164    // private
17165     update : function(date)
17166     {
17167         var vd = this.activeDate;
17168         this.activeDate = date;
17169 //        if(vd && this.el){
17170 //            var t = date.getTime();
17171 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17172 //                Roo.log('using add remove');
17173 //                
17174 //                this.fireEvent('monthchange', this, date);
17175 //                
17176 //                this.cells.removeClass("fc-state-highlight");
17177 //                this.cells.each(function(c){
17178 //                   if(c.dateValue == t){
17179 //                       c.addClass("fc-state-highlight");
17180 //                       setTimeout(function(){
17181 //                            try{c.dom.firstChild.focus();}catch(e){}
17182 //                       }, 50);
17183 //                       return false;
17184 //                   }
17185 //                   return true;
17186 //                });
17187 //                return;
17188 //            }
17189 //        }
17190         
17191         var days = date.getDaysInMonth();
17192         
17193         var firstOfMonth = date.getFirstDateOfMonth();
17194         var startingPos = firstOfMonth.getDay()-this.startDay;
17195         
17196         if(startingPos < this.startDay){
17197             startingPos += 7;
17198         }
17199         
17200         var pm = date.add(Date.MONTH, -1);
17201         var prevStart = pm.getDaysInMonth()-startingPos;
17202 //        
17203         this.cells = this.el.select('.fc-day',true);
17204         this.textNodes = this.el.query('.fc-day-number');
17205         this.cells.addClassOnOver('fc-state-hover');
17206         
17207         var cells = this.cells.elements;
17208         var textEls = this.textNodes;
17209         
17210         Roo.each(cells, function(cell){
17211             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17212         });
17213         
17214         days += startingPos;
17215
17216         // convert everything to numbers so it's fast
17217         var day = 86400000;
17218         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17219         //Roo.log(d);
17220         //Roo.log(pm);
17221         //Roo.log(prevStart);
17222         
17223         var today = new Date().clearTime().getTime();
17224         var sel = date.clearTime().getTime();
17225         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17226         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17227         var ddMatch = this.disabledDatesRE;
17228         var ddText = this.disabledDatesText;
17229         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17230         var ddaysText = this.disabledDaysText;
17231         var format = this.format;
17232         
17233         var setCellClass = function(cal, cell){
17234             cell.row = 0;
17235             cell.events = [];
17236             cell.more = [];
17237             //Roo.log('set Cell Class');
17238             cell.title = "";
17239             var t = d.getTime();
17240             
17241             //Roo.log(d);
17242             
17243             cell.dateValue = t;
17244             if(t == today){
17245                 cell.className += " fc-today";
17246                 cell.className += " fc-state-highlight";
17247                 cell.title = cal.todayText;
17248             }
17249             if(t == sel){
17250                 // disable highlight in other month..
17251                 //cell.className += " fc-state-highlight";
17252                 
17253             }
17254             // disabling
17255             if(t < min) {
17256                 cell.className = " fc-state-disabled";
17257                 cell.title = cal.minText;
17258                 return;
17259             }
17260             if(t > max) {
17261                 cell.className = " fc-state-disabled";
17262                 cell.title = cal.maxText;
17263                 return;
17264             }
17265             if(ddays){
17266                 if(ddays.indexOf(d.getDay()) != -1){
17267                     cell.title = ddaysText;
17268                     cell.className = " fc-state-disabled";
17269                 }
17270             }
17271             if(ddMatch && format){
17272                 var fvalue = d.dateFormat(format);
17273                 if(ddMatch.test(fvalue)){
17274                     cell.title = ddText.replace("%0", fvalue);
17275                     cell.className = " fc-state-disabled";
17276                 }
17277             }
17278             
17279             if (!cell.initialClassName) {
17280                 cell.initialClassName = cell.dom.className;
17281             }
17282             
17283             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17284         };
17285
17286         var i = 0;
17287         
17288         for(; i < startingPos; i++) {
17289             textEls[i].innerHTML = (++prevStart);
17290             d.setDate(d.getDate()+1);
17291             
17292             cells[i].className = "fc-past fc-other-month";
17293             setCellClass(this, cells[i]);
17294         }
17295         
17296         var intDay = 0;
17297         
17298         for(; i < days; i++){
17299             intDay = i - startingPos + 1;
17300             textEls[i].innerHTML = (intDay);
17301             d.setDate(d.getDate()+1);
17302             
17303             cells[i].className = ''; // "x-date-active";
17304             setCellClass(this, cells[i]);
17305         }
17306         var extraDays = 0;
17307         
17308         for(; i < 42; i++) {
17309             textEls[i].innerHTML = (++extraDays);
17310             d.setDate(d.getDate()+1);
17311             
17312             cells[i].className = "fc-future fc-other-month";
17313             setCellClass(this, cells[i]);
17314         }
17315         
17316         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17317         
17318         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17319         
17320         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17321         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17322         
17323         if(totalRows != 6){
17324             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17325             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17326         }
17327         
17328         this.fireEvent('monthchange', this, date);
17329         
17330         
17331         /*
17332         if(!this.internalRender){
17333             var main = this.el.dom.firstChild;
17334             var w = main.offsetWidth;
17335             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17336             Roo.fly(main).setWidth(w);
17337             this.internalRender = true;
17338             // opera does not respect the auto grow header center column
17339             // then, after it gets a width opera refuses to recalculate
17340             // without a second pass
17341             if(Roo.isOpera && !this.secondPass){
17342                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17343                 this.secondPass = true;
17344                 this.update.defer(10, this, [date]);
17345             }
17346         }
17347         */
17348         
17349     },
17350     
17351     findCell : function(dt) {
17352         dt = dt.clearTime().getTime();
17353         var ret = false;
17354         this.cells.each(function(c){
17355             //Roo.log("check " +c.dateValue + '?=' + dt);
17356             if(c.dateValue == dt){
17357                 ret = c;
17358                 return false;
17359             }
17360             return true;
17361         });
17362         
17363         return ret;
17364     },
17365     
17366     findCells : function(ev) {
17367         var s = ev.start.clone().clearTime().getTime();
17368        // Roo.log(s);
17369         var e= ev.end.clone().clearTime().getTime();
17370        // Roo.log(e);
17371         var ret = [];
17372         this.cells.each(function(c){
17373              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17374             
17375             if(c.dateValue > e){
17376                 return ;
17377             }
17378             if(c.dateValue < s){
17379                 return ;
17380             }
17381             ret.push(c);
17382         });
17383         
17384         return ret;    
17385     },
17386     
17387 //    findBestRow: function(cells)
17388 //    {
17389 //        var ret = 0;
17390 //        
17391 //        for (var i =0 ; i < cells.length;i++) {
17392 //            ret  = Math.max(cells[i].rows || 0,ret);
17393 //        }
17394 //        return ret;
17395 //        
17396 //    },
17397     
17398     
17399     addItem : function(ev)
17400     {
17401         // look for vertical location slot in
17402         var cells = this.findCells(ev);
17403         
17404 //        ev.row = this.findBestRow(cells);
17405         
17406         // work out the location.
17407         
17408         var crow = false;
17409         var rows = [];
17410         for(var i =0; i < cells.length; i++) {
17411             
17412             cells[i].row = cells[0].row;
17413             
17414             if(i == 0){
17415                 cells[i].row = cells[i].row + 1;
17416             }
17417             
17418             if (!crow) {
17419                 crow = {
17420                     start : cells[i],
17421                     end :  cells[i]
17422                 };
17423                 continue;
17424             }
17425             if (crow.start.getY() == cells[i].getY()) {
17426                 // on same row.
17427                 crow.end = cells[i];
17428                 continue;
17429             }
17430             // different row.
17431             rows.push(crow);
17432             crow = {
17433                 start: cells[i],
17434                 end : cells[i]
17435             };
17436             
17437         }
17438         
17439         rows.push(crow);
17440         ev.els = [];
17441         ev.rows = rows;
17442         ev.cells = cells;
17443         
17444         cells[0].events.push(ev);
17445         
17446         this.calevents.push(ev);
17447     },
17448     
17449     clearEvents: function() {
17450         
17451         if(!this.calevents){
17452             return;
17453         }
17454         
17455         Roo.each(this.cells.elements, function(c){
17456             c.row = 0;
17457             c.events = [];
17458             c.more = [];
17459         });
17460         
17461         Roo.each(this.calevents, function(e) {
17462             Roo.each(e.els, function(el) {
17463                 el.un('mouseenter' ,this.onEventEnter, this);
17464                 el.un('mouseleave' ,this.onEventLeave, this);
17465                 el.remove();
17466             },this);
17467         },this);
17468         
17469         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17470             e.remove();
17471         });
17472         
17473     },
17474     
17475     renderEvents: function()
17476     {   
17477         var _this = this;
17478         
17479         this.cells.each(function(c) {
17480             
17481             if(c.row < 5){
17482                 return;
17483             }
17484             
17485             var ev = c.events;
17486             
17487             var r = 4;
17488             if(c.row != c.events.length){
17489                 r = 4 - (4 - (c.row - c.events.length));
17490             }
17491             
17492             c.events = ev.slice(0, r);
17493             c.more = ev.slice(r);
17494             
17495             if(c.more.length && c.more.length == 1){
17496                 c.events.push(c.more.pop());
17497             }
17498             
17499             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17500             
17501         });
17502             
17503         this.cells.each(function(c) {
17504             
17505             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17506             
17507             
17508             for (var e = 0; e < c.events.length; e++){
17509                 var ev = c.events[e];
17510                 var rows = ev.rows;
17511                 
17512                 for(var i = 0; i < rows.length; i++) {
17513                 
17514                     // how many rows should it span..
17515
17516                     var  cfg = {
17517                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17518                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17519
17520                         unselectable : "on",
17521                         cn : [
17522                             {
17523                                 cls: 'fc-event-inner',
17524                                 cn : [
17525     //                                {
17526     //                                  tag:'span',
17527     //                                  cls: 'fc-event-time',
17528     //                                  html : cells.length > 1 ? '' : ev.time
17529     //                                },
17530                                     {
17531                                       tag:'span',
17532                                       cls: 'fc-event-title',
17533                                       html : String.format('{0}', ev.title)
17534                                     }
17535
17536
17537                                 ]
17538                             },
17539                             {
17540                                 cls: 'ui-resizable-handle ui-resizable-e',
17541                                 html : '&nbsp;&nbsp;&nbsp'
17542                             }
17543
17544                         ]
17545                     };
17546
17547                     if (i == 0) {
17548                         cfg.cls += ' fc-event-start';
17549                     }
17550                     if ((i+1) == rows.length) {
17551                         cfg.cls += ' fc-event-end';
17552                     }
17553
17554                     var ctr = _this.el.select('.fc-event-container',true).first();
17555                     var cg = ctr.createChild(cfg);
17556
17557                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17558                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17559
17560                     var r = (c.more.length) ? 1 : 0;
17561                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17562                     cg.setWidth(ebox.right - sbox.x -2);
17563
17564                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17565                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17566                     cg.on('click', _this.onEventClick, _this, ev);
17567
17568                     ev.els.push(cg);
17569                     
17570                 }
17571                 
17572             }
17573             
17574             
17575             if(c.more.length){
17576                 var  cfg = {
17577                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17578                     style : 'position: absolute',
17579                     unselectable : "on",
17580                     cn : [
17581                         {
17582                             cls: 'fc-event-inner',
17583                             cn : [
17584                                 {
17585                                   tag:'span',
17586                                   cls: 'fc-event-title',
17587                                   html : 'More'
17588                                 }
17589
17590
17591                             ]
17592                         },
17593                         {
17594                             cls: 'ui-resizable-handle ui-resizable-e',
17595                             html : '&nbsp;&nbsp;&nbsp'
17596                         }
17597
17598                     ]
17599                 };
17600
17601                 var ctr = _this.el.select('.fc-event-container',true).first();
17602                 var cg = ctr.createChild(cfg);
17603
17604                 var sbox = c.select('.fc-day-content',true).first().getBox();
17605                 var ebox = c.select('.fc-day-content',true).first().getBox();
17606                 //Roo.log(cg);
17607                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17608                 cg.setWidth(ebox.right - sbox.x -2);
17609
17610                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17611                 
17612             }
17613             
17614         });
17615         
17616         
17617         
17618     },
17619     
17620     onEventEnter: function (e, el,event,d) {
17621         this.fireEvent('evententer', this, el, event);
17622     },
17623     
17624     onEventLeave: function (e, el,event,d) {
17625         this.fireEvent('eventleave', this, el, event);
17626     },
17627     
17628     onEventClick: function (e, el,event,d) {
17629         this.fireEvent('eventclick', this, el, event);
17630     },
17631     
17632     onMonthChange: function () {
17633         this.store.load();
17634     },
17635     
17636     onMoreEventClick: function(e, el, more)
17637     {
17638         var _this = this;
17639         
17640         this.calpopover.placement = 'right';
17641         this.calpopover.setTitle('More');
17642         
17643         this.calpopover.setContent('');
17644         
17645         var ctr = this.calpopover.el.select('.popover-content', true).first();
17646         
17647         Roo.each(more, function(m){
17648             var cfg = {
17649                 cls : 'fc-event-hori fc-event-draggable',
17650                 html : m.title
17651             };
17652             var cg = ctr.createChild(cfg);
17653             
17654             cg.on('click', _this.onEventClick, _this, m);
17655         });
17656         
17657         this.calpopover.show(el);
17658         
17659         
17660     },
17661     
17662     onLoad: function () 
17663     {   
17664         this.calevents = [];
17665         var cal = this;
17666         
17667         if(this.store.getCount() > 0){
17668             this.store.data.each(function(d){
17669                cal.addItem({
17670                     id : d.data.id,
17671                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17672                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17673                     time : d.data.start_time,
17674                     title : d.data.title,
17675                     description : d.data.description,
17676                     venue : d.data.venue
17677                 });
17678             });
17679         }
17680         
17681         this.renderEvents();
17682         
17683         if(this.calevents.length && this.loadMask){
17684             this.maskEl.hide();
17685         }
17686     },
17687     
17688     onBeforeLoad: function()
17689     {
17690         this.clearEvents();
17691         if(this.loadMask){
17692             this.maskEl.show();
17693         }
17694     }
17695 });
17696
17697  
17698  /*
17699  * - LGPL
17700  *
17701  * element
17702  * 
17703  */
17704
17705 /**
17706  * @class Roo.bootstrap.Popover
17707  * @extends Roo.bootstrap.Component
17708  * Bootstrap Popover class
17709  * @cfg {String} html contents of the popover   (or false to use children..)
17710  * @cfg {String} title of popover (or false to hide)
17711  * @cfg {String} placement how it is placed
17712  * @cfg {String} trigger click || hover (or false to trigger manually)
17713  * @cfg {String} over what (parent or false to trigger manually.)
17714  * @cfg {Number} delay - delay before showing
17715  
17716  * @constructor
17717  * Create a new Popover
17718  * @param {Object} config The config object
17719  */
17720
17721 Roo.bootstrap.Popover = function(config){
17722     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17723     
17724     this.addEvents({
17725         // raw events
17726          /**
17727          * @event show
17728          * After the popover show
17729          * 
17730          * @param {Roo.bootstrap.Popover} this
17731          */
17732         "show" : true,
17733         /**
17734          * @event hide
17735          * After the popover hide
17736          * 
17737          * @param {Roo.bootstrap.Popover} this
17738          */
17739         "hide" : true
17740     });
17741 };
17742
17743 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17744     
17745     title: 'Fill in a title',
17746     html: false,
17747     
17748     placement : 'right',
17749     trigger : 'hover', // hover
17750     
17751     delay : 0,
17752     
17753     over: 'parent',
17754     
17755     can_build_overlaid : false,
17756     
17757     getChildContainer : function()
17758     {
17759         return this.el.select('.popover-content',true).first();
17760     },
17761     
17762     getAutoCreate : function(){
17763          
17764         var cfg = {
17765            cls : 'popover roo-dynamic',
17766            style: 'display:block',
17767            cn : [
17768                 {
17769                     cls : 'arrow'
17770                 },
17771                 {
17772                     cls : 'popover-inner',
17773                     cn : [
17774                         {
17775                             tag: 'h3',
17776                             cls: 'popover-title popover-header',
17777                             html : this.title
17778                         },
17779                         {
17780                             cls : 'popover-content popover-body',
17781                             html : this.html
17782                         }
17783                     ]
17784                     
17785                 }
17786            ]
17787         };
17788         
17789         return cfg;
17790     },
17791     setTitle: function(str)
17792     {
17793         this.title = str;
17794         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17795     },
17796     setContent: function(str)
17797     {
17798         this.html = str;
17799         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17800     },
17801     // as it get's added to the bottom of the page.
17802     onRender : function(ct, position)
17803     {
17804         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17805         if(!this.el){
17806             var cfg = Roo.apply({},  this.getAutoCreate());
17807             cfg.id = Roo.id();
17808             
17809             if (this.cls) {
17810                 cfg.cls += ' ' + this.cls;
17811             }
17812             if (this.style) {
17813                 cfg.style = this.style;
17814             }
17815             //Roo.log("adding to ");
17816             this.el = Roo.get(document.body).createChild(cfg, position);
17817 //            Roo.log(this.el);
17818         }
17819         this.initEvents();
17820     },
17821     
17822     initEvents : function()
17823     {
17824         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17825         this.el.enableDisplayMode('block');
17826         this.el.hide();
17827         if (this.over === false) {
17828             return; 
17829         }
17830         if (this.triggers === false) {
17831             return;
17832         }
17833         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17834         var triggers = this.trigger ? this.trigger.split(' ') : [];
17835         Roo.each(triggers, function(trigger) {
17836         
17837             if (trigger == 'click') {
17838                 on_el.on('click', this.toggle, this);
17839             } else if (trigger != 'manual') {
17840                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17841                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17842       
17843                 on_el.on(eventIn  ,this.enter, this);
17844                 on_el.on(eventOut, this.leave, this);
17845             }
17846         }, this);
17847         
17848     },
17849     
17850     
17851     // private
17852     timeout : null,
17853     hoverState : null,
17854     
17855     toggle : function () {
17856         this.hoverState == 'in' ? this.leave() : this.enter();
17857     },
17858     
17859     enter : function () {
17860         
17861         clearTimeout(this.timeout);
17862     
17863         this.hoverState = 'in';
17864     
17865         if (!this.delay || !this.delay.show) {
17866             this.show();
17867             return;
17868         }
17869         var _t = this;
17870         this.timeout = setTimeout(function () {
17871             if (_t.hoverState == 'in') {
17872                 _t.show();
17873             }
17874         }, this.delay.show)
17875     },
17876     
17877     leave : function() {
17878         clearTimeout(this.timeout);
17879     
17880         this.hoverState = 'out';
17881     
17882         if (!this.delay || !this.delay.hide) {
17883             this.hide();
17884             return;
17885         }
17886         var _t = this;
17887         this.timeout = setTimeout(function () {
17888             if (_t.hoverState == 'out') {
17889                 _t.hide();
17890             }
17891         }, this.delay.hide)
17892     },
17893     
17894     show : function (on_el)
17895     {
17896         if (!on_el) {
17897             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17898         }
17899         
17900         // set content.
17901         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17902         if (this.html !== false) {
17903             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17904         }
17905         this.el.removeClass([
17906             'fade','top','bottom', 'left', 'right','in',
17907             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17908         ]);
17909         if (!this.title.length) {
17910             this.el.select('.popover-title',true).hide();
17911         }
17912         
17913         var placement = typeof this.placement == 'function' ?
17914             this.placement.call(this, this.el, on_el) :
17915             this.placement;
17916             
17917         var autoToken = /\s?auto?\s?/i;
17918         var autoPlace = autoToken.test(placement);
17919         if (autoPlace) {
17920             placement = placement.replace(autoToken, '') || 'top';
17921         }
17922         
17923         //this.el.detach()
17924         //this.el.setXY([0,0]);
17925         this.el.show();
17926         this.el.dom.style.display='block';
17927         this.el.addClass(placement);
17928         
17929         //this.el.appendTo(on_el);
17930         
17931         var p = this.getPosition();
17932         var box = this.el.getBox();
17933         
17934         if (autoPlace) {
17935             // fixme..
17936         }
17937         var align = Roo.bootstrap.Popover.alignment[placement];
17938         
17939 //        Roo.log(align);
17940         this.el.alignTo(on_el, align[0],align[1]);
17941         //var arrow = this.el.select('.arrow',true).first();
17942         //arrow.set(align[2], 
17943         
17944         this.el.addClass('in');
17945         
17946         
17947         if (this.el.hasClass('fade')) {
17948             // fade it?
17949         }
17950         
17951         this.hoverState = 'in';
17952         
17953         this.fireEvent('show', this);
17954         
17955     },
17956     hide : function()
17957     {
17958         this.el.setXY([0,0]);
17959         this.el.removeClass('in');
17960         this.el.hide();
17961         this.hoverState = null;
17962         
17963         this.fireEvent('hide', this);
17964     }
17965     
17966 });
17967
17968 Roo.bootstrap.Popover.alignment = {
17969     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17970     'right' : ['l-r', [10,0], 'left bs-popover-left'],
17971     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17972     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17973 };
17974
17975  /*
17976  * - LGPL
17977  *
17978  * Progress
17979  * 
17980  */
17981
17982 /**
17983  * @class Roo.bootstrap.Progress
17984  * @extends Roo.bootstrap.Component
17985  * Bootstrap Progress class
17986  * @cfg {Boolean} striped striped of the progress bar
17987  * @cfg {Boolean} active animated of the progress bar
17988  * 
17989  * 
17990  * @constructor
17991  * Create a new Progress
17992  * @param {Object} config The config object
17993  */
17994
17995 Roo.bootstrap.Progress = function(config){
17996     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17997 };
17998
17999 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
18000     
18001     striped : false,
18002     active: false,
18003     
18004     getAutoCreate : function(){
18005         var cfg = {
18006             tag: 'div',
18007             cls: 'progress'
18008         };
18009         
18010         
18011         if(this.striped){
18012             cfg.cls += ' progress-striped';
18013         }
18014       
18015         if(this.active){
18016             cfg.cls += ' active';
18017         }
18018         
18019         
18020         return cfg;
18021     }
18022    
18023 });
18024
18025  
18026
18027  /*
18028  * - LGPL
18029  *
18030  * ProgressBar
18031  * 
18032  */
18033
18034 /**
18035  * @class Roo.bootstrap.ProgressBar
18036  * @extends Roo.bootstrap.Component
18037  * Bootstrap ProgressBar class
18038  * @cfg {Number} aria_valuenow aria-value now
18039  * @cfg {Number} aria_valuemin aria-value min
18040  * @cfg {Number} aria_valuemax aria-value max
18041  * @cfg {String} label label for the progress bar
18042  * @cfg {String} panel (success | info | warning | danger )
18043  * @cfg {String} role role of the progress bar
18044  * @cfg {String} sr_only text
18045  * 
18046  * 
18047  * @constructor
18048  * Create a new ProgressBar
18049  * @param {Object} config The config object
18050  */
18051
18052 Roo.bootstrap.ProgressBar = function(config){
18053     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18054 };
18055
18056 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18057     
18058     aria_valuenow : 0,
18059     aria_valuemin : 0,
18060     aria_valuemax : 100,
18061     label : false,
18062     panel : false,
18063     role : false,
18064     sr_only: false,
18065     
18066     getAutoCreate : function()
18067     {
18068         
18069         var cfg = {
18070             tag: 'div',
18071             cls: 'progress-bar',
18072             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18073         };
18074         
18075         if(this.sr_only){
18076             cfg.cn = {
18077                 tag: 'span',
18078                 cls: 'sr-only',
18079                 html: this.sr_only
18080             }
18081         }
18082         
18083         if(this.role){
18084             cfg.role = this.role;
18085         }
18086         
18087         if(this.aria_valuenow){
18088             cfg['aria-valuenow'] = this.aria_valuenow;
18089         }
18090         
18091         if(this.aria_valuemin){
18092             cfg['aria-valuemin'] = this.aria_valuemin;
18093         }
18094         
18095         if(this.aria_valuemax){
18096             cfg['aria-valuemax'] = this.aria_valuemax;
18097         }
18098         
18099         if(this.label && !this.sr_only){
18100             cfg.html = this.label;
18101         }
18102         
18103         if(this.panel){
18104             cfg.cls += ' progress-bar-' + this.panel;
18105         }
18106         
18107         return cfg;
18108     },
18109     
18110     update : function(aria_valuenow)
18111     {
18112         this.aria_valuenow = aria_valuenow;
18113         
18114         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18115     }
18116    
18117 });
18118
18119  
18120
18121  /*
18122  * - LGPL
18123  *
18124  * column
18125  * 
18126  */
18127
18128 /**
18129  * @class Roo.bootstrap.TabGroup
18130  * @extends Roo.bootstrap.Column
18131  * Bootstrap Column class
18132  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18133  * @cfg {Boolean} carousel true to make the group behave like a carousel
18134  * @cfg {Boolean} bullets show bullets for the panels
18135  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18136  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18137  * @cfg {Boolean} showarrow (true|false) show arrow default true
18138  * 
18139  * @constructor
18140  * Create a new TabGroup
18141  * @param {Object} config The config object
18142  */
18143
18144 Roo.bootstrap.TabGroup = function(config){
18145     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18146     if (!this.navId) {
18147         this.navId = Roo.id();
18148     }
18149     this.tabs = [];
18150     Roo.bootstrap.TabGroup.register(this);
18151     
18152 };
18153
18154 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18155     
18156     carousel : false,
18157     transition : false,
18158     bullets : 0,
18159     timer : 0,
18160     autoslide : false,
18161     slideFn : false,
18162     slideOnTouch : false,
18163     showarrow : true,
18164     
18165     getAutoCreate : function()
18166     {
18167         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18168         
18169         cfg.cls += ' tab-content';
18170         
18171         if (this.carousel) {
18172             cfg.cls += ' carousel slide';
18173             
18174             cfg.cn = [{
18175                cls : 'carousel-inner',
18176                cn : []
18177             }];
18178         
18179             if(this.bullets  && !Roo.isTouch){
18180                 
18181                 var bullets = {
18182                     cls : 'carousel-bullets',
18183                     cn : []
18184                 };
18185                
18186                 if(this.bullets_cls){
18187                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18188                 }
18189                 
18190                 bullets.cn.push({
18191                     cls : 'clear'
18192                 });
18193                 
18194                 cfg.cn[0].cn.push(bullets);
18195             }
18196             
18197             if(this.showarrow){
18198                 cfg.cn[0].cn.push({
18199                     tag : 'div',
18200                     class : 'carousel-arrow',
18201                     cn : [
18202                         {
18203                             tag : 'div',
18204                             class : 'carousel-prev',
18205                             cn : [
18206                                 {
18207                                     tag : 'i',
18208                                     class : 'fa fa-chevron-left'
18209                                 }
18210                             ]
18211                         },
18212                         {
18213                             tag : 'div',
18214                             class : 'carousel-next',
18215                             cn : [
18216                                 {
18217                                     tag : 'i',
18218                                     class : 'fa fa-chevron-right'
18219                                 }
18220                             ]
18221                         }
18222                     ]
18223                 });
18224             }
18225             
18226         }
18227         
18228         return cfg;
18229     },
18230     
18231     initEvents:  function()
18232     {
18233 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18234 //            this.el.on("touchstart", this.onTouchStart, this);
18235 //        }
18236         
18237         if(this.autoslide){
18238             var _this = this;
18239             
18240             this.slideFn = window.setInterval(function() {
18241                 _this.showPanelNext();
18242             }, this.timer);
18243         }
18244         
18245         if(this.showarrow){
18246             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18247             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18248         }
18249         
18250         
18251     },
18252     
18253 //    onTouchStart : function(e, el, o)
18254 //    {
18255 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18256 //            return;
18257 //        }
18258 //        
18259 //        this.showPanelNext();
18260 //    },
18261     
18262     
18263     getChildContainer : function()
18264     {
18265         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18266     },
18267     
18268     /**
18269     * register a Navigation item
18270     * @param {Roo.bootstrap.NavItem} the navitem to add
18271     */
18272     register : function(item)
18273     {
18274         this.tabs.push( item);
18275         item.navId = this.navId; // not really needed..
18276         this.addBullet();
18277     
18278     },
18279     
18280     getActivePanel : function()
18281     {
18282         var r = false;
18283         Roo.each(this.tabs, function(t) {
18284             if (t.active) {
18285                 r = t;
18286                 return false;
18287             }
18288             return null;
18289         });
18290         return r;
18291         
18292     },
18293     getPanelByName : function(n)
18294     {
18295         var r = false;
18296         Roo.each(this.tabs, function(t) {
18297             if (t.tabId == n) {
18298                 r = t;
18299                 return false;
18300             }
18301             return null;
18302         });
18303         return r;
18304     },
18305     indexOfPanel : function(p)
18306     {
18307         var r = false;
18308         Roo.each(this.tabs, function(t,i) {
18309             if (t.tabId == p.tabId) {
18310                 r = i;
18311                 return false;
18312             }
18313             return null;
18314         });
18315         return r;
18316     },
18317     /**
18318      * show a specific panel
18319      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18320      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18321      */
18322     showPanel : function (pan)
18323     {
18324         if(this.transition || typeof(pan) == 'undefined'){
18325             Roo.log("waiting for the transitionend");
18326             return;
18327         }
18328         
18329         if (typeof(pan) == 'number') {
18330             pan = this.tabs[pan];
18331         }
18332         
18333         if (typeof(pan) == 'string') {
18334             pan = this.getPanelByName(pan);
18335         }
18336         
18337         var cur = this.getActivePanel();
18338         
18339         if(!pan || !cur){
18340             Roo.log('pan or acitve pan is undefined');
18341             return false;
18342         }
18343         
18344         if (pan.tabId == this.getActivePanel().tabId) {
18345             return true;
18346         }
18347         
18348         if (false === cur.fireEvent('beforedeactivate')) {
18349             return false;
18350         }
18351         
18352         if(this.bullets > 0 && !Roo.isTouch){
18353             this.setActiveBullet(this.indexOfPanel(pan));
18354         }
18355         
18356         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18357             
18358             this.transition = true;
18359             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18360             var lr = dir == 'next' ? 'left' : 'right';
18361             pan.el.addClass(dir); // or prev
18362             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18363             cur.el.addClass(lr); // or right
18364             pan.el.addClass(lr);
18365             
18366             var _this = this;
18367             cur.el.on('transitionend', function() {
18368                 Roo.log("trans end?");
18369                 
18370                 pan.el.removeClass([lr,dir]);
18371                 pan.setActive(true);
18372                 
18373                 cur.el.removeClass([lr]);
18374                 cur.setActive(false);
18375                 
18376                 _this.transition = false;
18377                 
18378             }, this, { single:  true } );
18379             
18380             return true;
18381         }
18382         
18383         cur.setActive(false);
18384         pan.setActive(true);
18385         
18386         return true;
18387         
18388     },
18389     showPanelNext : function()
18390     {
18391         var i = this.indexOfPanel(this.getActivePanel());
18392         
18393         if (i >= this.tabs.length - 1 && !this.autoslide) {
18394             return;
18395         }
18396         
18397         if (i >= this.tabs.length - 1 && this.autoslide) {
18398             i = -1;
18399         }
18400         
18401         this.showPanel(this.tabs[i+1]);
18402     },
18403     
18404     showPanelPrev : function()
18405     {
18406         var i = this.indexOfPanel(this.getActivePanel());
18407         
18408         if (i  < 1 && !this.autoslide) {
18409             return;
18410         }
18411         
18412         if (i < 1 && this.autoslide) {
18413             i = this.tabs.length;
18414         }
18415         
18416         this.showPanel(this.tabs[i-1]);
18417     },
18418     
18419     
18420     addBullet: function()
18421     {
18422         if(!this.bullets || Roo.isTouch){
18423             return;
18424         }
18425         var ctr = this.el.select('.carousel-bullets',true).first();
18426         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18427         var bullet = ctr.createChild({
18428             cls : 'bullet bullet-' + i
18429         },ctr.dom.lastChild);
18430         
18431         
18432         var _this = this;
18433         
18434         bullet.on('click', (function(e, el, o, ii, t){
18435
18436             e.preventDefault();
18437
18438             this.showPanel(ii);
18439
18440             if(this.autoslide && this.slideFn){
18441                 clearInterval(this.slideFn);
18442                 this.slideFn = window.setInterval(function() {
18443                     _this.showPanelNext();
18444                 }, this.timer);
18445             }
18446
18447         }).createDelegate(this, [i, bullet], true));
18448                 
18449         
18450     },
18451      
18452     setActiveBullet : function(i)
18453     {
18454         if(Roo.isTouch){
18455             return;
18456         }
18457         
18458         Roo.each(this.el.select('.bullet', true).elements, function(el){
18459             el.removeClass('selected');
18460         });
18461
18462         var bullet = this.el.select('.bullet-' + i, true).first();
18463         
18464         if(!bullet){
18465             return;
18466         }
18467         
18468         bullet.addClass('selected');
18469     }
18470     
18471     
18472   
18473 });
18474
18475  
18476
18477  
18478  
18479 Roo.apply(Roo.bootstrap.TabGroup, {
18480     
18481     groups: {},
18482      /**
18483     * register a Navigation Group
18484     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18485     */
18486     register : function(navgrp)
18487     {
18488         this.groups[navgrp.navId] = navgrp;
18489         
18490     },
18491     /**
18492     * fetch a Navigation Group based on the navigation ID
18493     * if one does not exist , it will get created.
18494     * @param {string} the navgroup to add
18495     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18496     */
18497     get: function(navId) {
18498         if (typeof(this.groups[navId]) == 'undefined') {
18499             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18500         }
18501         return this.groups[navId] ;
18502     }
18503     
18504     
18505     
18506 });
18507
18508  /*
18509  * - LGPL
18510  *
18511  * TabPanel
18512  * 
18513  */
18514
18515 /**
18516  * @class Roo.bootstrap.TabPanel
18517  * @extends Roo.bootstrap.Component
18518  * Bootstrap TabPanel class
18519  * @cfg {Boolean} active panel active
18520  * @cfg {String} html panel content
18521  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18522  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18523  * @cfg {String} href click to link..
18524  * 
18525  * 
18526  * @constructor
18527  * Create a new TabPanel
18528  * @param {Object} config The config object
18529  */
18530
18531 Roo.bootstrap.TabPanel = function(config){
18532     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18533     this.addEvents({
18534         /**
18535              * @event changed
18536              * Fires when the active status changes
18537              * @param {Roo.bootstrap.TabPanel} this
18538              * @param {Boolean} state the new state
18539             
18540          */
18541         'changed': true,
18542         /**
18543              * @event beforedeactivate
18544              * Fires before a tab is de-activated - can be used to do validation on a form.
18545              * @param {Roo.bootstrap.TabPanel} this
18546              * @return {Boolean} false if there is an error
18547             
18548          */
18549         'beforedeactivate': true
18550      });
18551     
18552     this.tabId = this.tabId || Roo.id();
18553   
18554 };
18555
18556 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18557     
18558     active: false,
18559     html: false,
18560     tabId: false,
18561     navId : false,
18562     href : '',
18563     
18564     getAutoCreate : function(){
18565         var cfg = {
18566             tag: 'div',
18567             // item is needed for carousel - not sure if it has any effect otherwise
18568             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18569             html: this.html || ''
18570         };
18571         
18572         if(this.active){
18573             cfg.cls += ' active';
18574         }
18575         
18576         if(this.tabId){
18577             cfg.tabId = this.tabId;
18578         }
18579         
18580         
18581         return cfg;
18582     },
18583     
18584     initEvents:  function()
18585     {
18586         var p = this.parent();
18587         
18588         this.navId = this.navId || p.navId;
18589         
18590         if (typeof(this.navId) != 'undefined') {
18591             // not really needed.. but just in case.. parent should be a NavGroup.
18592             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18593             
18594             tg.register(this);
18595             
18596             var i = tg.tabs.length - 1;
18597             
18598             if(this.active && tg.bullets > 0 && i < tg.bullets){
18599                 tg.setActiveBullet(i);
18600             }
18601         }
18602         
18603         this.el.on('click', this.onClick, this);
18604         
18605         if(Roo.isTouch){
18606             this.el.on("touchstart", this.onTouchStart, this);
18607             this.el.on("touchmove", this.onTouchMove, this);
18608             this.el.on("touchend", this.onTouchEnd, this);
18609         }
18610         
18611     },
18612     
18613     onRender : function(ct, position)
18614     {
18615         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18616     },
18617     
18618     setActive : function(state)
18619     {
18620         Roo.log("panel - set active " + this.tabId + "=" + state);
18621         
18622         this.active = state;
18623         if (!state) {
18624             this.el.removeClass('active');
18625             
18626         } else  if (!this.el.hasClass('active')) {
18627             this.el.addClass('active');
18628         }
18629         
18630         this.fireEvent('changed', this, state);
18631     },
18632     
18633     onClick : function(e)
18634     {
18635         e.preventDefault();
18636         
18637         if(!this.href.length){
18638             return;
18639         }
18640         
18641         window.location.href = this.href;
18642     },
18643     
18644     startX : 0,
18645     startY : 0,
18646     endX : 0,
18647     endY : 0,
18648     swiping : false,
18649     
18650     onTouchStart : function(e)
18651     {
18652         this.swiping = false;
18653         
18654         this.startX = e.browserEvent.touches[0].clientX;
18655         this.startY = e.browserEvent.touches[0].clientY;
18656     },
18657     
18658     onTouchMove : function(e)
18659     {
18660         this.swiping = true;
18661         
18662         this.endX = e.browserEvent.touches[0].clientX;
18663         this.endY = e.browserEvent.touches[0].clientY;
18664     },
18665     
18666     onTouchEnd : function(e)
18667     {
18668         if(!this.swiping){
18669             this.onClick(e);
18670             return;
18671         }
18672         
18673         var tabGroup = this.parent();
18674         
18675         if(this.endX > this.startX){ // swiping right
18676             tabGroup.showPanelPrev();
18677             return;
18678         }
18679         
18680         if(this.startX > this.endX){ // swiping left
18681             tabGroup.showPanelNext();
18682             return;
18683         }
18684     }
18685     
18686     
18687 });
18688  
18689
18690  
18691
18692  /*
18693  * - LGPL
18694  *
18695  * DateField
18696  * 
18697  */
18698
18699 /**
18700  * @class Roo.bootstrap.DateField
18701  * @extends Roo.bootstrap.Input
18702  * Bootstrap DateField class
18703  * @cfg {Number} weekStart default 0
18704  * @cfg {String} viewMode default empty, (months|years)
18705  * @cfg {String} minViewMode default empty, (months|years)
18706  * @cfg {Number} startDate default -Infinity
18707  * @cfg {Number} endDate default Infinity
18708  * @cfg {Boolean} todayHighlight default false
18709  * @cfg {Boolean} todayBtn default false
18710  * @cfg {Boolean} calendarWeeks default false
18711  * @cfg {Object} daysOfWeekDisabled default empty
18712  * @cfg {Boolean} singleMode default false (true | false)
18713  * 
18714  * @cfg {Boolean} keyboardNavigation default true
18715  * @cfg {String} language default en
18716  * 
18717  * @constructor
18718  * Create a new DateField
18719  * @param {Object} config The config object
18720  */
18721
18722 Roo.bootstrap.DateField = function(config){
18723     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18724      this.addEvents({
18725             /**
18726              * @event show
18727              * Fires when this field show.
18728              * @param {Roo.bootstrap.DateField} this
18729              * @param {Mixed} date The date value
18730              */
18731             show : true,
18732             /**
18733              * @event show
18734              * Fires when this field hide.
18735              * @param {Roo.bootstrap.DateField} this
18736              * @param {Mixed} date The date value
18737              */
18738             hide : true,
18739             /**
18740              * @event select
18741              * Fires when select a date.
18742              * @param {Roo.bootstrap.DateField} this
18743              * @param {Mixed} date The date value
18744              */
18745             select : true,
18746             /**
18747              * @event beforeselect
18748              * Fires when before select a date.
18749              * @param {Roo.bootstrap.DateField} this
18750              * @param {Mixed} date The date value
18751              */
18752             beforeselect : true
18753         });
18754 };
18755
18756 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18757     
18758     /**
18759      * @cfg {String} format
18760      * The default date format string which can be overriden for localization support.  The format must be
18761      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18762      */
18763     format : "m/d/y",
18764     /**
18765      * @cfg {String} altFormats
18766      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18767      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18768      */
18769     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18770     
18771     weekStart : 0,
18772     
18773     viewMode : '',
18774     
18775     minViewMode : '',
18776     
18777     todayHighlight : false,
18778     
18779     todayBtn: false,
18780     
18781     language: 'en',
18782     
18783     keyboardNavigation: true,
18784     
18785     calendarWeeks: false,
18786     
18787     startDate: -Infinity,
18788     
18789     endDate: Infinity,
18790     
18791     daysOfWeekDisabled: [],
18792     
18793     _events: [],
18794     
18795     singleMode : false,
18796     
18797     UTCDate: function()
18798     {
18799         return new Date(Date.UTC.apply(Date, arguments));
18800     },
18801     
18802     UTCToday: function()
18803     {
18804         var today = new Date();
18805         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18806     },
18807     
18808     getDate: function() {
18809             var d = this.getUTCDate();
18810             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18811     },
18812     
18813     getUTCDate: function() {
18814             return this.date;
18815     },
18816     
18817     setDate: function(d) {
18818             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18819     },
18820     
18821     setUTCDate: function(d) {
18822             this.date = d;
18823             this.setValue(this.formatDate(this.date));
18824     },
18825         
18826     onRender: function(ct, position)
18827     {
18828         
18829         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18830         
18831         this.language = this.language || 'en';
18832         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18833         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18834         
18835         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18836         this.format = this.format || 'm/d/y';
18837         this.isInline = false;
18838         this.isInput = true;
18839         this.component = this.el.select('.add-on', true).first() || false;
18840         this.component = (this.component && this.component.length === 0) ? false : this.component;
18841         this.hasInput = this.component && this.inputEl().length;
18842         
18843         if (typeof(this.minViewMode === 'string')) {
18844             switch (this.minViewMode) {
18845                 case 'months':
18846                     this.minViewMode = 1;
18847                     break;
18848                 case 'years':
18849                     this.minViewMode = 2;
18850                     break;
18851                 default:
18852                     this.minViewMode = 0;
18853                     break;
18854             }
18855         }
18856         
18857         if (typeof(this.viewMode === 'string')) {
18858             switch (this.viewMode) {
18859                 case 'months':
18860                     this.viewMode = 1;
18861                     break;
18862                 case 'years':
18863                     this.viewMode = 2;
18864                     break;
18865                 default:
18866                     this.viewMode = 0;
18867                     break;
18868             }
18869         }
18870                 
18871         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18872         
18873 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18874         
18875         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18876         
18877         this.picker().on('mousedown', this.onMousedown, this);
18878         this.picker().on('click', this.onClick, this);
18879         
18880         this.picker().addClass('datepicker-dropdown');
18881         
18882         this.startViewMode = this.viewMode;
18883         
18884         if(this.singleMode){
18885             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18886                 v.setVisibilityMode(Roo.Element.DISPLAY);
18887                 v.hide();
18888             });
18889             
18890             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18891                 v.setStyle('width', '189px');
18892             });
18893         }
18894         
18895         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18896             if(!this.calendarWeeks){
18897                 v.remove();
18898                 return;
18899             }
18900             
18901             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18902             v.attr('colspan', function(i, val){
18903                 return parseInt(val) + 1;
18904             });
18905         });
18906                         
18907         
18908         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18909         
18910         this.setStartDate(this.startDate);
18911         this.setEndDate(this.endDate);
18912         
18913         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18914         
18915         this.fillDow();
18916         this.fillMonths();
18917         this.update();
18918         this.showMode();
18919         
18920         if(this.isInline) {
18921             this.showPopup();
18922         }
18923     },
18924     
18925     picker : function()
18926     {
18927         return this.pickerEl;
18928 //        return this.el.select('.datepicker', true).first();
18929     },
18930     
18931     fillDow: function()
18932     {
18933         var dowCnt = this.weekStart;
18934         
18935         var dow = {
18936             tag: 'tr',
18937             cn: [
18938                 
18939             ]
18940         };
18941         
18942         if(this.calendarWeeks){
18943             dow.cn.push({
18944                 tag: 'th',
18945                 cls: 'cw',
18946                 html: '&nbsp;'
18947             })
18948         }
18949         
18950         while (dowCnt < this.weekStart + 7) {
18951             dow.cn.push({
18952                 tag: 'th',
18953                 cls: 'dow',
18954                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18955             });
18956         }
18957         
18958         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18959     },
18960     
18961     fillMonths: function()
18962     {    
18963         var i = 0;
18964         var months = this.picker().select('>.datepicker-months td', true).first();
18965         
18966         months.dom.innerHTML = '';
18967         
18968         while (i < 12) {
18969             var month = {
18970                 tag: 'span',
18971                 cls: 'month',
18972                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18973             };
18974             
18975             months.createChild(month);
18976         }
18977         
18978     },
18979     
18980     update: function()
18981     {
18982         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;
18983         
18984         if (this.date < this.startDate) {
18985             this.viewDate = new Date(this.startDate);
18986         } else if (this.date > this.endDate) {
18987             this.viewDate = new Date(this.endDate);
18988         } else {
18989             this.viewDate = new Date(this.date);
18990         }
18991         
18992         this.fill();
18993     },
18994     
18995     fill: function() 
18996     {
18997         var d = new Date(this.viewDate),
18998                 year = d.getUTCFullYear(),
18999                 month = d.getUTCMonth(),
19000                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19001                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19002                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19003                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19004                 currentDate = this.date && this.date.valueOf(),
19005                 today = this.UTCToday();
19006         
19007         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19008         
19009 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19010         
19011 //        this.picker.select('>tfoot th.today').
19012 //                                              .text(dates[this.language].today)
19013 //                                              .toggle(this.todayBtn !== false);
19014     
19015         this.updateNavArrows();
19016         this.fillMonths();
19017                                                 
19018         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19019         
19020         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19021          
19022         prevMonth.setUTCDate(day);
19023         
19024         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19025         
19026         var nextMonth = new Date(prevMonth);
19027         
19028         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19029         
19030         nextMonth = nextMonth.valueOf();
19031         
19032         var fillMonths = false;
19033         
19034         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19035         
19036         while(prevMonth.valueOf() <= nextMonth) {
19037             var clsName = '';
19038             
19039             if (prevMonth.getUTCDay() === this.weekStart) {
19040                 if(fillMonths){
19041                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19042                 }
19043                     
19044                 fillMonths = {
19045                     tag: 'tr',
19046                     cn: []
19047                 };
19048                 
19049                 if(this.calendarWeeks){
19050                     // ISO 8601: First week contains first thursday.
19051                     // ISO also states week starts on Monday, but we can be more abstract here.
19052                     var
19053                     // Start of current week: based on weekstart/current date
19054                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19055                     // Thursday of this week
19056                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19057                     // First Thursday of year, year from thursday
19058                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19059                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19060                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19061                     
19062                     fillMonths.cn.push({
19063                         tag: 'td',
19064                         cls: 'cw',
19065                         html: calWeek
19066                     });
19067                 }
19068             }
19069             
19070             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19071                 clsName += ' old';
19072             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19073                 clsName += ' new';
19074             }
19075             if (this.todayHighlight &&
19076                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19077                 prevMonth.getUTCMonth() == today.getMonth() &&
19078                 prevMonth.getUTCDate() == today.getDate()) {
19079                 clsName += ' today';
19080             }
19081             
19082             if (currentDate && prevMonth.valueOf() === currentDate) {
19083                 clsName += ' active';
19084             }
19085             
19086             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19087                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19088                     clsName += ' disabled';
19089             }
19090             
19091             fillMonths.cn.push({
19092                 tag: 'td',
19093                 cls: 'day ' + clsName,
19094                 html: prevMonth.getDate()
19095             });
19096             
19097             prevMonth.setDate(prevMonth.getDate()+1);
19098         }
19099           
19100         var currentYear = this.date && this.date.getUTCFullYear();
19101         var currentMonth = this.date && this.date.getUTCMonth();
19102         
19103         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19104         
19105         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19106             v.removeClass('active');
19107             
19108             if(currentYear === year && k === currentMonth){
19109                 v.addClass('active');
19110             }
19111             
19112             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19113                 v.addClass('disabled');
19114             }
19115             
19116         });
19117         
19118         
19119         year = parseInt(year/10, 10) * 10;
19120         
19121         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19122         
19123         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19124         
19125         year -= 1;
19126         for (var i = -1; i < 11; i++) {
19127             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19128                 tag: 'span',
19129                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19130                 html: year
19131             });
19132             
19133             year += 1;
19134         }
19135     },
19136     
19137     showMode: function(dir) 
19138     {
19139         if (dir) {
19140             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19141         }
19142         
19143         Roo.each(this.picker().select('>div',true).elements, function(v){
19144             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19145             v.hide();
19146         });
19147         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19148     },
19149     
19150     place: function()
19151     {
19152         if(this.isInline) {
19153             return;
19154         }
19155         
19156         this.picker().removeClass(['bottom', 'top']);
19157         
19158         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19159             /*
19160              * place to the top of element!
19161              *
19162              */
19163             
19164             this.picker().addClass('top');
19165             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19166             
19167             return;
19168         }
19169         
19170         this.picker().addClass('bottom');
19171         
19172         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19173     },
19174     
19175     parseDate : function(value)
19176     {
19177         if(!value || value instanceof Date){
19178             return value;
19179         }
19180         var v = Date.parseDate(value, this.format);
19181         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19182             v = Date.parseDate(value, 'Y-m-d');
19183         }
19184         if(!v && this.altFormats){
19185             if(!this.altFormatsArray){
19186                 this.altFormatsArray = this.altFormats.split("|");
19187             }
19188             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19189                 v = Date.parseDate(value, this.altFormatsArray[i]);
19190             }
19191         }
19192         return v;
19193     },
19194     
19195     formatDate : function(date, fmt)
19196     {   
19197         return (!date || !(date instanceof Date)) ?
19198         date : date.dateFormat(fmt || this.format);
19199     },
19200     
19201     onFocus : function()
19202     {
19203         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19204         this.showPopup();
19205     },
19206     
19207     onBlur : function()
19208     {
19209         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19210         
19211         var d = this.inputEl().getValue();
19212         
19213         this.setValue(d);
19214                 
19215         this.hidePopup();
19216     },
19217     
19218     showPopup : function()
19219     {
19220         this.picker().show();
19221         this.update();
19222         this.place();
19223         
19224         this.fireEvent('showpopup', this, this.date);
19225     },
19226     
19227     hidePopup : function()
19228     {
19229         if(this.isInline) {
19230             return;
19231         }
19232         this.picker().hide();
19233         this.viewMode = this.startViewMode;
19234         this.showMode();
19235         
19236         this.fireEvent('hidepopup', this, this.date);
19237         
19238     },
19239     
19240     onMousedown: function(e)
19241     {
19242         e.stopPropagation();
19243         e.preventDefault();
19244     },
19245     
19246     keyup: function(e)
19247     {
19248         Roo.bootstrap.DateField.superclass.keyup.call(this);
19249         this.update();
19250     },
19251
19252     setValue: function(v)
19253     {
19254         if(this.fireEvent('beforeselect', this, v) !== false){
19255             var d = new Date(this.parseDate(v) ).clearTime();
19256         
19257             if(isNaN(d.getTime())){
19258                 this.date = this.viewDate = '';
19259                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19260                 return;
19261             }
19262
19263             v = this.formatDate(d);
19264
19265             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19266
19267             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19268
19269             this.update();
19270
19271             this.fireEvent('select', this, this.date);
19272         }
19273     },
19274     
19275     getValue: function()
19276     {
19277         return this.formatDate(this.date);
19278     },
19279     
19280     fireKey: function(e)
19281     {
19282         if (!this.picker().isVisible()){
19283             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19284                 this.showPopup();
19285             }
19286             return;
19287         }
19288         
19289         var dateChanged = false,
19290         dir, day, month,
19291         newDate, newViewDate;
19292         
19293         switch(e.keyCode){
19294             case 27: // escape
19295                 this.hidePopup();
19296                 e.preventDefault();
19297                 break;
19298             case 37: // left
19299             case 39: // right
19300                 if (!this.keyboardNavigation) {
19301                     break;
19302                 }
19303                 dir = e.keyCode == 37 ? -1 : 1;
19304                 
19305                 if (e.ctrlKey){
19306                     newDate = this.moveYear(this.date, dir);
19307                     newViewDate = this.moveYear(this.viewDate, dir);
19308                 } else if (e.shiftKey){
19309                     newDate = this.moveMonth(this.date, dir);
19310                     newViewDate = this.moveMonth(this.viewDate, dir);
19311                 } else {
19312                     newDate = new Date(this.date);
19313                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19314                     newViewDate = new Date(this.viewDate);
19315                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19316                 }
19317                 if (this.dateWithinRange(newDate)){
19318                     this.date = newDate;
19319                     this.viewDate = newViewDate;
19320                     this.setValue(this.formatDate(this.date));
19321 //                    this.update();
19322                     e.preventDefault();
19323                     dateChanged = true;
19324                 }
19325                 break;
19326             case 38: // up
19327             case 40: // down
19328                 if (!this.keyboardNavigation) {
19329                     break;
19330                 }
19331                 dir = e.keyCode == 38 ? -1 : 1;
19332                 if (e.ctrlKey){
19333                     newDate = this.moveYear(this.date, dir);
19334                     newViewDate = this.moveYear(this.viewDate, dir);
19335                 } else if (e.shiftKey){
19336                     newDate = this.moveMonth(this.date, dir);
19337                     newViewDate = this.moveMonth(this.viewDate, dir);
19338                 } else {
19339                     newDate = new Date(this.date);
19340                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19341                     newViewDate = new Date(this.viewDate);
19342                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19343                 }
19344                 if (this.dateWithinRange(newDate)){
19345                     this.date = newDate;
19346                     this.viewDate = newViewDate;
19347                     this.setValue(this.formatDate(this.date));
19348 //                    this.update();
19349                     e.preventDefault();
19350                     dateChanged = true;
19351                 }
19352                 break;
19353             case 13: // enter
19354                 this.setValue(this.formatDate(this.date));
19355                 this.hidePopup();
19356                 e.preventDefault();
19357                 break;
19358             case 9: // tab
19359                 this.setValue(this.formatDate(this.date));
19360                 this.hidePopup();
19361                 break;
19362             case 16: // shift
19363             case 17: // ctrl
19364             case 18: // alt
19365                 break;
19366             default :
19367                 this.hidePopup();
19368                 
19369         }
19370     },
19371     
19372     
19373     onClick: function(e) 
19374     {
19375         e.stopPropagation();
19376         e.preventDefault();
19377         
19378         var target = e.getTarget();
19379         
19380         if(target.nodeName.toLowerCase() === 'i'){
19381             target = Roo.get(target).dom.parentNode;
19382         }
19383         
19384         var nodeName = target.nodeName;
19385         var className = target.className;
19386         var html = target.innerHTML;
19387         //Roo.log(nodeName);
19388         
19389         switch(nodeName.toLowerCase()) {
19390             case 'th':
19391                 switch(className) {
19392                     case 'switch':
19393                         this.showMode(1);
19394                         break;
19395                     case 'prev':
19396                     case 'next':
19397                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19398                         switch(this.viewMode){
19399                                 case 0:
19400                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19401                                         break;
19402                                 case 1:
19403                                 case 2:
19404                                         this.viewDate = this.moveYear(this.viewDate, dir);
19405                                         break;
19406                         }
19407                         this.fill();
19408                         break;
19409                     case 'today':
19410                         var date = new Date();
19411                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19412 //                        this.fill()
19413                         this.setValue(this.formatDate(this.date));
19414                         
19415                         this.hidePopup();
19416                         break;
19417                 }
19418                 break;
19419             case 'span':
19420                 if (className.indexOf('disabled') < 0) {
19421                     this.viewDate.setUTCDate(1);
19422                     if (className.indexOf('month') > -1) {
19423                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19424                     } else {
19425                         var year = parseInt(html, 10) || 0;
19426                         this.viewDate.setUTCFullYear(year);
19427                         
19428                     }
19429                     
19430                     if(this.singleMode){
19431                         this.setValue(this.formatDate(this.viewDate));
19432                         this.hidePopup();
19433                         return;
19434                     }
19435                     
19436                     this.showMode(-1);
19437                     this.fill();
19438                 }
19439                 break;
19440                 
19441             case 'td':
19442                 //Roo.log(className);
19443                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19444                     var day = parseInt(html, 10) || 1;
19445                     var year = this.viewDate.getUTCFullYear(),
19446                         month = this.viewDate.getUTCMonth();
19447
19448                     if (className.indexOf('old') > -1) {
19449                         if(month === 0 ){
19450                             month = 11;
19451                             year -= 1;
19452                         }else{
19453                             month -= 1;
19454                         }
19455                     } else if (className.indexOf('new') > -1) {
19456                         if (month == 11) {
19457                             month = 0;
19458                             year += 1;
19459                         } else {
19460                             month += 1;
19461                         }
19462                     }
19463                     //Roo.log([year,month,day]);
19464                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19465                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19466 //                    this.fill();
19467                     //Roo.log(this.formatDate(this.date));
19468                     this.setValue(this.formatDate(this.date));
19469                     this.hidePopup();
19470                 }
19471                 break;
19472         }
19473     },
19474     
19475     setStartDate: function(startDate)
19476     {
19477         this.startDate = startDate || -Infinity;
19478         if (this.startDate !== -Infinity) {
19479             this.startDate = this.parseDate(this.startDate);
19480         }
19481         this.update();
19482         this.updateNavArrows();
19483     },
19484
19485     setEndDate: function(endDate)
19486     {
19487         this.endDate = endDate || Infinity;
19488         if (this.endDate !== Infinity) {
19489             this.endDate = this.parseDate(this.endDate);
19490         }
19491         this.update();
19492         this.updateNavArrows();
19493     },
19494     
19495     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19496     {
19497         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19498         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19499             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19500         }
19501         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19502             return parseInt(d, 10);
19503         });
19504         this.update();
19505         this.updateNavArrows();
19506     },
19507     
19508     updateNavArrows: function() 
19509     {
19510         if(this.singleMode){
19511             return;
19512         }
19513         
19514         var d = new Date(this.viewDate),
19515         year = d.getUTCFullYear(),
19516         month = d.getUTCMonth();
19517         
19518         Roo.each(this.picker().select('.prev', true).elements, function(v){
19519             v.show();
19520             switch (this.viewMode) {
19521                 case 0:
19522
19523                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19524                         v.hide();
19525                     }
19526                     break;
19527                 case 1:
19528                 case 2:
19529                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19530                         v.hide();
19531                     }
19532                     break;
19533             }
19534         });
19535         
19536         Roo.each(this.picker().select('.next', true).elements, function(v){
19537             v.show();
19538             switch (this.viewMode) {
19539                 case 0:
19540
19541                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19542                         v.hide();
19543                     }
19544                     break;
19545                 case 1:
19546                 case 2:
19547                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19548                         v.hide();
19549                     }
19550                     break;
19551             }
19552         })
19553     },
19554     
19555     moveMonth: function(date, dir)
19556     {
19557         if (!dir) {
19558             return date;
19559         }
19560         var new_date = new Date(date.valueOf()),
19561         day = new_date.getUTCDate(),
19562         month = new_date.getUTCMonth(),
19563         mag = Math.abs(dir),
19564         new_month, test;
19565         dir = dir > 0 ? 1 : -1;
19566         if (mag == 1){
19567             test = dir == -1
19568             // If going back one month, make sure month is not current month
19569             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19570             ? function(){
19571                 return new_date.getUTCMonth() == month;
19572             }
19573             // If going forward one month, make sure month is as expected
19574             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19575             : function(){
19576                 return new_date.getUTCMonth() != new_month;
19577             };
19578             new_month = month + dir;
19579             new_date.setUTCMonth(new_month);
19580             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19581             if (new_month < 0 || new_month > 11) {
19582                 new_month = (new_month + 12) % 12;
19583             }
19584         } else {
19585             // For magnitudes >1, move one month at a time...
19586             for (var i=0; i<mag; i++) {
19587                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19588                 new_date = this.moveMonth(new_date, dir);
19589             }
19590             // ...then reset the day, keeping it in the new month
19591             new_month = new_date.getUTCMonth();
19592             new_date.setUTCDate(day);
19593             test = function(){
19594                 return new_month != new_date.getUTCMonth();
19595             };
19596         }
19597         // Common date-resetting loop -- if date is beyond end of month, make it
19598         // end of month
19599         while (test()){
19600             new_date.setUTCDate(--day);
19601             new_date.setUTCMonth(new_month);
19602         }
19603         return new_date;
19604     },
19605
19606     moveYear: function(date, dir)
19607     {
19608         return this.moveMonth(date, dir*12);
19609     },
19610
19611     dateWithinRange: function(date)
19612     {
19613         return date >= this.startDate && date <= this.endDate;
19614     },
19615
19616     
19617     remove: function() 
19618     {
19619         this.picker().remove();
19620     },
19621     
19622     validateValue : function(value)
19623     {
19624         if(this.getVisibilityEl().hasClass('hidden')){
19625             return true;
19626         }
19627         
19628         if(value.length < 1)  {
19629             if(this.allowBlank){
19630                 return true;
19631             }
19632             return false;
19633         }
19634         
19635         if(value.length < this.minLength){
19636             return false;
19637         }
19638         if(value.length > this.maxLength){
19639             return false;
19640         }
19641         if(this.vtype){
19642             var vt = Roo.form.VTypes;
19643             if(!vt[this.vtype](value, this)){
19644                 return false;
19645             }
19646         }
19647         if(typeof this.validator == "function"){
19648             var msg = this.validator(value);
19649             if(msg !== true){
19650                 return false;
19651             }
19652         }
19653         
19654         if(this.regex && !this.regex.test(value)){
19655             return false;
19656         }
19657         
19658         if(typeof(this.parseDate(value)) == 'undefined'){
19659             return false;
19660         }
19661         
19662         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19663             return false;
19664         }      
19665         
19666         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19667             return false;
19668         } 
19669         
19670         
19671         return true;
19672     },
19673     
19674     reset : function()
19675     {
19676         this.date = this.viewDate = '';
19677         
19678         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19679     }
19680    
19681 });
19682
19683 Roo.apply(Roo.bootstrap.DateField,  {
19684     
19685     head : {
19686         tag: 'thead',
19687         cn: [
19688         {
19689             tag: 'tr',
19690             cn: [
19691             {
19692                 tag: 'th',
19693                 cls: 'prev',
19694                 html: '<i class="fa fa-arrow-left"/>'
19695             },
19696             {
19697                 tag: 'th',
19698                 cls: 'switch',
19699                 colspan: '5'
19700             },
19701             {
19702                 tag: 'th',
19703                 cls: 'next',
19704                 html: '<i class="fa fa-arrow-right"/>'
19705             }
19706
19707             ]
19708         }
19709         ]
19710     },
19711     
19712     content : {
19713         tag: 'tbody',
19714         cn: [
19715         {
19716             tag: 'tr',
19717             cn: [
19718             {
19719                 tag: 'td',
19720                 colspan: '7'
19721             }
19722             ]
19723         }
19724         ]
19725     },
19726     
19727     footer : {
19728         tag: 'tfoot',
19729         cn: [
19730         {
19731             tag: 'tr',
19732             cn: [
19733             {
19734                 tag: 'th',
19735                 colspan: '7',
19736                 cls: 'today'
19737             }
19738                     
19739             ]
19740         }
19741         ]
19742     },
19743     
19744     dates:{
19745         en: {
19746             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19747             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19748             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19749             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19750             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19751             today: "Today"
19752         }
19753     },
19754     
19755     modes: [
19756     {
19757         clsName: 'days',
19758         navFnc: 'Month',
19759         navStep: 1
19760     },
19761     {
19762         clsName: 'months',
19763         navFnc: 'FullYear',
19764         navStep: 1
19765     },
19766     {
19767         clsName: 'years',
19768         navFnc: 'FullYear',
19769         navStep: 10
19770     }]
19771 });
19772
19773 Roo.apply(Roo.bootstrap.DateField,  {
19774   
19775     template : {
19776         tag: 'div',
19777         cls: 'datepicker dropdown-menu roo-dynamic',
19778         cn: [
19779         {
19780             tag: 'div',
19781             cls: 'datepicker-days',
19782             cn: [
19783             {
19784                 tag: 'table',
19785                 cls: 'table-condensed',
19786                 cn:[
19787                 Roo.bootstrap.DateField.head,
19788                 {
19789                     tag: 'tbody'
19790                 },
19791                 Roo.bootstrap.DateField.footer
19792                 ]
19793             }
19794             ]
19795         },
19796         {
19797             tag: 'div',
19798             cls: 'datepicker-months',
19799             cn: [
19800             {
19801                 tag: 'table',
19802                 cls: 'table-condensed',
19803                 cn:[
19804                 Roo.bootstrap.DateField.head,
19805                 Roo.bootstrap.DateField.content,
19806                 Roo.bootstrap.DateField.footer
19807                 ]
19808             }
19809             ]
19810         },
19811         {
19812             tag: 'div',
19813             cls: 'datepicker-years',
19814             cn: [
19815             {
19816                 tag: 'table',
19817                 cls: 'table-condensed',
19818                 cn:[
19819                 Roo.bootstrap.DateField.head,
19820                 Roo.bootstrap.DateField.content,
19821                 Roo.bootstrap.DateField.footer
19822                 ]
19823             }
19824             ]
19825         }
19826         ]
19827     }
19828 });
19829
19830  
19831
19832  /*
19833  * - LGPL
19834  *
19835  * TimeField
19836  * 
19837  */
19838
19839 /**
19840  * @class Roo.bootstrap.TimeField
19841  * @extends Roo.bootstrap.Input
19842  * Bootstrap DateField class
19843  * 
19844  * 
19845  * @constructor
19846  * Create a new TimeField
19847  * @param {Object} config The config object
19848  */
19849
19850 Roo.bootstrap.TimeField = function(config){
19851     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19852     this.addEvents({
19853             /**
19854              * @event show
19855              * Fires when this field show.
19856              * @param {Roo.bootstrap.DateField} thisthis
19857              * @param {Mixed} date The date value
19858              */
19859             show : true,
19860             /**
19861              * @event show
19862              * Fires when this field hide.
19863              * @param {Roo.bootstrap.DateField} this
19864              * @param {Mixed} date The date value
19865              */
19866             hide : true,
19867             /**
19868              * @event select
19869              * Fires when select a date.
19870              * @param {Roo.bootstrap.DateField} this
19871              * @param {Mixed} date The date value
19872              */
19873             select : true
19874         });
19875 };
19876
19877 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19878     
19879     /**
19880      * @cfg {String} format
19881      * The default time format string which can be overriden for localization support.  The format must be
19882      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19883      */
19884     format : "H:i",
19885        
19886     onRender: function(ct, position)
19887     {
19888         
19889         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19890                 
19891         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19892         
19893         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19894         
19895         this.pop = this.picker().select('>.datepicker-time',true).first();
19896         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19897         
19898         this.picker().on('mousedown', this.onMousedown, this);
19899         this.picker().on('click', this.onClick, this);
19900         
19901         this.picker().addClass('datepicker-dropdown');
19902     
19903         this.fillTime();
19904         this.update();
19905             
19906         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19907         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19908         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19909         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19910         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19911         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19912
19913     },
19914     
19915     fireKey: function(e){
19916         if (!this.picker().isVisible()){
19917             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19918                 this.show();
19919             }
19920             return;
19921         }
19922
19923         e.preventDefault();
19924         
19925         switch(e.keyCode){
19926             case 27: // escape
19927                 this.hide();
19928                 break;
19929             case 37: // left
19930             case 39: // right
19931                 this.onTogglePeriod();
19932                 break;
19933             case 38: // up
19934                 this.onIncrementMinutes();
19935                 break;
19936             case 40: // down
19937                 this.onDecrementMinutes();
19938                 break;
19939             case 13: // enter
19940             case 9: // tab
19941                 this.setTime();
19942                 break;
19943         }
19944     },
19945     
19946     onClick: function(e) {
19947         e.stopPropagation();
19948         e.preventDefault();
19949     },
19950     
19951     picker : function()
19952     {
19953         return this.el.select('.datepicker', true).first();
19954     },
19955     
19956     fillTime: function()
19957     {    
19958         var time = this.pop.select('tbody', true).first();
19959         
19960         time.dom.innerHTML = '';
19961         
19962         time.createChild({
19963             tag: 'tr',
19964             cn: [
19965                 {
19966                     tag: 'td',
19967                     cn: [
19968                         {
19969                             tag: 'a',
19970                             href: '#',
19971                             cls: 'btn',
19972                             cn: [
19973                                 {
19974                                     tag: 'span',
19975                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19976                                 }
19977                             ]
19978                         } 
19979                     ]
19980                 },
19981                 {
19982                     tag: 'td',
19983                     cls: 'separator'
19984                 },
19985                 {
19986                     tag: 'td',
19987                     cn: [
19988                         {
19989                             tag: 'a',
19990                             href: '#',
19991                             cls: 'btn',
19992                             cn: [
19993                                 {
19994                                     tag: 'span',
19995                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19996                                 }
19997                             ]
19998                         }
19999                     ]
20000                 },
20001                 {
20002                     tag: 'td',
20003                     cls: 'separator'
20004                 }
20005             ]
20006         });
20007         
20008         time.createChild({
20009             tag: 'tr',
20010             cn: [
20011                 {
20012                     tag: 'td',
20013                     cn: [
20014                         {
20015                             tag: 'span',
20016                             cls: 'timepicker-hour',
20017                             html: '00'
20018                         }  
20019                     ]
20020                 },
20021                 {
20022                     tag: 'td',
20023                     cls: 'separator',
20024                     html: ':'
20025                 },
20026                 {
20027                     tag: 'td',
20028                     cn: [
20029                         {
20030                             tag: 'span',
20031                             cls: 'timepicker-minute',
20032                             html: '00'
20033                         }  
20034                     ]
20035                 },
20036                 {
20037                     tag: 'td',
20038                     cls: 'separator'
20039                 },
20040                 {
20041                     tag: 'td',
20042                     cn: [
20043                         {
20044                             tag: 'button',
20045                             type: 'button',
20046                             cls: 'btn btn-primary period',
20047                             html: 'AM'
20048                             
20049                         }
20050                     ]
20051                 }
20052             ]
20053         });
20054         
20055         time.createChild({
20056             tag: 'tr',
20057             cn: [
20058                 {
20059                     tag: 'td',
20060                     cn: [
20061                         {
20062                             tag: 'a',
20063                             href: '#',
20064                             cls: 'btn',
20065                             cn: [
20066                                 {
20067                                     tag: 'span',
20068                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20069                                 }
20070                             ]
20071                         }
20072                     ]
20073                 },
20074                 {
20075                     tag: 'td',
20076                     cls: 'separator'
20077                 },
20078                 {
20079                     tag: 'td',
20080                     cn: [
20081                         {
20082                             tag: 'a',
20083                             href: '#',
20084                             cls: 'btn',
20085                             cn: [
20086                                 {
20087                                     tag: 'span',
20088                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20089                                 }
20090                             ]
20091                         }
20092                     ]
20093                 },
20094                 {
20095                     tag: 'td',
20096                     cls: 'separator'
20097                 }
20098             ]
20099         });
20100         
20101     },
20102     
20103     update: function()
20104     {
20105         
20106         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20107         
20108         this.fill();
20109     },
20110     
20111     fill: function() 
20112     {
20113         var hours = this.time.getHours();
20114         var minutes = this.time.getMinutes();
20115         var period = 'AM';
20116         
20117         if(hours > 11){
20118             period = 'PM';
20119         }
20120         
20121         if(hours == 0){
20122             hours = 12;
20123         }
20124         
20125         
20126         if(hours > 12){
20127             hours = hours - 12;
20128         }
20129         
20130         if(hours < 10){
20131             hours = '0' + hours;
20132         }
20133         
20134         if(minutes < 10){
20135             minutes = '0' + minutes;
20136         }
20137         
20138         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20139         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20140         this.pop.select('button', true).first().dom.innerHTML = period;
20141         
20142     },
20143     
20144     place: function()
20145     {   
20146         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20147         
20148         var cls = ['bottom'];
20149         
20150         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20151             cls.pop();
20152             cls.push('top');
20153         }
20154         
20155         cls.push('right');
20156         
20157         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20158             cls.pop();
20159             cls.push('left');
20160         }
20161         
20162         this.picker().addClass(cls.join('-'));
20163         
20164         var _this = this;
20165         
20166         Roo.each(cls, function(c){
20167             if(c == 'bottom'){
20168                 _this.picker().setTop(_this.inputEl().getHeight());
20169                 return;
20170             }
20171             if(c == 'top'){
20172                 _this.picker().setTop(0 - _this.picker().getHeight());
20173                 return;
20174             }
20175             
20176             if(c == 'left'){
20177                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20178                 return;
20179             }
20180             if(c == 'right'){
20181                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20182                 return;
20183             }
20184         });
20185         
20186     },
20187   
20188     onFocus : function()
20189     {
20190         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20191         this.show();
20192     },
20193     
20194     onBlur : function()
20195     {
20196         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20197         this.hide();
20198     },
20199     
20200     show : function()
20201     {
20202         this.picker().show();
20203         this.pop.show();
20204         this.update();
20205         this.place();
20206         
20207         this.fireEvent('show', this, this.date);
20208     },
20209     
20210     hide : function()
20211     {
20212         this.picker().hide();
20213         this.pop.hide();
20214         
20215         this.fireEvent('hide', this, this.date);
20216     },
20217     
20218     setTime : function()
20219     {
20220         this.hide();
20221         this.setValue(this.time.format(this.format));
20222         
20223         this.fireEvent('select', this, this.date);
20224         
20225         
20226     },
20227     
20228     onMousedown: function(e){
20229         e.stopPropagation();
20230         e.preventDefault();
20231     },
20232     
20233     onIncrementHours: function()
20234     {
20235         Roo.log('onIncrementHours');
20236         this.time = this.time.add(Date.HOUR, 1);
20237         this.update();
20238         
20239     },
20240     
20241     onDecrementHours: function()
20242     {
20243         Roo.log('onDecrementHours');
20244         this.time = this.time.add(Date.HOUR, -1);
20245         this.update();
20246     },
20247     
20248     onIncrementMinutes: function()
20249     {
20250         Roo.log('onIncrementMinutes');
20251         this.time = this.time.add(Date.MINUTE, 1);
20252         this.update();
20253     },
20254     
20255     onDecrementMinutes: function()
20256     {
20257         Roo.log('onDecrementMinutes');
20258         this.time = this.time.add(Date.MINUTE, -1);
20259         this.update();
20260     },
20261     
20262     onTogglePeriod: function()
20263     {
20264         Roo.log('onTogglePeriod');
20265         this.time = this.time.add(Date.HOUR, 12);
20266         this.update();
20267     }
20268     
20269    
20270 });
20271
20272 Roo.apply(Roo.bootstrap.TimeField,  {
20273     
20274     content : {
20275         tag: 'tbody',
20276         cn: [
20277             {
20278                 tag: 'tr',
20279                 cn: [
20280                 {
20281                     tag: 'td',
20282                     colspan: '7'
20283                 }
20284                 ]
20285             }
20286         ]
20287     },
20288     
20289     footer : {
20290         tag: 'tfoot',
20291         cn: [
20292             {
20293                 tag: 'tr',
20294                 cn: [
20295                 {
20296                     tag: 'th',
20297                     colspan: '7',
20298                     cls: '',
20299                     cn: [
20300                         {
20301                             tag: 'button',
20302                             cls: 'btn btn-info ok',
20303                             html: 'OK'
20304                         }
20305                     ]
20306                 }
20307
20308                 ]
20309             }
20310         ]
20311     }
20312 });
20313
20314 Roo.apply(Roo.bootstrap.TimeField,  {
20315   
20316     template : {
20317         tag: 'div',
20318         cls: 'datepicker dropdown-menu',
20319         cn: [
20320             {
20321                 tag: 'div',
20322                 cls: 'datepicker-time',
20323                 cn: [
20324                 {
20325                     tag: 'table',
20326                     cls: 'table-condensed',
20327                     cn:[
20328                     Roo.bootstrap.TimeField.content,
20329                     Roo.bootstrap.TimeField.footer
20330                     ]
20331                 }
20332                 ]
20333             }
20334         ]
20335     }
20336 });
20337
20338  
20339
20340  /*
20341  * - LGPL
20342  *
20343  * MonthField
20344  * 
20345  */
20346
20347 /**
20348  * @class Roo.bootstrap.MonthField
20349  * @extends Roo.bootstrap.Input
20350  * Bootstrap MonthField class
20351  * 
20352  * @cfg {String} language default en
20353  * 
20354  * @constructor
20355  * Create a new MonthField
20356  * @param {Object} config The config object
20357  */
20358
20359 Roo.bootstrap.MonthField = function(config){
20360     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20361     
20362     this.addEvents({
20363         /**
20364          * @event show
20365          * Fires when this field show.
20366          * @param {Roo.bootstrap.MonthField} this
20367          * @param {Mixed} date The date value
20368          */
20369         show : true,
20370         /**
20371          * @event show
20372          * Fires when this field hide.
20373          * @param {Roo.bootstrap.MonthField} this
20374          * @param {Mixed} date The date value
20375          */
20376         hide : true,
20377         /**
20378          * @event select
20379          * Fires when select a date.
20380          * @param {Roo.bootstrap.MonthField} this
20381          * @param {String} oldvalue The old value
20382          * @param {String} newvalue The new value
20383          */
20384         select : true
20385     });
20386 };
20387
20388 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20389     
20390     onRender: function(ct, position)
20391     {
20392         
20393         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20394         
20395         this.language = this.language || 'en';
20396         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20397         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20398         
20399         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20400         this.isInline = false;
20401         this.isInput = true;
20402         this.component = this.el.select('.add-on', true).first() || false;
20403         this.component = (this.component && this.component.length === 0) ? false : this.component;
20404         this.hasInput = this.component && this.inputEL().length;
20405         
20406         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20407         
20408         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20409         
20410         this.picker().on('mousedown', this.onMousedown, this);
20411         this.picker().on('click', this.onClick, this);
20412         
20413         this.picker().addClass('datepicker-dropdown');
20414         
20415         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20416             v.setStyle('width', '189px');
20417         });
20418         
20419         this.fillMonths();
20420         
20421         this.update();
20422         
20423         if(this.isInline) {
20424             this.show();
20425         }
20426         
20427     },
20428     
20429     setValue: function(v, suppressEvent)
20430     {   
20431         var o = this.getValue();
20432         
20433         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20434         
20435         this.update();
20436
20437         if(suppressEvent !== true){
20438             this.fireEvent('select', this, o, v);
20439         }
20440         
20441     },
20442     
20443     getValue: function()
20444     {
20445         return this.value;
20446     },
20447     
20448     onClick: function(e) 
20449     {
20450         e.stopPropagation();
20451         e.preventDefault();
20452         
20453         var target = e.getTarget();
20454         
20455         if(target.nodeName.toLowerCase() === 'i'){
20456             target = Roo.get(target).dom.parentNode;
20457         }
20458         
20459         var nodeName = target.nodeName;
20460         var className = target.className;
20461         var html = target.innerHTML;
20462         
20463         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20464             return;
20465         }
20466         
20467         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20468         
20469         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20470         
20471         this.hide();
20472                         
20473     },
20474     
20475     picker : function()
20476     {
20477         return this.pickerEl;
20478     },
20479     
20480     fillMonths: function()
20481     {    
20482         var i = 0;
20483         var months = this.picker().select('>.datepicker-months td', true).first();
20484         
20485         months.dom.innerHTML = '';
20486         
20487         while (i < 12) {
20488             var month = {
20489                 tag: 'span',
20490                 cls: 'month',
20491                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20492             };
20493             
20494             months.createChild(month);
20495         }
20496         
20497     },
20498     
20499     update: function()
20500     {
20501         var _this = this;
20502         
20503         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20504             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20505         }
20506         
20507         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20508             e.removeClass('active');
20509             
20510             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20511                 e.addClass('active');
20512             }
20513         })
20514     },
20515     
20516     place: function()
20517     {
20518         if(this.isInline) {
20519             return;
20520         }
20521         
20522         this.picker().removeClass(['bottom', 'top']);
20523         
20524         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20525             /*
20526              * place to the top of element!
20527              *
20528              */
20529             
20530             this.picker().addClass('top');
20531             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20532             
20533             return;
20534         }
20535         
20536         this.picker().addClass('bottom');
20537         
20538         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20539     },
20540     
20541     onFocus : function()
20542     {
20543         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20544         this.show();
20545     },
20546     
20547     onBlur : function()
20548     {
20549         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20550         
20551         var d = this.inputEl().getValue();
20552         
20553         this.setValue(d);
20554                 
20555         this.hide();
20556     },
20557     
20558     show : function()
20559     {
20560         this.picker().show();
20561         this.picker().select('>.datepicker-months', true).first().show();
20562         this.update();
20563         this.place();
20564         
20565         this.fireEvent('show', this, this.date);
20566     },
20567     
20568     hide : function()
20569     {
20570         if(this.isInline) {
20571             return;
20572         }
20573         this.picker().hide();
20574         this.fireEvent('hide', this, this.date);
20575         
20576     },
20577     
20578     onMousedown: function(e)
20579     {
20580         e.stopPropagation();
20581         e.preventDefault();
20582     },
20583     
20584     keyup: function(e)
20585     {
20586         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20587         this.update();
20588     },
20589
20590     fireKey: function(e)
20591     {
20592         if (!this.picker().isVisible()){
20593             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20594                 this.show();
20595             }
20596             return;
20597         }
20598         
20599         var dir;
20600         
20601         switch(e.keyCode){
20602             case 27: // escape
20603                 this.hide();
20604                 e.preventDefault();
20605                 break;
20606             case 37: // left
20607             case 39: // right
20608                 dir = e.keyCode == 37 ? -1 : 1;
20609                 
20610                 this.vIndex = this.vIndex + dir;
20611                 
20612                 if(this.vIndex < 0){
20613                     this.vIndex = 0;
20614                 }
20615                 
20616                 if(this.vIndex > 11){
20617                     this.vIndex = 11;
20618                 }
20619                 
20620                 if(isNaN(this.vIndex)){
20621                     this.vIndex = 0;
20622                 }
20623                 
20624                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20625                 
20626                 break;
20627             case 38: // up
20628             case 40: // down
20629                 
20630                 dir = e.keyCode == 38 ? -1 : 1;
20631                 
20632                 this.vIndex = this.vIndex + dir * 4;
20633                 
20634                 if(this.vIndex < 0){
20635                     this.vIndex = 0;
20636                 }
20637                 
20638                 if(this.vIndex > 11){
20639                     this.vIndex = 11;
20640                 }
20641                 
20642                 if(isNaN(this.vIndex)){
20643                     this.vIndex = 0;
20644                 }
20645                 
20646                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20647                 break;
20648                 
20649             case 13: // enter
20650                 
20651                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20652                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20653                 }
20654                 
20655                 this.hide();
20656                 e.preventDefault();
20657                 break;
20658             case 9: // tab
20659                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20660                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20661                 }
20662                 this.hide();
20663                 break;
20664             case 16: // shift
20665             case 17: // ctrl
20666             case 18: // alt
20667                 break;
20668             default :
20669                 this.hide();
20670                 
20671         }
20672     },
20673     
20674     remove: function() 
20675     {
20676         this.picker().remove();
20677     }
20678    
20679 });
20680
20681 Roo.apply(Roo.bootstrap.MonthField,  {
20682     
20683     content : {
20684         tag: 'tbody',
20685         cn: [
20686         {
20687             tag: 'tr',
20688             cn: [
20689             {
20690                 tag: 'td',
20691                 colspan: '7'
20692             }
20693             ]
20694         }
20695         ]
20696     },
20697     
20698     dates:{
20699         en: {
20700             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20701             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20702         }
20703     }
20704 });
20705
20706 Roo.apply(Roo.bootstrap.MonthField,  {
20707   
20708     template : {
20709         tag: 'div',
20710         cls: 'datepicker dropdown-menu roo-dynamic',
20711         cn: [
20712             {
20713                 tag: 'div',
20714                 cls: 'datepicker-months',
20715                 cn: [
20716                 {
20717                     tag: 'table',
20718                     cls: 'table-condensed',
20719                     cn:[
20720                         Roo.bootstrap.DateField.content
20721                     ]
20722                 }
20723                 ]
20724             }
20725         ]
20726     }
20727 });
20728
20729  
20730
20731  
20732  /*
20733  * - LGPL
20734  *
20735  * CheckBox
20736  * 
20737  */
20738
20739 /**
20740  * @class Roo.bootstrap.CheckBox
20741  * @extends Roo.bootstrap.Input
20742  * Bootstrap CheckBox class
20743  * 
20744  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20745  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20746  * @cfg {String} boxLabel The text that appears beside the checkbox
20747  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20748  * @cfg {Boolean} checked initnal the element
20749  * @cfg {Boolean} inline inline the element (default false)
20750  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20751  * @cfg {String} tooltip label tooltip
20752  * 
20753  * @constructor
20754  * Create a new CheckBox
20755  * @param {Object} config The config object
20756  */
20757
20758 Roo.bootstrap.CheckBox = function(config){
20759     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20760    
20761     this.addEvents({
20762         /**
20763         * @event check
20764         * Fires when the element is checked or unchecked.
20765         * @param {Roo.bootstrap.CheckBox} this This input
20766         * @param {Boolean} checked The new checked value
20767         */
20768        check : true,
20769        /**
20770         * @event click
20771         * Fires when the element is click.
20772         * @param {Roo.bootstrap.CheckBox} this This input
20773         */
20774        click : true
20775     });
20776     
20777 };
20778
20779 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20780   
20781     inputType: 'checkbox',
20782     inputValue: 1,
20783     valueOff: 0,
20784     boxLabel: false,
20785     checked: false,
20786     weight : false,
20787     inline: false,
20788     tooltip : '',
20789     
20790     getAutoCreate : function()
20791     {
20792         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20793         
20794         var id = Roo.id();
20795         
20796         var cfg = {};
20797         
20798         cfg.cls = 'form-group ' + this.inputType; //input-group
20799         
20800         if(this.inline){
20801             cfg.cls += ' ' + this.inputType + '-inline';
20802         }
20803         
20804         var input =  {
20805             tag: 'input',
20806             id : id,
20807             type : this.inputType,
20808             value : this.inputValue,
20809             cls : 'roo-' + this.inputType, //'form-box',
20810             placeholder : this.placeholder || ''
20811             
20812         };
20813         
20814         if(this.inputType != 'radio'){
20815             var hidden =  {
20816                 tag: 'input',
20817                 type : 'hidden',
20818                 cls : 'roo-hidden-value',
20819                 value : this.checked ? this.inputValue : this.valueOff
20820             };
20821         }
20822         
20823             
20824         if (this.weight) { // Validity check?
20825             cfg.cls += " " + this.inputType + "-" + this.weight;
20826         }
20827         
20828         if (this.disabled) {
20829             input.disabled=true;
20830         }
20831         
20832         if(this.checked){
20833             input.checked = this.checked;
20834         }
20835         
20836         if (this.name) {
20837             
20838             input.name = this.name;
20839             
20840             if(this.inputType != 'radio'){
20841                 hidden.name = this.name;
20842                 input.name = '_hidden_' + this.name;
20843             }
20844         }
20845         
20846         if (this.size) {
20847             input.cls += ' input-' + this.size;
20848         }
20849         
20850         var settings=this;
20851         
20852         ['xs','sm','md','lg'].map(function(size){
20853             if (settings[size]) {
20854                 cfg.cls += ' col-' + size + '-' + settings[size];
20855             }
20856         });
20857         
20858         var inputblock = input;
20859          
20860         if (this.before || this.after) {
20861             
20862             inputblock = {
20863                 cls : 'input-group',
20864                 cn :  [] 
20865             };
20866             
20867             if (this.before) {
20868                 inputblock.cn.push({
20869                     tag :'span',
20870                     cls : 'input-group-addon',
20871                     html : this.before
20872                 });
20873             }
20874             
20875             inputblock.cn.push(input);
20876             
20877             if(this.inputType != 'radio'){
20878                 inputblock.cn.push(hidden);
20879             }
20880             
20881             if (this.after) {
20882                 inputblock.cn.push({
20883                     tag :'span',
20884                     cls : 'input-group-addon',
20885                     html : this.after
20886                 });
20887             }
20888             
20889         }
20890         
20891         if (align ==='left' && this.fieldLabel.length) {
20892 //                Roo.log("left and has label");
20893             cfg.cn = [
20894                 {
20895                     tag: 'label',
20896                     'for' :  id,
20897                     cls : 'control-label',
20898                     html : this.fieldLabel
20899                 },
20900                 {
20901                     cls : "", 
20902                     cn: [
20903                         inputblock
20904                     ]
20905                 }
20906             ];
20907             
20908             if(this.labelWidth > 12){
20909                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20910             }
20911             
20912             if(this.labelWidth < 13 && this.labelmd == 0){
20913                 this.labelmd = this.labelWidth;
20914             }
20915             
20916             if(this.labellg > 0){
20917                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20918                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20919             }
20920             
20921             if(this.labelmd > 0){
20922                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20923                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20924             }
20925             
20926             if(this.labelsm > 0){
20927                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20928                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20929             }
20930             
20931             if(this.labelxs > 0){
20932                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20933                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20934             }
20935             
20936         } else if ( this.fieldLabel.length) {
20937 //                Roo.log(" label");
20938                 cfg.cn = [
20939                    
20940                     {
20941                         tag: this.boxLabel ? 'span' : 'label',
20942                         'for': id,
20943                         cls: 'control-label box-input-label',
20944                         //cls : 'input-group-addon',
20945                         html : this.fieldLabel
20946                     },
20947                     
20948                     inputblock
20949                     
20950                 ];
20951
20952         } else {
20953             
20954 //                Roo.log(" no label && no align");
20955                 cfg.cn = [  inputblock ] ;
20956                 
20957                 
20958         }
20959         
20960         if(this.boxLabel){
20961              var boxLabelCfg = {
20962                 tag: 'label',
20963                 //'for': id, // box label is handled by onclick - so no for...
20964                 cls: 'box-label',
20965                 html: this.boxLabel
20966             };
20967             
20968             if(this.tooltip){
20969                 boxLabelCfg.tooltip = this.tooltip;
20970             }
20971              
20972             cfg.cn.push(boxLabelCfg);
20973         }
20974         
20975         if(this.inputType != 'radio'){
20976             cfg.cn.push(hidden);
20977         }
20978         
20979         return cfg;
20980         
20981     },
20982     
20983     /**
20984      * return the real input element.
20985      */
20986     inputEl: function ()
20987     {
20988         return this.el.select('input.roo-' + this.inputType,true).first();
20989     },
20990     hiddenEl: function ()
20991     {
20992         return this.el.select('input.roo-hidden-value',true).first();
20993     },
20994     
20995     labelEl: function()
20996     {
20997         return this.el.select('label.control-label',true).first();
20998     },
20999     /* depricated... */
21000     
21001     label: function()
21002     {
21003         return this.labelEl();
21004     },
21005     
21006     boxLabelEl: function()
21007     {
21008         return this.el.select('label.box-label',true).first();
21009     },
21010     
21011     initEvents : function()
21012     {
21013 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21014         
21015         this.inputEl().on('click', this.onClick,  this);
21016         
21017         if (this.boxLabel) { 
21018             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
21019         }
21020         
21021         this.startValue = this.getValue();
21022         
21023         if(this.groupId){
21024             Roo.bootstrap.CheckBox.register(this);
21025         }
21026     },
21027     
21028     onClick : function(e)
21029     {   
21030         if(this.fireEvent('click', this, e) !== false){
21031             this.setChecked(!this.checked);
21032         }
21033         
21034     },
21035     
21036     setChecked : function(state,suppressEvent)
21037     {
21038         this.startValue = this.getValue();
21039
21040         if(this.inputType == 'radio'){
21041             
21042             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21043                 e.dom.checked = false;
21044             });
21045             
21046             this.inputEl().dom.checked = true;
21047             
21048             this.inputEl().dom.value = this.inputValue;
21049             
21050             if(suppressEvent !== true){
21051                 this.fireEvent('check', this, true);
21052             }
21053             
21054             this.validate();
21055             
21056             return;
21057         }
21058         
21059         this.checked = state;
21060         
21061         this.inputEl().dom.checked = state;
21062         
21063         
21064         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21065         
21066         if(suppressEvent !== true){
21067             this.fireEvent('check', this, state);
21068         }
21069         
21070         this.validate();
21071     },
21072     
21073     getValue : function()
21074     {
21075         if(this.inputType == 'radio'){
21076             return this.getGroupValue();
21077         }
21078         
21079         return this.hiddenEl().dom.value;
21080         
21081     },
21082     
21083     getGroupValue : function()
21084     {
21085         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21086             return '';
21087         }
21088         
21089         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21090     },
21091     
21092     setValue : function(v,suppressEvent)
21093     {
21094         if(this.inputType == 'radio'){
21095             this.setGroupValue(v, suppressEvent);
21096             return;
21097         }
21098         
21099         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21100         
21101         this.validate();
21102     },
21103     
21104     setGroupValue : function(v, suppressEvent)
21105     {
21106         this.startValue = this.getValue();
21107         
21108         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21109             e.dom.checked = false;
21110             
21111             if(e.dom.value == v){
21112                 e.dom.checked = true;
21113             }
21114         });
21115         
21116         if(suppressEvent !== true){
21117             this.fireEvent('check', this, true);
21118         }
21119
21120         this.validate();
21121         
21122         return;
21123     },
21124     
21125     validate : function()
21126     {
21127         if(this.getVisibilityEl().hasClass('hidden')){
21128             return true;
21129         }
21130         
21131         if(
21132                 this.disabled || 
21133                 (this.inputType == 'radio' && this.validateRadio()) ||
21134                 (this.inputType == 'checkbox' && this.validateCheckbox())
21135         ){
21136             this.markValid();
21137             return true;
21138         }
21139         
21140         this.markInvalid();
21141         return false;
21142     },
21143     
21144     validateRadio : function()
21145     {
21146         if(this.getVisibilityEl().hasClass('hidden')){
21147             return true;
21148         }
21149         
21150         if(this.allowBlank){
21151             return true;
21152         }
21153         
21154         var valid = false;
21155         
21156         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21157             if(!e.dom.checked){
21158                 return;
21159             }
21160             
21161             valid = true;
21162             
21163             return false;
21164         });
21165         
21166         return valid;
21167     },
21168     
21169     validateCheckbox : function()
21170     {
21171         if(!this.groupId){
21172             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21173             //return (this.getValue() == this.inputValue) ? true : false;
21174         }
21175         
21176         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21177         
21178         if(!group){
21179             return false;
21180         }
21181         
21182         var r = false;
21183         
21184         for(var i in group){
21185             if(group[i].el.isVisible(true)){
21186                 r = false;
21187                 break;
21188             }
21189             
21190             r = true;
21191         }
21192         
21193         for(var i in group){
21194             if(r){
21195                 break;
21196             }
21197             
21198             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21199         }
21200         
21201         return r;
21202     },
21203     
21204     /**
21205      * Mark this field as valid
21206      */
21207     markValid : function()
21208     {
21209         var _this = this;
21210         
21211         this.fireEvent('valid', this);
21212         
21213         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21214         
21215         if(this.groupId){
21216             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21217         }
21218         
21219         if(label){
21220             label.markValid();
21221         }
21222
21223         if(this.inputType == 'radio'){
21224             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21225                 var fg = e.findParent('.form-group', false, true);
21226                 if (Roo.bootstrap.version == 3) {
21227                     fg.removeClass([_this.invalidClass, _this.validClass]);
21228                     fg.addClass(_this.validClass);
21229                 } else {
21230                     fg.removeClass(['is-valid', 'is-invalid']);
21231                     fg.addClass('is-valid');
21232                 }
21233             });
21234             
21235             return;
21236         }
21237
21238         if(!this.groupId){
21239             var fg = this.el.findParent('.form-group', false, true);
21240             if (Roo.bootstrap.version == 3) {
21241                 fg.removeClass([this.invalidClass, this.validClass]);
21242                 fg.addClass(this.validClass);
21243             } else {
21244                 fg.removeClass(['is-valid', 'is-invalid']);
21245                 fg.addClass('is-valid');
21246             }
21247             return;
21248         }
21249         
21250         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21251         
21252         if(!group){
21253             return;
21254         }
21255         
21256         for(var i in group){
21257             var fg = group[i].el.findParent('.form-group', false, true);
21258             if (Roo.bootstrap.version == 3) {
21259                 fg.removeClass([this.invalidClass, this.validClass]);
21260                 fg.addClass(this.validClass);
21261             } else {
21262                 fg.removeClass(['is-valid', 'is-invalid']);
21263                 fg.addClass('is-valid');
21264             }
21265         }
21266     },
21267     
21268      /**
21269      * Mark this field as invalid
21270      * @param {String} msg The validation message
21271      */
21272     markInvalid : function(msg)
21273     {
21274         if(this.allowBlank){
21275             return;
21276         }
21277         
21278         var _this = this;
21279         
21280         this.fireEvent('invalid', this, msg);
21281         
21282         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21283         
21284         if(this.groupId){
21285             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21286         }
21287         
21288         if(label){
21289             label.markInvalid();
21290         }
21291             
21292         if(this.inputType == 'radio'){
21293             
21294             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21295                 var fg = e.findParent('.form-group', false, true);
21296                 if (Roo.bootstrap.version == 3) {
21297                     fg.removeClass([_this.invalidClass, _this.validClass]);
21298                     fg.addClass(_this.invalidClass);
21299                 } else {
21300                     fg.removeClass(['is-invalid', 'is-valid']);
21301                     fg.addClass('is-invalid');
21302                 }
21303             });
21304             
21305             return;
21306         }
21307         
21308         if(!this.groupId){
21309             var fg = this.el.findParent('.form-group', false, true);
21310             if (Roo.bootstrap.version == 3) {
21311                 fg.removeClass([_this.invalidClass, _this.validClass]);
21312                 fg.addClass(_this.invalidClass);
21313             } else {
21314                 fg.removeClass(['is-invalid', 'is-valid']);
21315                 fg.addClass('is-invalid');
21316             }
21317             return;
21318         }
21319         
21320         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21321         
21322         if(!group){
21323             return;
21324         }
21325         
21326         for(var i in group){
21327             var fg = group[i].el.findParent('.form-group', false, true);
21328             if (Roo.bootstrap.version == 3) {
21329                 fg.removeClass([_this.invalidClass, _this.validClass]);
21330                 fg.addClass(_this.invalidClass);
21331             } else {
21332                 fg.removeClass(['is-invalid', 'is-valid']);
21333                 fg.addClass('is-invalid');
21334             }
21335         }
21336         
21337     },
21338     
21339     clearInvalid : function()
21340     {
21341         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21342         
21343         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21344         
21345         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21346         
21347         if (label && label.iconEl) {
21348             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21349             label.iconEl.removeClass(['is-invalid', 'is-valid']);
21350         }
21351     },
21352     
21353     disable : function()
21354     {
21355         if(this.inputType != 'radio'){
21356             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21357             return;
21358         }
21359         
21360         var _this = this;
21361         
21362         if(this.rendered){
21363             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21364                 _this.getActionEl().addClass(this.disabledClass);
21365                 e.dom.disabled = true;
21366             });
21367         }
21368         
21369         this.disabled = true;
21370         this.fireEvent("disable", this);
21371         return this;
21372     },
21373
21374     enable : function()
21375     {
21376         if(this.inputType != 'radio'){
21377             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21378             return;
21379         }
21380         
21381         var _this = this;
21382         
21383         if(this.rendered){
21384             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21385                 _this.getActionEl().removeClass(this.disabledClass);
21386                 e.dom.disabled = false;
21387             });
21388         }
21389         
21390         this.disabled = false;
21391         this.fireEvent("enable", this);
21392         return this;
21393     },
21394     
21395     setBoxLabel : function(v)
21396     {
21397         this.boxLabel = v;
21398         
21399         if(this.rendered){
21400             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21401         }
21402     }
21403
21404 });
21405
21406 Roo.apply(Roo.bootstrap.CheckBox, {
21407     
21408     groups: {},
21409     
21410      /**
21411     * register a CheckBox Group
21412     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21413     */
21414     register : function(checkbox)
21415     {
21416         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21417             this.groups[checkbox.groupId] = {};
21418         }
21419         
21420         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21421             return;
21422         }
21423         
21424         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21425         
21426     },
21427     /**
21428     * fetch a CheckBox Group based on the group ID
21429     * @param {string} the group ID
21430     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21431     */
21432     get: function(groupId) {
21433         if (typeof(this.groups[groupId]) == 'undefined') {
21434             return false;
21435         }
21436         
21437         return this.groups[groupId] ;
21438     }
21439     
21440     
21441 });
21442 /*
21443  * - LGPL
21444  *
21445  * RadioItem
21446  * 
21447  */
21448
21449 /**
21450  * @class Roo.bootstrap.Radio
21451  * @extends Roo.bootstrap.Component
21452  * Bootstrap Radio class
21453  * @cfg {String} boxLabel - the label associated
21454  * @cfg {String} value - the value of radio
21455  * 
21456  * @constructor
21457  * Create a new Radio
21458  * @param {Object} config The config object
21459  */
21460 Roo.bootstrap.Radio = function(config){
21461     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21462     
21463 };
21464
21465 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21466     
21467     boxLabel : '',
21468     
21469     value : '',
21470     
21471     getAutoCreate : function()
21472     {
21473         var cfg = {
21474             tag : 'div',
21475             cls : 'form-group radio',
21476             cn : [
21477                 {
21478                     tag : 'label',
21479                     cls : 'box-label',
21480                     html : this.boxLabel
21481                 }
21482             ]
21483         };
21484         
21485         return cfg;
21486     },
21487     
21488     initEvents : function() 
21489     {
21490         this.parent().register(this);
21491         
21492         this.el.on('click', this.onClick, this);
21493         
21494     },
21495     
21496     onClick : function(e)
21497     {
21498         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21499             this.setChecked(true);
21500         }
21501     },
21502     
21503     setChecked : function(state, suppressEvent)
21504     {
21505         this.parent().setValue(this.value, suppressEvent);
21506         
21507     },
21508     
21509     setBoxLabel : function(v)
21510     {
21511         this.boxLabel = v;
21512         
21513         if(this.rendered){
21514             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21515         }
21516     }
21517     
21518 });
21519  
21520
21521  /*
21522  * - LGPL
21523  *
21524  * Input
21525  * 
21526  */
21527
21528 /**
21529  * @class Roo.bootstrap.SecurePass
21530  * @extends Roo.bootstrap.Input
21531  * Bootstrap SecurePass class
21532  *
21533  * 
21534  * @constructor
21535  * Create a new SecurePass
21536  * @param {Object} config The config object
21537  */
21538  
21539 Roo.bootstrap.SecurePass = function (config) {
21540     // these go here, so the translation tool can replace them..
21541     this.errors = {
21542         PwdEmpty: "Please type a password, and then retype it to confirm.",
21543         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21544         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21545         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21546         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21547         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21548         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21549         TooWeak: "Your password is Too Weak."
21550     },
21551     this.meterLabel = "Password strength:";
21552     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21553     this.meterClass = [
21554         "roo-password-meter-tooweak", 
21555         "roo-password-meter-weak", 
21556         "roo-password-meter-medium", 
21557         "roo-password-meter-strong", 
21558         "roo-password-meter-grey"
21559     ];
21560     
21561     this.errors = {};
21562     
21563     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21564 }
21565
21566 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21567     /**
21568      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21569      * {
21570      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21571      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21572      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21573      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21574      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21575      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21576      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21577      * })
21578      */
21579     // private
21580     
21581     meterWidth: 300,
21582     errorMsg :'',    
21583     errors: false,
21584     imageRoot: '/',
21585     /**
21586      * @cfg {String/Object} Label for the strength meter (defaults to
21587      * 'Password strength:')
21588      */
21589     // private
21590     meterLabel: '',
21591     /**
21592      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21593      * ['Weak', 'Medium', 'Strong'])
21594      */
21595     // private    
21596     pwdStrengths: false,    
21597     // private
21598     strength: 0,
21599     // private
21600     _lastPwd: null,
21601     // private
21602     kCapitalLetter: 0,
21603     kSmallLetter: 1,
21604     kDigit: 2,
21605     kPunctuation: 3,
21606     
21607     insecure: false,
21608     // private
21609     initEvents: function ()
21610     {
21611         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21612
21613         if (this.el.is('input[type=password]') && Roo.isSafari) {
21614             this.el.on('keydown', this.SafariOnKeyDown, this);
21615         }
21616
21617         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21618     },
21619     // private
21620     onRender: function (ct, position)
21621     {
21622         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21623         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21624         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21625
21626         this.trigger.createChild({
21627                    cn: [
21628                     {
21629                     //id: 'PwdMeter',
21630                     tag: 'div',
21631                     cls: 'roo-password-meter-grey col-xs-12',
21632                     style: {
21633                         //width: 0,
21634                         //width: this.meterWidth + 'px'                                                
21635                         }
21636                     },
21637                     {                            
21638                          cls: 'roo-password-meter-text'                          
21639                     }
21640                 ]            
21641         });
21642
21643          
21644         if (this.hideTrigger) {
21645             this.trigger.setDisplayed(false);
21646         }
21647         this.setSize(this.width || '', this.height || '');
21648     },
21649     // private
21650     onDestroy: function ()
21651     {
21652         if (this.trigger) {
21653             this.trigger.removeAllListeners();
21654             this.trigger.remove();
21655         }
21656         if (this.wrap) {
21657             this.wrap.remove();
21658         }
21659         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21660     },
21661     // private
21662     checkStrength: function ()
21663     {
21664         var pwd = this.inputEl().getValue();
21665         if (pwd == this._lastPwd) {
21666             return;
21667         }
21668
21669         var strength;
21670         if (this.ClientSideStrongPassword(pwd)) {
21671             strength = 3;
21672         } else if (this.ClientSideMediumPassword(pwd)) {
21673             strength = 2;
21674         } else if (this.ClientSideWeakPassword(pwd)) {
21675             strength = 1;
21676         } else {
21677             strength = 0;
21678         }
21679         
21680         Roo.log('strength1: ' + strength);
21681         
21682         //var pm = this.trigger.child('div/div/div').dom;
21683         var pm = this.trigger.child('div/div');
21684         pm.removeClass(this.meterClass);
21685         pm.addClass(this.meterClass[strength]);
21686                 
21687         
21688         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21689                 
21690         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21691         
21692         this._lastPwd = pwd;
21693     },
21694     reset: function ()
21695     {
21696         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21697         
21698         this._lastPwd = '';
21699         
21700         var pm = this.trigger.child('div/div');
21701         pm.removeClass(this.meterClass);
21702         pm.addClass('roo-password-meter-grey');        
21703         
21704         
21705         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21706         
21707         pt.innerHTML = '';
21708         this.inputEl().dom.type='password';
21709     },
21710     // private
21711     validateValue: function (value)
21712     {
21713         
21714         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21715             return false;
21716         }
21717         if (value.length == 0) {
21718             if (this.allowBlank) {
21719                 this.clearInvalid();
21720                 return true;
21721             }
21722
21723             this.markInvalid(this.errors.PwdEmpty);
21724             this.errorMsg = this.errors.PwdEmpty;
21725             return false;
21726         }
21727         
21728         if(this.insecure){
21729             return true;
21730         }
21731         
21732         if ('[\x21-\x7e]*'.match(value)) {
21733             this.markInvalid(this.errors.PwdBadChar);
21734             this.errorMsg = this.errors.PwdBadChar;
21735             return false;
21736         }
21737         if (value.length < 6) {
21738             this.markInvalid(this.errors.PwdShort);
21739             this.errorMsg = this.errors.PwdShort;
21740             return false;
21741         }
21742         if (value.length > 16) {
21743             this.markInvalid(this.errors.PwdLong);
21744             this.errorMsg = this.errors.PwdLong;
21745             return false;
21746         }
21747         var strength;
21748         if (this.ClientSideStrongPassword(value)) {
21749             strength = 3;
21750         } else if (this.ClientSideMediumPassword(value)) {
21751             strength = 2;
21752         } else if (this.ClientSideWeakPassword(value)) {
21753             strength = 1;
21754         } else {
21755             strength = 0;
21756         }
21757
21758         
21759         if (strength < 2) {
21760             //this.markInvalid(this.errors.TooWeak);
21761             this.errorMsg = this.errors.TooWeak;
21762             //return false;
21763         }
21764         
21765         
21766         console.log('strength2: ' + strength);
21767         
21768         //var pm = this.trigger.child('div/div/div').dom;
21769         
21770         var pm = this.trigger.child('div/div');
21771         pm.removeClass(this.meterClass);
21772         pm.addClass(this.meterClass[strength]);
21773                 
21774         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21775                 
21776         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21777         
21778         this.errorMsg = ''; 
21779         return true;
21780     },
21781     // private
21782     CharacterSetChecks: function (type)
21783     {
21784         this.type = type;
21785         this.fResult = false;
21786     },
21787     // private
21788     isctype: function (character, type)
21789     {
21790         switch (type) {  
21791             case this.kCapitalLetter:
21792                 if (character >= 'A' && character <= 'Z') {
21793                     return true;
21794                 }
21795                 break;
21796             
21797             case this.kSmallLetter:
21798                 if (character >= 'a' && character <= 'z') {
21799                     return true;
21800                 }
21801                 break;
21802             
21803             case this.kDigit:
21804                 if (character >= '0' && character <= '9') {
21805                     return true;
21806                 }
21807                 break;
21808             
21809             case this.kPunctuation:
21810                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21811                     return true;
21812                 }
21813                 break;
21814             
21815             default:
21816                 return false;
21817         }
21818
21819     },
21820     // private
21821     IsLongEnough: function (pwd, size)
21822     {
21823         return !(pwd == null || isNaN(size) || pwd.length < size);
21824     },
21825     // private
21826     SpansEnoughCharacterSets: function (word, nb)
21827     {
21828         if (!this.IsLongEnough(word, nb))
21829         {
21830             return false;
21831         }
21832
21833         var characterSetChecks = new Array(
21834             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21835             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21836         );
21837         
21838         for (var index = 0; index < word.length; ++index) {
21839             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21840                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21841                     characterSetChecks[nCharSet].fResult = true;
21842                     break;
21843                 }
21844             }
21845         }
21846
21847         var nCharSets = 0;
21848         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21849             if (characterSetChecks[nCharSet].fResult) {
21850                 ++nCharSets;
21851             }
21852         }
21853
21854         if (nCharSets < nb) {
21855             return false;
21856         }
21857         return true;
21858     },
21859     // private
21860     ClientSideStrongPassword: function (pwd)
21861     {
21862         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21863     },
21864     // private
21865     ClientSideMediumPassword: function (pwd)
21866     {
21867         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21868     },
21869     // private
21870     ClientSideWeakPassword: function (pwd)
21871     {
21872         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21873     }
21874           
21875 })//<script type="text/javascript">
21876
21877 /*
21878  * Based  Ext JS Library 1.1.1
21879  * Copyright(c) 2006-2007, Ext JS, LLC.
21880  * LGPL
21881  *
21882  */
21883  
21884 /**
21885  * @class Roo.HtmlEditorCore
21886  * @extends Roo.Component
21887  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21888  *
21889  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21890  */
21891
21892 Roo.HtmlEditorCore = function(config){
21893     
21894     
21895     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21896     
21897     
21898     this.addEvents({
21899         /**
21900          * @event initialize
21901          * Fires when the editor is fully initialized (including the iframe)
21902          * @param {Roo.HtmlEditorCore} this
21903          */
21904         initialize: true,
21905         /**
21906          * @event activate
21907          * Fires when the editor is first receives the focus. Any insertion must wait
21908          * until after this event.
21909          * @param {Roo.HtmlEditorCore} this
21910          */
21911         activate: true,
21912          /**
21913          * @event beforesync
21914          * Fires before the textarea is updated with content from the editor iframe. Return false
21915          * to cancel the sync.
21916          * @param {Roo.HtmlEditorCore} this
21917          * @param {String} html
21918          */
21919         beforesync: true,
21920          /**
21921          * @event beforepush
21922          * Fires before the iframe editor is updated with content from the textarea. Return false
21923          * to cancel the push.
21924          * @param {Roo.HtmlEditorCore} this
21925          * @param {String} html
21926          */
21927         beforepush: true,
21928          /**
21929          * @event sync
21930          * Fires when the textarea is updated with content from the editor iframe.
21931          * @param {Roo.HtmlEditorCore} this
21932          * @param {String} html
21933          */
21934         sync: true,
21935          /**
21936          * @event push
21937          * Fires when the iframe editor is updated with content from the textarea.
21938          * @param {Roo.HtmlEditorCore} this
21939          * @param {String} html
21940          */
21941         push: true,
21942         
21943         /**
21944          * @event editorevent
21945          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21946          * @param {Roo.HtmlEditorCore} this
21947          */
21948         editorevent: true
21949         
21950     });
21951     
21952     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21953     
21954     // defaults : white / black...
21955     this.applyBlacklists();
21956     
21957     
21958     
21959 };
21960
21961
21962 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21963
21964
21965      /**
21966      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21967      */
21968     
21969     owner : false,
21970     
21971      /**
21972      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21973      *                        Roo.resizable.
21974      */
21975     resizable : false,
21976      /**
21977      * @cfg {Number} height (in pixels)
21978      */   
21979     height: 300,
21980    /**
21981      * @cfg {Number} width (in pixels)
21982      */   
21983     width: 500,
21984     
21985     /**
21986      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21987      * 
21988      */
21989     stylesheets: false,
21990     
21991     // id of frame..
21992     frameId: false,
21993     
21994     // private properties
21995     validationEvent : false,
21996     deferHeight: true,
21997     initialized : false,
21998     activated : false,
21999     sourceEditMode : false,
22000     onFocus : Roo.emptyFn,
22001     iframePad:3,
22002     hideMode:'offsets',
22003     
22004     clearUp: true,
22005     
22006     // blacklist + whitelisted elements..
22007     black: false,
22008     white: false,
22009      
22010     bodyCls : '',
22011
22012     /**
22013      * Protected method that will not generally be called directly. It
22014      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22015      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22016      */
22017     getDocMarkup : function(){
22018         // body styles..
22019         var st = '';
22020         
22021         // inherit styels from page...?? 
22022         if (this.stylesheets === false) {
22023             
22024             Roo.get(document.head).select('style').each(function(node) {
22025                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22026             });
22027             
22028             Roo.get(document.head).select('link').each(function(node) { 
22029                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22030             });
22031             
22032         } else if (!this.stylesheets.length) {
22033                 // simple..
22034                 st = '<style type="text/css">' +
22035                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22036                    '</style>';
22037         } else { 
22038             st = '<style type="text/css">' +
22039                     this.stylesheets +
22040                 '</style>';
22041         }
22042         
22043         st +=  '<style type="text/css">' +
22044             'IMG { cursor: pointer } ' +
22045         '</style>';
22046
22047         var cls = 'roo-htmleditor-body';
22048         
22049         if(this.bodyCls.length){
22050             cls += ' ' + this.bodyCls;
22051         }
22052         
22053         return '<html><head>' + st  +
22054             //<style type="text/css">' +
22055             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22056             //'</style>' +
22057             ' </head><body class="' +  cls + '"></body></html>';
22058     },
22059
22060     // private
22061     onRender : function(ct, position)
22062     {
22063         var _t = this;
22064         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22065         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22066         
22067         
22068         this.el.dom.style.border = '0 none';
22069         this.el.dom.setAttribute('tabIndex', -1);
22070         this.el.addClass('x-hidden hide');
22071         
22072         
22073         
22074         if(Roo.isIE){ // fix IE 1px bogus margin
22075             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22076         }
22077        
22078         
22079         this.frameId = Roo.id();
22080         
22081          
22082         
22083         var iframe = this.owner.wrap.createChild({
22084             tag: 'iframe',
22085             cls: 'form-control', // bootstrap..
22086             id: this.frameId,
22087             name: this.frameId,
22088             frameBorder : 'no',
22089             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22090         }, this.el
22091         );
22092         
22093         
22094         this.iframe = iframe.dom;
22095
22096          this.assignDocWin();
22097         
22098         this.doc.designMode = 'on';
22099        
22100         this.doc.open();
22101         this.doc.write(this.getDocMarkup());
22102         this.doc.close();
22103
22104         
22105         var task = { // must defer to wait for browser to be ready
22106             run : function(){
22107                 //console.log("run task?" + this.doc.readyState);
22108                 this.assignDocWin();
22109                 if(this.doc.body || this.doc.readyState == 'complete'){
22110                     try {
22111                         this.doc.designMode="on";
22112                     } catch (e) {
22113                         return;
22114                     }
22115                     Roo.TaskMgr.stop(task);
22116                     this.initEditor.defer(10, this);
22117                 }
22118             },
22119             interval : 10,
22120             duration: 10000,
22121             scope: this
22122         };
22123         Roo.TaskMgr.start(task);
22124
22125     },
22126
22127     // private
22128     onResize : function(w, h)
22129     {
22130          Roo.log('resize: ' +w + ',' + h );
22131         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22132         if(!this.iframe){
22133             return;
22134         }
22135         if(typeof w == 'number'){
22136             
22137             this.iframe.style.width = w + 'px';
22138         }
22139         if(typeof h == 'number'){
22140             
22141             this.iframe.style.height = h + 'px';
22142             if(this.doc){
22143                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22144             }
22145         }
22146         
22147     },
22148
22149     /**
22150      * Toggles the editor between standard and source edit mode.
22151      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22152      */
22153     toggleSourceEdit : function(sourceEditMode){
22154         
22155         this.sourceEditMode = sourceEditMode === true;
22156         
22157         if(this.sourceEditMode){
22158  
22159             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22160             
22161         }else{
22162             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22163             //this.iframe.className = '';
22164             this.deferFocus();
22165         }
22166         //this.setSize(this.owner.wrap.getSize());
22167         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22168     },
22169
22170     
22171   
22172
22173     /**
22174      * Protected method that will not generally be called directly. If you need/want
22175      * custom HTML cleanup, this is the method you should override.
22176      * @param {String} html The HTML to be cleaned
22177      * return {String} The cleaned HTML
22178      */
22179     cleanHtml : function(html){
22180         html = String(html);
22181         if(html.length > 5){
22182             if(Roo.isSafari){ // strip safari nonsense
22183                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22184             }
22185         }
22186         if(html == '&nbsp;'){
22187             html = '';
22188         }
22189         return html;
22190     },
22191
22192     /**
22193      * HTML Editor -> Textarea
22194      * Protected method that will not generally be called directly. Syncs the contents
22195      * of the editor iframe with the textarea.
22196      */
22197     syncValue : function(){
22198         if(this.initialized){
22199             var bd = (this.doc.body || this.doc.documentElement);
22200             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22201             var html = bd.innerHTML;
22202             if(Roo.isSafari){
22203                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22204                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22205                 if(m && m[1]){
22206                     html = '<div style="'+m[0]+'">' + html + '</div>';
22207                 }
22208             }
22209             html = this.cleanHtml(html);
22210             // fix up the special chars.. normaly like back quotes in word...
22211             // however we do not want to do this with chinese..
22212             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22213                 var cc = b.charCodeAt();
22214                 if (
22215                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22216                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22217                     (cc >= 0xf900 && cc < 0xfb00 )
22218                 ) {
22219                         return b;
22220                 }
22221                 return "&#"+cc+";" 
22222             });
22223             if(this.owner.fireEvent('beforesync', this, html) !== false){
22224                 this.el.dom.value = html;
22225                 this.owner.fireEvent('sync', this, html);
22226             }
22227         }
22228     },
22229
22230     /**
22231      * Protected method that will not generally be called directly. Pushes the value of the textarea
22232      * into the iframe editor.
22233      */
22234     pushValue : function(){
22235         if(this.initialized){
22236             var v = this.el.dom.value.trim();
22237             
22238 //            if(v.length < 1){
22239 //                v = '&#160;';
22240 //            }
22241             
22242             if(this.owner.fireEvent('beforepush', this, v) !== false){
22243                 var d = (this.doc.body || this.doc.documentElement);
22244                 d.innerHTML = v;
22245                 this.cleanUpPaste();
22246                 this.el.dom.value = d.innerHTML;
22247                 this.owner.fireEvent('push', this, v);
22248             }
22249         }
22250     },
22251
22252     // private
22253     deferFocus : function(){
22254         this.focus.defer(10, this);
22255     },
22256
22257     // doc'ed in Field
22258     focus : function(){
22259         if(this.win && !this.sourceEditMode){
22260             this.win.focus();
22261         }else{
22262             this.el.focus();
22263         }
22264     },
22265     
22266     assignDocWin: function()
22267     {
22268         var iframe = this.iframe;
22269         
22270          if(Roo.isIE){
22271             this.doc = iframe.contentWindow.document;
22272             this.win = iframe.contentWindow;
22273         } else {
22274 //            if (!Roo.get(this.frameId)) {
22275 //                return;
22276 //            }
22277 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22278 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22279             
22280             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22281                 return;
22282             }
22283             
22284             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22285             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22286         }
22287     },
22288     
22289     // private
22290     initEditor : function(){
22291         //console.log("INIT EDITOR");
22292         this.assignDocWin();
22293         
22294         
22295         
22296         this.doc.designMode="on";
22297         this.doc.open();
22298         this.doc.write(this.getDocMarkup());
22299         this.doc.close();
22300         
22301         var dbody = (this.doc.body || this.doc.documentElement);
22302         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22303         // this copies styles from the containing element into thsi one..
22304         // not sure why we need all of this..
22305         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22306         
22307         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22308         //ss['background-attachment'] = 'fixed'; // w3c
22309         dbody.bgProperties = 'fixed'; // ie
22310         //Roo.DomHelper.applyStyles(dbody, ss);
22311         Roo.EventManager.on(this.doc, {
22312             //'mousedown': this.onEditorEvent,
22313             'mouseup': this.onEditorEvent,
22314             'dblclick': this.onEditorEvent,
22315             'click': this.onEditorEvent,
22316             'keyup': this.onEditorEvent,
22317             buffer:100,
22318             scope: this
22319         });
22320         if(Roo.isGecko){
22321             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22322         }
22323         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22324             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22325         }
22326         this.initialized = true;
22327
22328         this.owner.fireEvent('initialize', this);
22329         this.pushValue();
22330     },
22331
22332     // private
22333     onDestroy : function(){
22334         
22335         
22336         
22337         if(this.rendered){
22338             
22339             //for (var i =0; i < this.toolbars.length;i++) {
22340             //    // fixme - ask toolbars for heights?
22341             //    this.toolbars[i].onDestroy();
22342            // }
22343             
22344             //this.wrap.dom.innerHTML = '';
22345             //this.wrap.remove();
22346         }
22347     },
22348
22349     // private
22350     onFirstFocus : function(){
22351         
22352         this.assignDocWin();
22353         
22354         
22355         this.activated = true;
22356          
22357     
22358         if(Roo.isGecko){ // prevent silly gecko errors
22359             this.win.focus();
22360             var s = this.win.getSelection();
22361             if(!s.focusNode || s.focusNode.nodeType != 3){
22362                 var r = s.getRangeAt(0);
22363                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22364                 r.collapse(true);
22365                 this.deferFocus();
22366             }
22367             try{
22368                 this.execCmd('useCSS', true);
22369                 this.execCmd('styleWithCSS', false);
22370             }catch(e){}
22371         }
22372         this.owner.fireEvent('activate', this);
22373     },
22374
22375     // private
22376     adjustFont: function(btn){
22377         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22378         //if(Roo.isSafari){ // safari
22379         //    adjust *= 2;
22380        // }
22381         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22382         if(Roo.isSafari){ // safari
22383             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22384             v =  (v < 10) ? 10 : v;
22385             v =  (v > 48) ? 48 : v;
22386             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22387             
22388         }
22389         
22390         
22391         v = Math.max(1, v+adjust);
22392         
22393         this.execCmd('FontSize', v  );
22394     },
22395
22396     onEditorEvent : function(e)
22397     {
22398         this.owner.fireEvent('editorevent', this, e);
22399       //  this.updateToolbar();
22400         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22401     },
22402
22403     insertTag : function(tg)
22404     {
22405         // could be a bit smarter... -> wrap the current selected tRoo..
22406         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22407             
22408             range = this.createRange(this.getSelection());
22409             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22410             wrappingNode.appendChild(range.extractContents());
22411             range.insertNode(wrappingNode);
22412
22413             return;
22414             
22415             
22416             
22417         }
22418         this.execCmd("formatblock",   tg);
22419         
22420     },
22421     
22422     insertText : function(txt)
22423     {
22424         
22425         
22426         var range = this.createRange();
22427         range.deleteContents();
22428                //alert(Sender.getAttribute('label'));
22429                
22430         range.insertNode(this.doc.createTextNode(txt));
22431     } ,
22432     
22433      
22434
22435     /**
22436      * Executes a Midas editor command on the editor document and performs necessary focus and
22437      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22438      * @param {String} cmd The Midas command
22439      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22440      */
22441     relayCmd : function(cmd, value){
22442         this.win.focus();
22443         this.execCmd(cmd, value);
22444         this.owner.fireEvent('editorevent', this);
22445         //this.updateToolbar();
22446         this.owner.deferFocus();
22447     },
22448
22449     /**
22450      * Executes a Midas editor command directly on the editor document.
22451      * For visual commands, you should use {@link #relayCmd} instead.
22452      * <b>This should only be called after the editor is initialized.</b>
22453      * @param {String} cmd The Midas command
22454      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22455      */
22456     execCmd : function(cmd, value){
22457         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22458         this.syncValue();
22459     },
22460  
22461  
22462    
22463     /**
22464      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22465      * to insert tRoo.
22466      * @param {String} text | dom node.. 
22467      */
22468     insertAtCursor : function(text)
22469     {
22470         
22471         if(!this.activated){
22472             return;
22473         }
22474         /*
22475         if(Roo.isIE){
22476             this.win.focus();
22477             var r = this.doc.selection.createRange();
22478             if(r){
22479                 r.collapse(true);
22480                 r.pasteHTML(text);
22481                 this.syncValue();
22482                 this.deferFocus();
22483             
22484             }
22485             return;
22486         }
22487         */
22488         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22489             this.win.focus();
22490             
22491             
22492             // from jquery ui (MIT licenced)
22493             var range, node;
22494             var win = this.win;
22495             
22496             if (win.getSelection && win.getSelection().getRangeAt) {
22497                 range = win.getSelection().getRangeAt(0);
22498                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22499                 range.insertNode(node);
22500             } else if (win.document.selection && win.document.selection.createRange) {
22501                 // no firefox support
22502                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22503                 win.document.selection.createRange().pasteHTML(txt);
22504             } else {
22505                 // no firefox support
22506                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22507                 this.execCmd('InsertHTML', txt);
22508             } 
22509             
22510             this.syncValue();
22511             
22512             this.deferFocus();
22513         }
22514     },
22515  // private
22516     mozKeyPress : function(e){
22517         if(e.ctrlKey){
22518             var c = e.getCharCode(), cmd;
22519           
22520             if(c > 0){
22521                 c = String.fromCharCode(c).toLowerCase();
22522                 switch(c){
22523                     case 'b':
22524                         cmd = 'bold';
22525                         break;
22526                     case 'i':
22527                         cmd = 'italic';
22528                         break;
22529                     
22530                     case 'u':
22531                         cmd = 'underline';
22532                         break;
22533                     
22534                     case 'v':
22535                         this.cleanUpPaste.defer(100, this);
22536                         return;
22537                         
22538                 }
22539                 if(cmd){
22540                     this.win.focus();
22541                     this.execCmd(cmd);
22542                     this.deferFocus();
22543                     e.preventDefault();
22544                 }
22545                 
22546             }
22547         }
22548     },
22549
22550     // private
22551     fixKeys : function(){ // load time branching for fastest keydown performance
22552         if(Roo.isIE){
22553             return function(e){
22554                 var k = e.getKey(), r;
22555                 if(k == e.TAB){
22556                     e.stopEvent();
22557                     r = this.doc.selection.createRange();
22558                     if(r){
22559                         r.collapse(true);
22560                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22561                         this.deferFocus();
22562                     }
22563                     return;
22564                 }
22565                 
22566                 if(k == e.ENTER){
22567                     r = this.doc.selection.createRange();
22568                     if(r){
22569                         var target = r.parentElement();
22570                         if(!target || target.tagName.toLowerCase() != 'li'){
22571                             e.stopEvent();
22572                             r.pasteHTML('<br />');
22573                             r.collapse(false);
22574                             r.select();
22575                         }
22576                     }
22577                 }
22578                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22579                     this.cleanUpPaste.defer(100, this);
22580                     return;
22581                 }
22582                 
22583                 
22584             };
22585         }else if(Roo.isOpera){
22586             return function(e){
22587                 var k = e.getKey();
22588                 if(k == e.TAB){
22589                     e.stopEvent();
22590                     this.win.focus();
22591                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22592                     this.deferFocus();
22593                 }
22594                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22595                     this.cleanUpPaste.defer(100, this);
22596                     return;
22597                 }
22598                 
22599             };
22600         }else if(Roo.isSafari){
22601             return function(e){
22602                 var k = e.getKey();
22603                 
22604                 if(k == e.TAB){
22605                     e.stopEvent();
22606                     this.execCmd('InsertText','\t');
22607                     this.deferFocus();
22608                     return;
22609                 }
22610                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22611                     this.cleanUpPaste.defer(100, this);
22612                     return;
22613                 }
22614                 
22615              };
22616         }
22617     }(),
22618     
22619     getAllAncestors: function()
22620     {
22621         var p = this.getSelectedNode();
22622         var a = [];
22623         if (!p) {
22624             a.push(p); // push blank onto stack..
22625             p = this.getParentElement();
22626         }
22627         
22628         
22629         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22630             a.push(p);
22631             p = p.parentNode;
22632         }
22633         a.push(this.doc.body);
22634         return a;
22635     },
22636     lastSel : false,
22637     lastSelNode : false,
22638     
22639     
22640     getSelection : function() 
22641     {
22642         this.assignDocWin();
22643         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22644     },
22645     
22646     getSelectedNode: function() 
22647     {
22648         // this may only work on Gecko!!!
22649         
22650         // should we cache this!!!!
22651         
22652         
22653         
22654          
22655         var range = this.createRange(this.getSelection()).cloneRange();
22656         
22657         if (Roo.isIE) {
22658             var parent = range.parentElement();
22659             while (true) {
22660                 var testRange = range.duplicate();
22661                 testRange.moveToElementText(parent);
22662                 if (testRange.inRange(range)) {
22663                     break;
22664                 }
22665                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22666                     break;
22667                 }
22668                 parent = parent.parentElement;
22669             }
22670             return parent;
22671         }
22672         
22673         // is ancestor a text element.
22674         var ac =  range.commonAncestorContainer;
22675         if (ac.nodeType == 3) {
22676             ac = ac.parentNode;
22677         }
22678         
22679         var ar = ac.childNodes;
22680          
22681         var nodes = [];
22682         var other_nodes = [];
22683         var has_other_nodes = false;
22684         for (var i=0;i<ar.length;i++) {
22685             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22686                 continue;
22687             }
22688             // fullly contained node.
22689             
22690             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22691                 nodes.push(ar[i]);
22692                 continue;
22693             }
22694             
22695             // probably selected..
22696             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22697                 other_nodes.push(ar[i]);
22698                 continue;
22699             }
22700             // outer..
22701             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22702                 continue;
22703             }
22704             
22705             
22706             has_other_nodes = true;
22707         }
22708         if (!nodes.length && other_nodes.length) {
22709             nodes= other_nodes;
22710         }
22711         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22712             return false;
22713         }
22714         
22715         return nodes[0];
22716     },
22717     createRange: function(sel)
22718     {
22719         // this has strange effects when using with 
22720         // top toolbar - not sure if it's a great idea.
22721         //this.editor.contentWindow.focus();
22722         if (typeof sel != "undefined") {
22723             try {
22724                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22725             } catch(e) {
22726                 return this.doc.createRange();
22727             }
22728         } else {
22729             return this.doc.createRange();
22730         }
22731     },
22732     getParentElement: function()
22733     {
22734         
22735         this.assignDocWin();
22736         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22737         
22738         var range = this.createRange(sel);
22739          
22740         try {
22741             var p = range.commonAncestorContainer;
22742             while (p.nodeType == 3) { // text node
22743                 p = p.parentNode;
22744             }
22745             return p;
22746         } catch (e) {
22747             return null;
22748         }
22749     
22750     },
22751     /***
22752      *
22753      * Range intersection.. the hard stuff...
22754      *  '-1' = before
22755      *  '0' = hits..
22756      *  '1' = after.
22757      *         [ -- selected range --- ]
22758      *   [fail]                        [fail]
22759      *
22760      *    basically..
22761      *      if end is before start or  hits it. fail.
22762      *      if start is after end or hits it fail.
22763      *
22764      *   if either hits (but other is outside. - then it's not 
22765      *   
22766      *    
22767      **/
22768     
22769     
22770     // @see http://www.thismuchiknow.co.uk/?p=64.
22771     rangeIntersectsNode : function(range, node)
22772     {
22773         var nodeRange = node.ownerDocument.createRange();
22774         try {
22775             nodeRange.selectNode(node);
22776         } catch (e) {
22777             nodeRange.selectNodeContents(node);
22778         }
22779     
22780         var rangeStartRange = range.cloneRange();
22781         rangeStartRange.collapse(true);
22782     
22783         var rangeEndRange = range.cloneRange();
22784         rangeEndRange.collapse(false);
22785     
22786         var nodeStartRange = nodeRange.cloneRange();
22787         nodeStartRange.collapse(true);
22788     
22789         var nodeEndRange = nodeRange.cloneRange();
22790         nodeEndRange.collapse(false);
22791     
22792         return rangeStartRange.compareBoundaryPoints(
22793                  Range.START_TO_START, nodeEndRange) == -1 &&
22794                rangeEndRange.compareBoundaryPoints(
22795                  Range.START_TO_START, nodeStartRange) == 1;
22796         
22797          
22798     },
22799     rangeCompareNode : function(range, node)
22800     {
22801         var nodeRange = node.ownerDocument.createRange();
22802         try {
22803             nodeRange.selectNode(node);
22804         } catch (e) {
22805             nodeRange.selectNodeContents(node);
22806         }
22807         
22808         
22809         range.collapse(true);
22810     
22811         nodeRange.collapse(true);
22812      
22813         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22814         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22815          
22816         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22817         
22818         var nodeIsBefore   =  ss == 1;
22819         var nodeIsAfter    = ee == -1;
22820         
22821         if (nodeIsBefore && nodeIsAfter) {
22822             return 0; // outer
22823         }
22824         if (!nodeIsBefore && nodeIsAfter) {
22825             return 1; //right trailed.
22826         }
22827         
22828         if (nodeIsBefore && !nodeIsAfter) {
22829             return 2;  // left trailed.
22830         }
22831         // fully contined.
22832         return 3;
22833     },
22834
22835     // private? - in a new class?
22836     cleanUpPaste :  function()
22837     {
22838         // cleans up the whole document..
22839         Roo.log('cleanuppaste');
22840         
22841         this.cleanUpChildren(this.doc.body);
22842         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22843         if (clean != this.doc.body.innerHTML) {
22844             this.doc.body.innerHTML = clean;
22845         }
22846         
22847     },
22848     
22849     cleanWordChars : function(input) {// change the chars to hex code
22850         var he = Roo.HtmlEditorCore;
22851         
22852         var output = input;
22853         Roo.each(he.swapCodes, function(sw) { 
22854             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22855             
22856             output = output.replace(swapper, sw[1]);
22857         });
22858         
22859         return output;
22860     },
22861     
22862     
22863     cleanUpChildren : function (n)
22864     {
22865         if (!n.childNodes.length) {
22866             return;
22867         }
22868         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22869            this.cleanUpChild(n.childNodes[i]);
22870         }
22871     },
22872     
22873     
22874         
22875     
22876     cleanUpChild : function (node)
22877     {
22878         var ed = this;
22879         //console.log(node);
22880         if (node.nodeName == "#text") {
22881             // clean up silly Windows -- stuff?
22882             return; 
22883         }
22884         if (node.nodeName == "#comment") {
22885             node.parentNode.removeChild(node);
22886             // clean up silly Windows -- stuff?
22887             return; 
22888         }
22889         var lcname = node.tagName.toLowerCase();
22890         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22891         // whitelist of tags..
22892         
22893         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22894             // remove node.
22895             node.parentNode.removeChild(node);
22896             return;
22897             
22898         }
22899         
22900         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22901         
22902         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22903         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22904         
22905         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22906         //    remove_keep_children = true;
22907         //}
22908         
22909         if (remove_keep_children) {
22910             this.cleanUpChildren(node);
22911             // inserts everything just before this node...
22912             while (node.childNodes.length) {
22913                 var cn = node.childNodes[0];
22914                 node.removeChild(cn);
22915                 node.parentNode.insertBefore(cn, node);
22916             }
22917             node.parentNode.removeChild(node);
22918             return;
22919         }
22920         
22921         if (!node.attributes || !node.attributes.length) {
22922             this.cleanUpChildren(node);
22923             return;
22924         }
22925         
22926         function cleanAttr(n,v)
22927         {
22928             
22929             if (v.match(/^\./) || v.match(/^\//)) {
22930                 return;
22931             }
22932             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22933                 return;
22934             }
22935             if (v.match(/^#/)) {
22936                 return;
22937             }
22938 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22939             node.removeAttribute(n);
22940             
22941         }
22942         
22943         var cwhite = this.cwhite;
22944         var cblack = this.cblack;
22945             
22946         function cleanStyle(n,v)
22947         {
22948             if (v.match(/expression/)) { //XSS?? should we even bother..
22949                 node.removeAttribute(n);
22950                 return;
22951             }
22952             
22953             var parts = v.split(/;/);
22954             var clean = [];
22955             
22956             Roo.each(parts, function(p) {
22957                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22958                 if (!p.length) {
22959                     return true;
22960                 }
22961                 var l = p.split(':').shift().replace(/\s+/g,'');
22962                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22963                 
22964                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22965 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22966                     //node.removeAttribute(n);
22967                     return true;
22968                 }
22969                 //Roo.log()
22970                 // only allow 'c whitelisted system attributes'
22971                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22972 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22973                     //node.removeAttribute(n);
22974                     return true;
22975                 }
22976                 
22977                 
22978                  
22979                 
22980                 clean.push(p);
22981                 return true;
22982             });
22983             if (clean.length) { 
22984                 node.setAttribute(n, clean.join(';'));
22985             } else {
22986                 node.removeAttribute(n);
22987             }
22988             
22989         }
22990         
22991         
22992         for (var i = node.attributes.length-1; i > -1 ; i--) {
22993             var a = node.attributes[i];
22994             //console.log(a);
22995             
22996             if (a.name.toLowerCase().substr(0,2)=='on')  {
22997                 node.removeAttribute(a.name);
22998                 continue;
22999             }
23000             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23001                 node.removeAttribute(a.name);
23002                 continue;
23003             }
23004             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23005                 cleanAttr(a.name,a.value); // fixme..
23006                 continue;
23007             }
23008             if (a.name == 'style') {
23009                 cleanStyle(a.name,a.value);
23010                 continue;
23011             }
23012             /// clean up MS crap..
23013             // tecnically this should be a list of valid class'es..
23014             
23015             
23016             if (a.name == 'class') {
23017                 if (a.value.match(/^Mso/)) {
23018                     node.className = '';
23019                 }
23020                 
23021                 if (a.value.match(/^body$/)) {
23022                     node.className = '';
23023                 }
23024                 continue;
23025             }
23026             
23027             // style cleanup!?
23028             // class cleanup?
23029             
23030         }
23031         
23032         
23033         this.cleanUpChildren(node);
23034         
23035         
23036     },
23037     
23038     /**
23039      * Clean up MS wordisms...
23040      */
23041     cleanWord : function(node)
23042     {
23043         
23044         
23045         if (!node) {
23046             this.cleanWord(this.doc.body);
23047             return;
23048         }
23049         if (node.nodeName == "#text") {
23050             // clean up silly Windows -- stuff?
23051             return; 
23052         }
23053         if (node.nodeName == "#comment") {
23054             node.parentNode.removeChild(node);
23055             // clean up silly Windows -- stuff?
23056             return; 
23057         }
23058         
23059         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23060             node.parentNode.removeChild(node);
23061             return;
23062         }
23063         
23064         // remove - but keep children..
23065         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
23066             while (node.childNodes.length) {
23067                 var cn = node.childNodes[0];
23068                 node.removeChild(cn);
23069                 node.parentNode.insertBefore(cn, node);
23070             }
23071             node.parentNode.removeChild(node);
23072             this.iterateChildren(node, this.cleanWord);
23073             return;
23074         }
23075         // clean styles
23076         if (node.className.length) {
23077             
23078             var cn = node.className.split(/\W+/);
23079             var cna = [];
23080             Roo.each(cn, function(cls) {
23081                 if (cls.match(/Mso[a-zA-Z]+/)) {
23082                     return;
23083                 }
23084                 cna.push(cls);
23085             });
23086             node.className = cna.length ? cna.join(' ') : '';
23087             if (!cna.length) {
23088                 node.removeAttribute("class");
23089             }
23090         }
23091         
23092         if (node.hasAttribute("lang")) {
23093             node.removeAttribute("lang");
23094         }
23095         
23096         if (node.hasAttribute("style")) {
23097             
23098             var styles = node.getAttribute("style").split(";");
23099             var nstyle = [];
23100             Roo.each(styles, function(s) {
23101                 if (!s.match(/:/)) {
23102                     return;
23103                 }
23104                 var kv = s.split(":");
23105                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23106                     return;
23107                 }
23108                 // what ever is left... we allow.
23109                 nstyle.push(s);
23110             });
23111             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23112             if (!nstyle.length) {
23113                 node.removeAttribute('style');
23114             }
23115         }
23116         this.iterateChildren(node, this.cleanWord);
23117         
23118         
23119         
23120     },
23121     /**
23122      * iterateChildren of a Node, calling fn each time, using this as the scole..
23123      * @param {DomNode} node node to iterate children of.
23124      * @param {Function} fn method of this class to call on each item.
23125      */
23126     iterateChildren : function(node, fn)
23127     {
23128         if (!node.childNodes.length) {
23129                 return;
23130         }
23131         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23132            fn.call(this, node.childNodes[i])
23133         }
23134     },
23135     
23136     
23137     /**
23138      * cleanTableWidths.
23139      *
23140      * Quite often pasting from word etc.. results in tables with column and widths.
23141      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23142      *
23143      */
23144     cleanTableWidths : function(node)
23145     {
23146          
23147          
23148         if (!node) {
23149             this.cleanTableWidths(this.doc.body);
23150             return;
23151         }
23152         
23153         // ignore list...
23154         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23155             return; 
23156         }
23157         Roo.log(node.tagName);
23158         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23159             this.iterateChildren(node, this.cleanTableWidths);
23160             return;
23161         }
23162         if (node.hasAttribute('width')) {
23163             node.removeAttribute('width');
23164         }
23165         
23166          
23167         if (node.hasAttribute("style")) {
23168             // pretty basic...
23169             
23170             var styles = node.getAttribute("style").split(";");
23171             var nstyle = [];
23172             Roo.each(styles, function(s) {
23173                 if (!s.match(/:/)) {
23174                     return;
23175                 }
23176                 var kv = s.split(":");
23177                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23178                     return;
23179                 }
23180                 // what ever is left... we allow.
23181                 nstyle.push(s);
23182             });
23183             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23184             if (!nstyle.length) {
23185                 node.removeAttribute('style');
23186             }
23187         }
23188         
23189         this.iterateChildren(node, this.cleanTableWidths);
23190         
23191         
23192     },
23193     
23194     
23195     
23196     
23197     domToHTML : function(currentElement, depth, nopadtext) {
23198         
23199         depth = depth || 0;
23200         nopadtext = nopadtext || false;
23201     
23202         if (!currentElement) {
23203             return this.domToHTML(this.doc.body);
23204         }
23205         
23206         //Roo.log(currentElement);
23207         var j;
23208         var allText = false;
23209         var nodeName = currentElement.nodeName;
23210         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23211         
23212         if  (nodeName == '#text') {
23213             
23214             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23215         }
23216         
23217         
23218         var ret = '';
23219         if (nodeName != 'BODY') {
23220              
23221             var i = 0;
23222             // Prints the node tagName, such as <A>, <IMG>, etc
23223             if (tagName) {
23224                 var attr = [];
23225                 for(i = 0; i < currentElement.attributes.length;i++) {
23226                     // quoting?
23227                     var aname = currentElement.attributes.item(i).name;
23228                     if (!currentElement.attributes.item(i).value.length) {
23229                         continue;
23230                     }
23231                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23232                 }
23233                 
23234                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23235             } 
23236             else {
23237                 
23238                 // eack
23239             }
23240         } else {
23241             tagName = false;
23242         }
23243         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23244             return ret;
23245         }
23246         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23247             nopadtext = true;
23248         }
23249         
23250         
23251         // Traverse the tree
23252         i = 0;
23253         var currentElementChild = currentElement.childNodes.item(i);
23254         var allText = true;
23255         var innerHTML  = '';
23256         lastnode = '';
23257         while (currentElementChild) {
23258             // Formatting code (indent the tree so it looks nice on the screen)
23259             var nopad = nopadtext;
23260             if (lastnode == 'SPAN') {
23261                 nopad  = true;
23262             }
23263             // text
23264             if  (currentElementChild.nodeName == '#text') {
23265                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23266                 toadd = nopadtext ? toadd : toadd.trim();
23267                 if (!nopad && toadd.length > 80) {
23268                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23269                 }
23270                 innerHTML  += toadd;
23271                 
23272                 i++;
23273                 currentElementChild = currentElement.childNodes.item(i);
23274                 lastNode = '';
23275                 continue;
23276             }
23277             allText = false;
23278             
23279             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23280                 
23281             // Recursively traverse the tree structure of the child node
23282             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23283             lastnode = currentElementChild.nodeName;
23284             i++;
23285             currentElementChild=currentElement.childNodes.item(i);
23286         }
23287         
23288         ret += innerHTML;
23289         
23290         if (!allText) {
23291                 // The remaining code is mostly for formatting the tree
23292             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23293         }
23294         
23295         
23296         if (tagName) {
23297             ret+= "</"+tagName+">";
23298         }
23299         return ret;
23300         
23301     },
23302         
23303     applyBlacklists : function()
23304     {
23305         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23306         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23307         
23308         this.white = [];
23309         this.black = [];
23310         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23311             if (b.indexOf(tag) > -1) {
23312                 return;
23313             }
23314             this.white.push(tag);
23315             
23316         }, this);
23317         
23318         Roo.each(w, function(tag) {
23319             if (b.indexOf(tag) > -1) {
23320                 return;
23321             }
23322             if (this.white.indexOf(tag) > -1) {
23323                 return;
23324             }
23325             this.white.push(tag);
23326             
23327         }, this);
23328         
23329         
23330         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23331             if (w.indexOf(tag) > -1) {
23332                 return;
23333             }
23334             this.black.push(tag);
23335             
23336         }, this);
23337         
23338         Roo.each(b, function(tag) {
23339             if (w.indexOf(tag) > -1) {
23340                 return;
23341             }
23342             if (this.black.indexOf(tag) > -1) {
23343                 return;
23344             }
23345             this.black.push(tag);
23346             
23347         }, this);
23348         
23349         
23350         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23351         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23352         
23353         this.cwhite = [];
23354         this.cblack = [];
23355         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23356             if (b.indexOf(tag) > -1) {
23357                 return;
23358             }
23359             this.cwhite.push(tag);
23360             
23361         }, this);
23362         
23363         Roo.each(w, function(tag) {
23364             if (b.indexOf(tag) > -1) {
23365                 return;
23366             }
23367             if (this.cwhite.indexOf(tag) > -1) {
23368                 return;
23369             }
23370             this.cwhite.push(tag);
23371             
23372         }, this);
23373         
23374         
23375         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23376             if (w.indexOf(tag) > -1) {
23377                 return;
23378             }
23379             this.cblack.push(tag);
23380             
23381         }, this);
23382         
23383         Roo.each(b, function(tag) {
23384             if (w.indexOf(tag) > -1) {
23385                 return;
23386             }
23387             if (this.cblack.indexOf(tag) > -1) {
23388                 return;
23389             }
23390             this.cblack.push(tag);
23391             
23392         }, this);
23393     },
23394     
23395     setStylesheets : function(stylesheets)
23396     {
23397         if(typeof(stylesheets) == 'string'){
23398             Roo.get(this.iframe.contentDocument.head).createChild({
23399                 tag : 'link',
23400                 rel : 'stylesheet',
23401                 type : 'text/css',
23402                 href : stylesheets
23403             });
23404             
23405             return;
23406         }
23407         var _this = this;
23408      
23409         Roo.each(stylesheets, function(s) {
23410             if(!s.length){
23411                 return;
23412             }
23413             
23414             Roo.get(_this.iframe.contentDocument.head).createChild({
23415                 tag : 'link',
23416                 rel : 'stylesheet',
23417                 type : 'text/css',
23418                 href : s
23419             });
23420         });
23421
23422         
23423     },
23424     
23425     removeStylesheets : function()
23426     {
23427         var _this = this;
23428         
23429         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23430             s.remove();
23431         });
23432     },
23433     
23434     setStyle : function(style)
23435     {
23436         Roo.get(this.iframe.contentDocument.head).createChild({
23437             tag : 'style',
23438             type : 'text/css',
23439             html : style
23440         });
23441
23442         return;
23443     }
23444     
23445     // hide stuff that is not compatible
23446     /**
23447      * @event blur
23448      * @hide
23449      */
23450     /**
23451      * @event change
23452      * @hide
23453      */
23454     /**
23455      * @event focus
23456      * @hide
23457      */
23458     /**
23459      * @event specialkey
23460      * @hide
23461      */
23462     /**
23463      * @cfg {String} fieldClass @hide
23464      */
23465     /**
23466      * @cfg {String} focusClass @hide
23467      */
23468     /**
23469      * @cfg {String} autoCreate @hide
23470      */
23471     /**
23472      * @cfg {String} inputType @hide
23473      */
23474     /**
23475      * @cfg {String} invalidClass @hide
23476      */
23477     /**
23478      * @cfg {String} invalidText @hide
23479      */
23480     /**
23481      * @cfg {String} msgFx @hide
23482      */
23483     /**
23484      * @cfg {String} validateOnBlur @hide
23485      */
23486 });
23487
23488 Roo.HtmlEditorCore.white = [
23489         'area', 'br', 'img', 'input', 'hr', 'wbr',
23490         
23491        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23492        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23493        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23494        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23495        'table',   'ul',         'xmp', 
23496        
23497        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23498       'thead',   'tr', 
23499      
23500       'dir', 'menu', 'ol', 'ul', 'dl',
23501        
23502       'embed',  'object'
23503 ];
23504
23505
23506 Roo.HtmlEditorCore.black = [
23507     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23508         'applet', // 
23509         'base',   'basefont', 'bgsound', 'blink',  'body', 
23510         'frame',  'frameset', 'head',    'html',   'ilayer', 
23511         'iframe', 'layer',  'link',     'meta',    'object',   
23512         'script', 'style' ,'title',  'xml' // clean later..
23513 ];
23514 Roo.HtmlEditorCore.clean = [
23515     'script', 'style', 'title', 'xml'
23516 ];
23517 Roo.HtmlEditorCore.remove = [
23518     'font'
23519 ];
23520 // attributes..
23521
23522 Roo.HtmlEditorCore.ablack = [
23523     'on'
23524 ];
23525     
23526 Roo.HtmlEditorCore.aclean = [ 
23527     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23528 ];
23529
23530 // protocols..
23531 Roo.HtmlEditorCore.pwhite= [
23532         'http',  'https',  'mailto'
23533 ];
23534
23535 // white listed style attributes.
23536 Roo.HtmlEditorCore.cwhite= [
23537       //  'text-align', /// default is to allow most things..
23538       
23539          
23540 //        'font-size'//??
23541 ];
23542
23543 // black listed style attributes.
23544 Roo.HtmlEditorCore.cblack= [
23545       //  'font-size' -- this can be set by the project 
23546 ];
23547
23548
23549 Roo.HtmlEditorCore.swapCodes   =[ 
23550     [    8211, "--" ], 
23551     [    8212, "--" ], 
23552     [    8216,  "'" ],  
23553     [    8217, "'" ],  
23554     [    8220, '"' ],  
23555     [    8221, '"' ],  
23556     [    8226, "*" ],  
23557     [    8230, "..." ]
23558 ]; 
23559
23560     /*
23561  * - LGPL
23562  *
23563  * HtmlEditor
23564  * 
23565  */
23566
23567 /**
23568  * @class Roo.bootstrap.HtmlEditor
23569  * @extends Roo.bootstrap.TextArea
23570  * Bootstrap HtmlEditor class
23571
23572  * @constructor
23573  * Create a new HtmlEditor
23574  * @param {Object} config The config object
23575  */
23576
23577 Roo.bootstrap.HtmlEditor = function(config){
23578     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23579     if (!this.toolbars) {
23580         this.toolbars = [];
23581     }
23582     
23583     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23584     this.addEvents({
23585             /**
23586              * @event initialize
23587              * Fires when the editor is fully initialized (including the iframe)
23588              * @param {HtmlEditor} this
23589              */
23590             initialize: true,
23591             /**
23592              * @event activate
23593              * Fires when the editor is first receives the focus. Any insertion must wait
23594              * until after this event.
23595              * @param {HtmlEditor} this
23596              */
23597             activate: true,
23598              /**
23599              * @event beforesync
23600              * Fires before the textarea is updated with content from the editor iframe. Return false
23601              * to cancel the sync.
23602              * @param {HtmlEditor} this
23603              * @param {String} html
23604              */
23605             beforesync: true,
23606              /**
23607              * @event beforepush
23608              * Fires before the iframe editor is updated with content from the textarea. Return false
23609              * to cancel the push.
23610              * @param {HtmlEditor} this
23611              * @param {String} html
23612              */
23613             beforepush: true,
23614              /**
23615              * @event sync
23616              * Fires when the textarea is updated with content from the editor iframe.
23617              * @param {HtmlEditor} this
23618              * @param {String} html
23619              */
23620             sync: true,
23621              /**
23622              * @event push
23623              * Fires when the iframe editor is updated with content from the textarea.
23624              * @param {HtmlEditor} this
23625              * @param {String} html
23626              */
23627             push: true,
23628              /**
23629              * @event editmodechange
23630              * Fires when the editor switches edit modes
23631              * @param {HtmlEditor} this
23632              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23633              */
23634             editmodechange: true,
23635             /**
23636              * @event editorevent
23637              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23638              * @param {HtmlEditor} this
23639              */
23640             editorevent: true,
23641             /**
23642              * @event firstfocus
23643              * Fires when on first focus - needed by toolbars..
23644              * @param {HtmlEditor} this
23645              */
23646             firstfocus: true,
23647             /**
23648              * @event autosave
23649              * Auto save the htmlEditor value as a file into Events
23650              * @param {HtmlEditor} this
23651              */
23652             autosave: true,
23653             /**
23654              * @event savedpreview
23655              * preview the saved version of htmlEditor
23656              * @param {HtmlEditor} this
23657              */
23658             savedpreview: true
23659         });
23660 };
23661
23662
23663 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23664     
23665     
23666       /**
23667      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23668      */
23669     toolbars : false,
23670     
23671      /**
23672     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23673     */
23674     btns : [],
23675    
23676      /**
23677      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23678      *                        Roo.resizable.
23679      */
23680     resizable : false,
23681      /**
23682      * @cfg {Number} height (in pixels)
23683      */   
23684     height: 300,
23685    /**
23686      * @cfg {Number} width (in pixels)
23687      */   
23688     width: false,
23689     
23690     /**
23691      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23692      * 
23693      */
23694     stylesheets: false,
23695     
23696     // id of frame..
23697     frameId: false,
23698     
23699     // private properties
23700     validationEvent : false,
23701     deferHeight: true,
23702     initialized : false,
23703     activated : false,
23704     
23705     onFocus : Roo.emptyFn,
23706     iframePad:3,
23707     hideMode:'offsets',
23708     
23709     tbContainer : false,
23710     
23711     bodyCls : '',
23712     
23713     toolbarContainer :function() {
23714         return this.wrap.select('.x-html-editor-tb',true).first();
23715     },
23716
23717     /**
23718      * Protected method that will not generally be called directly. It
23719      * is called when the editor creates its toolbar. Override this method if you need to
23720      * add custom toolbar buttons.
23721      * @param {HtmlEditor} editor
23722      */
23723     createToolbar : function(){
23724         Roo.log('renewing');
23725         Roo.log("create toolbars");
23726         
23727         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23728         this.toolbars[0].render(this.toolbarContainer());
23729         
23730         return;
23731         
23732 //        if (!editor.toolbars || !editor.toolbars.length) {
23733 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23734 //        }
23735 //        
23736 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23737 //            editor.toolbars[i] = Roo.factory(
23738 //                    typeof(editor.toolbars[i]) == 'string' ?
23739 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23740 //                Roo.bootstrap.HtmlEditor);
23741 //            editor.toolbars[i].init(editor);
23742 //        }
23743     },
23744
23745      
23746     // private
23747     onRender : function(ct, position)
23748     {
23749        // Roo.log("Call onRender: " + this.xtype);
23750         var _t = this;
23751         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23752       
23753         this.wrap = this.inputEl().wrap({
23754             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23755         });
23756         
23757         this.editorcore.onRender(ct, position);
23758          
23759         if (this.resizable) {
23760             this.resizeEl = new Roo.Resizable(this.wrap, {
23761                 pinned : true,
23762                 wrap: true,
23763                 dynamic : true,
23764                 minHeight : this.height,
23765                 height: this.height,
23766                 handles : this.resizable,
23767                 width: this.width,
23768                 listeners : {
23769                     resize : function(r, w, h) {
23770                         _t.onResize(w,h); // -something
23771                     }
23772                 }
23773             });
23774             
23775         }
23776         this.createToolbar(this);
23777        
23778         
23779         if(!this.width && this.resizable){
23780             this.setSize(this.wrap.getSize());
23781         }
23782         if (this.resizeEl) {
23783             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23784             // should trigger onReize..
23785         }
23786         
23787     },
23788
23789     // private
23790     onResize : function(w, h)
23791     {
23792         Roo.log('resize: ' +w + ',' + h );
23793         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23794         var ew = false;
23795         var eh = false;
23796         
23797         if(this.inputEl() ){
23798             if(typeof w == 'number'){
23799                 var aw = w - this.wrap.getFrameWidth('lr');
23800                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23801                 ew = aw;
23802             }
23803             if(typeof h == 'number'){
23804                  var tbh = -11;  // fixme it needs to tool bar size!
23805                 for (var i =0; i < this.toolbars.length;i++) {
23806                     // fixme - ask toolbars for heights?
23807                     tbh += this.toolbars[i].el.getHeight();
23808                     //if (this.toolbars[i].footer) {
23809                     //    tbh += this.toolbars[i].footer.el.getHeight();
23810                     //}
23811                 }
23812               
23813                 
23814                 
23815                 
23816                 
23817                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23818                 ah -= 5; // knock a few pixes off for look..
23819                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23820                 var eh = ah;
23821             }
23822         }
23823         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23824         this.editorcore.onResize(ew,eh);
23825         
23826     },
23827
23828     /**
23829      * Toggles the editor between standard and source edit mode.
23830      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23831      */
23832     toggleSourceEdit : function(sourceEditMode)
23833     {
23834         this.editorcore.toggleSourceEdit(sourceEditMode);
23835         
23836         if(this.editorcore.sourceEditMode){
23837             Roo.log('editor - showing textarea');
23838             
23839 //            Roo.log('in');
23840 //            Roo.log(this.syncValue());
23841             this.syncValue();
23842             this.inputEl().removeClass(['hide', 'x-hidden']);
23843             this.inputEl().dom.removeAttribute('tabIndex');
23844             this.inputEl().focus();
23845         }else{
23846             Roo.log('editor - hiding textarea');
23847 //            Roo.log('out')
23848 //            Roo.log(this.pushValue()); 
23849             this.pushValue();
23850             
23851             this.inputEl().addClass(['hide', 'x-hidden']);
23852             this.inputEl().dom.setAttribute('tabIndex', -1);
23853             //this.deferFocus();
23854         }
23855          
23856         if(this.resizable){
23857             this.setSize(this.wrap.getSize());
23858         }
23859         
23860         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23861     },
23862  
23863     // private (for BoxComponent)
23864     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23865
23866     // private (for BoxComponent)
23867     getResizeEl : function(){
23868         return this.wrap;
23869     },
23870
23871     // private (for BoxComponent)
23872     getPositionEl : function(){
23873         return this.wrap;
23874     },
23875
23876     // private
23877     initEvents : function(){
23878         this.originalValue = this.getValue();
23879     },
23880
23881 //    /**
23882 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23883 //     * @method
23884 //     */
23885 //    markInvalid : Roo.emptyFn,
23886 //    /**
23887 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23888 //     * @method
23889 //     */
23890 //    clearInvalid : Roo.emptyFn,
23891
23892     setValue : function(v){
23893         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23894         this.editorcore.pushValue();
23895     },
23896
23897      
23898     // private
23899     deferFocus : function(){
23900         this.focus.defer(10, this);
23901     },
23902
23903     // doc'ed in Field
23904     focus : function(){
23905         this.editorcore.focus();
23906         
23907     },
23908       
23909
23910     // private
23911     onDestroy : function(){
23912         
23913         
23914         
23915         if(this.rendered){
23916             
23917             for (var i =0; i < this.toolbars.length;i++) {
23918                 // fixme - ask toolbars for heights?
23919                 this.toolbars[i].onDestroy();
23920             }
23921             
23922             this.wrap.dom.innerHTML = '';
23923             this.wrap.remove();
23924         }
23925     },
23926
23927     // private
23928     onFirstFocus : function(){
23929         //Roo.log("onFirstFocus");
23930         this.editorcore.onFirstFocus();
23931          for (var i =0; i < this.toolbars.length;i++) {
23932             this.toolbars[i].onFirstFocus();
23933         }
23934         
23935     },
23936     
23937     // private
23938     syncValue : function()
23939     {   
23940         this.editorcore.syncValue();
23941     },
23942     
23943     pushValue : function()
23944     {   
23945         this.editorcore.pushValue();
23946     }
23947      
23948     
23949     // hide stuff that is not compatible
23950     /**
23951      * @event blur
23952      * @hide
23953      */
23954     /**
23955      * @event change
23956      * @hide
23957      */
23958     /**
23959      * @event focus
23960      * @hide
23961      */
23962     /**
23963      * @event specialkey
23964      * @hide
23965      */
23966     /**
23967      * @cfg {String} fieldClass @hide
23968      */
23969     /**
23970      * @cfg {String} focusClass @hide
23971      */
23972     /**
23973      * @cfg {String} autoCreate @hide
23974      */
23975     /**
23976      * @cfg {String} inputType @hide
23977      */
23978      
23979     /**
23980      * @cfg {String} invalidText @hide
23981      */
23982     /**
23983      * @cfg {String} msgFx @hide
23984      */
23985     /**
23986      * @cfg {String} validateOnBlur @hide
23987      */
23988 });
23989  
23990     
23991    
23992    
23993    
23994       
23995 Roo.namespace('Roo.bootstrap.htmleditor');
23996 /**
23997  * @class Roo.bootstrap.HtmlEditorToolbar1
23998  * Basic Toolbar
23999  * 
24000  * Usage:
24001  *
24002  new Roo.bootstrap.HtmlEditor({
24003     ....
24004     toolbars : [
24005         new Roo.bootstrap.HtmlEditorToolbar1({
24006             disable : { fonts: 1 , format: 1, ..., ... , ...],
24007             btns : [ .... ]
24008         })
24009     }
24010      
24011  * 
24012  * @cfg {Object} disable List of elements to disable..
24013  * @cfg {Array} btns List of additional buttons.
24014  * 
24015  * 
24016  * NEEDS Extra CSS? 
24017  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24018  */
24019  
24020 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24021 {
24022     
24023     Roo.apply(this, config);
24024     
24025     // default disabled, based on 'good practice'..
24026     this.disable = this.disable || {};
24027     Roo.applyIf(this.disable, {
24028         fontSize : true,
24029         colors : true,
24030         specialElements : true
24031     });
24032     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24033     
24034     this.editor = config.editor;
24035     this.editorcore = config.editor.editorcore;
24036     
24037     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24038     
24039     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24040     // dont call parent... till later.
24041 }
24042 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
24043      
24044     bar : true,
24045     
24046     editor : false,
24047     editorcore : false,
24048     
24049     
24050     formats : [
24051         "p" ,  
24052         "h1","h2","h3","h4","h5","h6", 
24053         "pre", "code", 
24054         "abbr", "acronym", "address", "cite", "samp", "var",
24055         'div','span'
24056     ],
24057     
24058     onRender : function(ct, position)
24059     {
24060        // Roo.log("Call onRender: " + this.xtype);
24061         
24062        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24063        Roo.log(this.el);
24064        this.el.dom.style.marginBottom = '0';
24065        var _this = this;
24066        var editorcore = this.editorcore;
24067        var editor= this.editor;
24068        
24069        var children = [];
24070        var btn = function(id,cmd , toggle, handler, html){
24071        
24072             var  event = toggle ? 'toggle' : 'click';
24073        
24074             var a = {
24075                 size : 'sm',
24076                 xtype: 'Button',
24077                 xns: Roo.bootstrap,
24078                 //glyphicon : id,
24079                 fa: id,
24080                 cmd : id || cmd,
24081                 enableToggle:toggle !== false,
24082                 html : html || '',
24083                 pressed : toggle ? false : null,
24084                 listeners : {}
24085             };
24086             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24087                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24088             };
24089             children.push(a);
24090             return a;
24091        }
24092        
24093     //    var cb_box = function...
24094         
24095         var style = {
24096                 xtype: 'Button',
24097                 size : 'sm',
24098                 xns: Roo.bootstrap,
24099                 fa : 'font',
24100                 //html : 'submit'
24101                 menu : {
24102                     xtype: 'Menu',
24103                     xns: Roo.bootstrap,
24104                     items:  []
24105                 }
24106         };
24107         Roo.each(this.formats, function(f) {
24108             style.menu.items.push({
24109                 xtype :'MenuItem',
24110                 xns: Roo.bootstrap,
24111                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24112                 tagname : f,
24113                 listeners : {
24114                     click : function()
24115                     {
24116                         editorcore.insertTag(this.tagname);
24117                         editor.focus();
24118                     }
24119                 }
24120                 
24121             });
24122         });
24123         children.push(style);   
24124         
24125         btn('bold',false,true);
24126         btn('italic',false,true);
24127         btn('align-left', 'justifyleft',true);
24128         btn('align-center', 'justifycenter',true);
24129         btn('align-right' , 'justifyright',true);
24130         btn('link', false, false, function(btn) {
24131             //Roo.log("create link?");
24132             var url = prompt(this.createLinkText, this.defaultLinkValue);
24133             if(url && url != 'http:/'+'/'){
24134                 this.editorcore.relayCmd('createlink', url);
24135             }
24136         }),
24137         btn('list','insertunorderedlist',true);
24138         btn('pencil', false,true, function(btn){
24139                 Roo.log(this);
24140                 this.toggleSourceEdit(btn.pressed);
24141         });
24142         
24143         if (this.editor.btns.length > 0) {
24144             for (var i = 0; i<this.editor.btns.length; i++) {
24145                 children.push(this.editor.btns[i]);
24146             }
24147         }
24148         
24149         /*
24150         var cog = {
24151                 xtype: 'Button',
24152                 size : 'sm',
24153                 xns: Roo.bootstrap,
24154                 glyphicon : 'cog',
24155                 //html : 'submit'
24156                 menu : {
24157                     xtype: 'Menu',
24158                     xns: Roo.bootstrap,
24159                     items:  []
24160                 }
24161         };
24162         
24163         cog.menu.items.push({
24164             xtype :'MenuItem',
24165             xns: Roo.bootstrap,
24166             html : Clean styles,
24167             tagname : f,
24168             listeners : {
24169                 click : function()
24170                 {
24171                     editorcore.insertTag(this.tagname);
24172                     editor.focus();
24173                 }
24174             }
24175             
24176         });
24177        */
24178         
24179          
24180        this.xtype = 'NavSimplebar';
24181         
24182         for(var i=0;i< children.length;i++) {
24183             
24184             this.buttons.add(this.addxtypeChild(children[i]));
24185             
24186         }
24187         
24188         editor.on('editorevent', this.updateToolbar, this);
24189     },
24190     onBtnClick : function(id)
24191     {
24192        this.editorcore.relayCmd(id);
24193        this.editorcore.focus();
24194     },
24195     
24196     /**
24197      * Protected method that will not generally be called directly. It triggers
24198      * a toolbar update by reading the markup state of the current selection in the editor.
24199      */
24200     updateToolbar: function(){
24201
24202         if(!this.editorcore.activated){
24203             this.editor.onFirstFocus(); // is this neeed?
24204             return;
24205         }
24206
24207         var btns = this.buttons; 
24208         var doc = this.editorcore.doc;
24209         btns.get('bold').setActive(doc.queryCommandState('bold'));
24210         btns.get('italic').setActive(doc.queryCommandState('italic'));
24211         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24212         
24213         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24214         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24215         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24216         
24217         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24218         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24219          /*
24220         
24221         var ans = this.editorcore.getAllAncestors();
24222         if (this.formatCombo) {
24223             
24224             
24225             var store = this.formatCombo.store;
24226             this.formatCombo.setValue("");
24227             for (var i =0; i < ans.length;i++) {
24228                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24229                     // select it..
24230                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24231                     break;
24232                 }
24233             }
24234         }
24235         
24236         
24237         
24238         // hides menus... - so this cant be on a menu...
24239         Roo.bootstrap.MenuMgr.hideAll();
24240         */
24241         Roo.bootstrap.MenuMgr.hideAll();
24242         //this.editorsyncValue();
24243     },
24244     onFirstFocus: function() {
24245         this.buttons.each(function(item){
24246            item.enable();
24247         });
24248     },
24249     toggleSourceEdit : function(sourceEditMode){
24250         
24251           
24252         if(sourceEditMode){
24253             Roo.log("disabling buttons");
24254            this.buttons.each( function(item){
24255                 if(item.cmd != 'pencil'){
24256                     item.disable();
24257                 }
24258             });
24259           
24260         }else{
24261             Roo.log("enabling buttons");
24262             if(this.editorcore.initialized){
24263                 this.buttons.each( function(item){
24264                     item.enable();
24265                 });
24266             }
24267             
24268         }
24269         Roo.log("calling toggole on editor");
24270         // tell the editor that it's been pressed..
24271         this.editor.toggleSourceEdit(sourceEditMode);
24272        
24273     }
24274 });
24275
24276
24277
24278
24279
24280 /**
24281  * @class Roo.bootstrap.Table.AbstractSelectionModel
24282  * @extends Roo.util.Observable
24283  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24284  * implemented by descendant classes.  This class should not be directly instantiated.
24285  * @constructor
24286  */
24287 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24288     this.locked = false;
24289     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24290 };
24291
24292
24293 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24294     /** @ignore Called by the grid automatically. Do not call directly. */
24295     init : function(grid){
24296         this.grid = grid;
24297         this.initEvents();
24298     },
24299
24300     /**
24301      * Locks the selections.
24302      */
24303     lock : function(){
24304         this.locked = true;
24305     },
24306
24307     /**
24308      * Unlocks the selections.
24309      */
24310     unlock : function(){
24311         this.locked = false;
24312     },
24313
24314     /**
24315      * Returns true if the selections are locked.
24316      * @return {Boolean}
24317      */
24318     isLocked : function(){
24319         return this.locked;
24320     }
24321 });
24322 /**
24323  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24324  * @class Roo.bootstrap.Table.RowSelectionModel
24325  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24326  * It supports multiple selections and keyboard selection/navigation. 
24327  * @constructor
24328  * @param {Object} config
24329  */
24330
24331 Roo.bootstrap.Table.RowSelectionModel = function(config){
24332     Roo.apply(this, config);
24333     this.selections = new Roo.util.MixedCollection(false, function(o){
24334         return o.id;
24335     });
24336
24337     this.last = false;
24338     this.lastActive = false;
24339
24340     this.addEvents({
24341         /**
24342              * @event selectionchange
24343              * Fires when the selection changes
24344              * @param {SelectionModel} this
24345              */
24346             "selectionchange" : true,
24347         /**
24348              * @event afterselectionchange
24349              * Fires after the selection changes (eg. by key press or clicking)
24350              * @param {SelectionModel} this
24351              */
24352             "afterselectionchange" : true,
24353         /**
24354              * @event beforerowselect
24355              * Fires when a row is selected being selected, return false to cancel.
24356              * @param {SelectionModel} this
24357              * @param {Number} rowIndex The selected index
24358              * @param {Boolean} keepExisting False if other selections will be cleared
24359              */
24360             "beforerowselect" : true,
24361         /**
24362              * @event rowselect
24363              * Fires when a row is selected.
24364              * @param {SelectionModel} this
24365              * @param {Number} rowIndex The selected index
24366              * @param {Roo.data.Record} r The record
24367              */
24368             "rowselect" : true,
24369         /**
24370              * @event rowdeselect
24371              * Fires when a row is deselected.
24372              * @param {SelectionModel} this
24373              * @param {Number} rowIndex The selected index
24374              */
24375         "rowdeselect" : true
24376     });
24377     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24378     this.locked = false;
24379  };
24380
24381 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24382     /**
24383      * @cfg {Boolean} singleSelect
24384      * True to allow selection of only one row at a time (defaults to false)
24385      */
24386     singleSelect : false,
24387
24388     // private
24389     initEvents : function()
24390     {
24391
24392         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24393         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24394         //}else{ // allow click to work like normal
24395          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24396         //}
24397         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24398         this.grid.on("rowclick", this.handleMouseDown, this);
24399         
24400         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24401             "up" : function(e){
24402                 if(!e.shiftKey){
24403                     this.selectPrevious(e.shiftKey);
24404                 }else if(this.last !== false && this.lastActive !== false){
24405                     var last = this.last;
24406                     this.selectRange(this.last,  this.lastActive-1);
24407                     this.grid.getView().focusRow(this.lastActive);
24408                     if(last !== false){
24409                         this.last = last;
24410                     }
24411                 }else{
24412                     this.selectFirstRow();
24413                 }
24414                 this.fireEvent("afterselectionchange", this);
24415             },
24416             "down" : function(e){
24417                 if(!e.shiftKey){
24418                     this.selectNext(e.shiftKey);
24419                 }else if(this.last !== false && this.lastActive !== false){
24420                     var last = this.last;
24421                     this.selectRange(this.last,  this.lastActive+1);
24422                     this.grid.getView().focusRow(this.lastActive);
24423                     if(last !== false){
24424                         this.last = last;
24425                     }
24426                 }else{
24427                     this.selectFirstRow();
24428                 }
24429                 this.fireEvent("afterselectionchange", this);
24430             },
24431             scope: this
24432         });
24433         this.grid.store.on('load', function(){
24434             this.selections.clear();
24435         },this);
24436         /*
24437         var view = this.grid.view;
24438         view.on("refresh", this.onRefresh, this);
24439         view.on("rowupdated", this.onRowUpdated, this);
24440         view.on("rowremoved", this.onRemove, this);
24441         */
24442     },
24443
24444     // private
24445     onRefresh : function()
24446     {
24447         var ds = this.grid.store, i, v = this.grid.view;
24448         var s = this.selections;
24449         s.each(function(r){
24450             if((i = ds.indexOfId(r.id)) != -1){
24451                 v.onRowSelect(i);
24452             }else{
24453                 s.remove(r);
24454             }
24455         });
24456     },
24457
24458     // private
24459     onRemove : function(v, index, r){
24460         this.selections.remove(r);
24461     },
24462
24463     // private
24464     onRowUpdated : function(v, index, r){
24465         if(this.isSelected(r)){
24466             v.onRowSelect(index);
24467         }
24468     },
24469
24470     /**
24471      * Select records.
24472      * @param {Array} records The records to select
24473      * @param {Boolean} keepExisting (optional) True to keep existing selections
24474      */
24475     selectRecords : function(records, keepExisting)
24476     {
24477         if(!keepExisting){
24478             this.clearSelections();
24479         }
24480             var ds = this.grid.store;
24481         for(var i = 0, len = records.length; i < len; i++){
24482             this.selectRow(ds.indexOf(records[i]), true);
24483         }
24484     },
24485
24486     /**
24487      * Gets the number of selected rows.
24488      * @return {Number}
24489      */
24490     getCount : function(){
24491         return this.selections.length;
24492     },
24493
24494     /**
24495      * Selects the first row in the grid.
24496      */
24497     selectFirstRow : function(){
24498         this.selectRow(0);
24499     },
24500
24501     /**
24502      * Select the last row.
24503      * @param {Boolean} keepExisting (optional) True to keep existing selections
24504      */
24505     selectLastRow : function(keepExisting){
24506         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24507         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24508     },
24509
24510     /**
24511      * Selects the row immediately following the last selected row.
24512      * @param {Boolean} keepExisting (optional) True to keep existing selections
24513      */
24514     selectNext : function(keepExisting)
24515     {
24516             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24517             this.selectRow(this.last+1, keepExisting);
24518             this.grid.getView().focusRow(this.last);
24519         }
24520     },
24521
24522     /**
24523      * Selects the row that precedes the last selected row.
24524      * @param {Boolean} keepExisting (optional) True to keep existing selections
24525      */
24526     selectPrevious : function(keepExisting){
24527         if(this.last){
24528             this.selectRow(this.last-1, keepExisting);
24529             this.grid.getView().focusRow(this.last);
24530         }
24531     },
24532
24533     /**
24534      * Returns the selected records
24535      * @return {Array} Array of selected records
24536      */
24537     getSelections : function(){
24538         return [].concat(this.selections.items);
24539     },
24540
24541     /**
24542      * Returns the first selected record.
24543      * @return {Record}
24544      */
24545     getSelected : function(){
24546         return this.selections.itemAt(0);
24547     },
24548
24549
24550     /**
24551      * Clears all selections.
24552      */
24553     clearSelections : function(fast)
24554     {
24555         if(this.locked) {
24556             return;
24557         }
24558         if(fast !== true){
24559                 var ds = this.grid.store;
24560             var s = this.selections;
24561             s.each(function(r){
24562                 this.deselectRow(ds.indexOfId(r.id));
24563             }, this);
24564             s.clear();
24565         }else{
24566             this.selections.clear();
24567         }
24568         this.last = false;
24569     },
24570
24571
24572     /**
24573      * Selects all rows.
24574      */
24575     selectAll : function(){
24576         if(this.locked) {
24577             return;
24578         }
24579         this.selections.clear();
24580         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24581             this.selectRow(i, true);
24582         }
24583     },
24584
24585     /**
24586      * Returns True if there is a selection.
24587      * @return {Boolean}
24588      */
24589     hasSelection : function(){
24590         return this.selections.length > 0;
24591     },
24592
24593     /**
24594      * Returns True if the specified row is selected.
24595      * @param {Number/Record} record The record or index of the record to check
24596      * @return {Boolean}
24597      */
24598     isSelected : function(index){
24599             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24600         return (r && this.selections.key(r.id) ? true : false);
24601     },
24602
24603     /**
24604      * Returns True if the specified record id is selected.
24605      * @param {String} id The id of record to check
24606      * @return {Boolean}
24607      */
24608     isIdSelected : function(id){
24609         return (this.selections.key(id) ? true : false);
24610     },
24611
24612
24613     // private
24614     handleMouseDBClick : function(e, t){
24615         
24616     },
24617     // private
24618     handleMouseDown : function(e, t)
24619     {
24620             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24621         if(this.isLocked() || rowIndex < 0 ){
24622             return;
24623         };
24624         if(e.shiftKey && this.last !== false){
24625             var last = this.last;
24626             this.selectRange(last, rowIndex, e.ctrlKey);
24627             this.last = last; // reset the last
24628             t.focus();
24629     
24630         }else{
24631             var isSelected = this.isSelected(rowIndex);
24632             //Roo.log("select row:" + rowIndex);
24633             if(isSelected){
24634                 this.deselectRow(rowIndex);
24635             } else {
24636                         this.selectRow(rowIndex, true);
24637             }
24638     
24639             /*
24640                 if(e.button !== 0 && isSelected){
24641                 alert('rowIndex 2: ' + rowIndex);
24642                     view.focusRow(rowIndex);
24643                 }else if(e.ctrlKey && isSelected){
24644                     this.deselectRow(rowIndex);
24645                 }else if(!isSelected){
24646                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24647                     view.focusRow(rowIndex);
24648                 }
24649             */
24650         }
24651         this.fireEvent("afterselectionchange", this);
24652     },
24653     // private
24654     handleDragableRowClick :  function(grid, rowIndex, e) 
24655     {
24656         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24657             this.selectRow(rowIndex, false);
24658             grid.view.focusRow(rowIndex);
24659              this.fireEvent("afterselectionchange", this);
24660         }
24661     },
24662     
24663     /**
24664      * Selects multiple rows.
24665      * @param {Array} rows Array of the indexes of the row to select
24666      * @param {Boolean} keepExisting (optional) True to keep existing selections
24667      */
24668     selectRows : function(rows, keepExisting){
24669         if(!keepExisting){
24670             this.clearSelections();
24671         }
24672         for(var i = 0, len = rows.length; i < len; i++){
24673             this.selectRow(rows[i], true);
24674         }
24675     },
24676
24677     /**
24678      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24679      * @param {Number} startRow The index of the first row in the range
24680      * @param {Number} endRow The index of the last row in the range
24681      * @param {Boolean} keepExisting (optional) True to retain existing selections
24682      */
24683     selectRange : function(startRow, endRow, keepExisting){
24684         if(this.locked) {
24685             return;
24686         }
24687         if(!keepExisting){
24688             this.clearSelections();
24689         }
24690         if(startRow <= endRow){
24691             for(var i = startRow; i <= endRow; i++){
24692                 this.selectRow(i, true);
24693             }
24694         }else{
24695             for(var i = startRow; i >= endRow; i--){
24696                 this.selectRow(i, true);
24697             }
24698         }
24699     },
24700
24701     /**
24702      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24703      * @param {Number} startRow The index of the first row in the range
24704      * @param {Number} endRow The index of the last row in the range
24705      */
24706     deselectRange : function(startRow, endRow, preventViewNotify){
24707         if(this.locked) {
24708             return;
24709         }
24710         for(var i = startRow; i <= endRow; i++){
24711             this.deselectRow(i, preventViewNotify);
24712         }
24713     },
24714
24715     /**
24716      * Selects a row.
24717      * @param {Number} row The index of the row to select
24718      * @param {Boolean} keepExisting (optional) True to keep existing selections
24719      */
24720     selectRow : function(index, keepExisting, preventViewNotify)
24721     {
24722             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24723             return;
24724         }
24725         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24726             if(!keepExisting || this.singleSelect){
24727                 this.clearSelections();
24728             }
24729             
24730             var r = this.grid.store.getAt(index);
24731             //console.log('selectRow - record id :' + r.id);
24732             
24733             this.selections.add(r);
24734             this.last = this.lastActive = index;
24735             if(!preventViewNotify){
24736                 var proxy = new Roo.Element(
24737                                 this.grid.getRowDom(index)
24738                 );
24739                 proxy.addClass('bg-info info');
24740             }
24741             this.fireEvent("rowselect", this, index, r);
24742             this.fireEvent("selectionchange", this);
24743         }
24744     },
24745
24746     /**
24747      * Deselects a row.
24748      * @param {Number} row The index of the row to deselect
24749      */
24750     deselectRow : function(index, preventViewNotify)
24751     {
24752         if(this.locked) {
24753             return;
24754         }
24755         if(this.last == index){
24756             this.last = false;
24757         }
24758         if(this.lastActive == index){
24759             this.lastActive = false;
24760         }
24761         
24762         var r = this.grid.store.getAt(index);
24763         if (!r) {
24764             return;
24765         }
24766         
24767         this.selections.remove(r);
24768         //.console.log('deselectRow - record id :' + r.id);
24769         if(!preventViewNotify){
24770         
24771             var proxy = new Roo.Element(
24772                 this.grid.getRowDom(index)
24773             );
24774             proxy.removeClass('bg-info info');
24775         }
24776         this.fireEvent("rowdeselect", this, index);
24777         this.fireEvent("selectionchange", this);
24778     },
24779
24780     // private
24781     restoreLast : function(){
24782         if(this._last){
24783             this.last = this._last;
24784         }
24785     },
24786
24787     // private
24788     acceptsNav : function(row, col, cm){
24789         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24790     },
24791
24792     // private
24793     onEditorKey : function(field, e){
24794         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24795         if(k == e.TAB){
24796             e.stopEvent();
24797             ed.completeEdit();
24798             if(e.shiftKey){
24799                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24800             }else{
24801                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24802             }
24803         }else if(k == e.ENTER && !e.ctrlKey){
24804             e.stopEvent();
24805             ed.completeEdit();
24806             if(e.shiftKey){
24807                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24808             }else{
24809                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24810             }
24811         }else if(k == e.ESC){
24812             ed.cancelEdit();
24813         }
24814         if(newCell){
24815             g.startEditing(newCell[0], newCell[1]);
24816         }
24817     }
24818 });
24819 /*
24820  * Based on:
24821  * Ext JS Library 1.1.1
24822  * Copyright(c) 2006-2007, Ext JS, LLC.
24823  *
24824  * Originally Released Under LGPL - original licence link has changed is not relivant.
24825  *
24826  * Fork - LGPL
24827  * <script type="text/javascript">
24828  */
24829  
24830 /**
24831  * @class Roo.bootstrap.PagingToolbar
24832  * @extends Roo.bootstrap.NavSimplebar
24833  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24834  * @constructor
24835  * Create a new PagingToolbar
24836  * @param {Object} config The config object
24837  * @param {Roo.data.Store} store
24838  */
24839 Roo.bootstrap.PagingToolbar = function(config)
24840 {
24841     // old args format still supported... - xtype is prefered..
24842         // created from xtype...
24843     
24844     this.ds = config.dataSource;
24845     
24846     if (config.store && !this.ds) {
24847         this.store= Roo.factory(config.store, Roo.data);
24848         this.ds = this.store;
24849         this.ds.xmodule = this.xmodule || false;
24850     }
24851     
24852     this.toolbarItems = [];
24853     if (config.items) {
24854         this.toolbarItems = config.items;
24855     }
24856     
24857     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24858     
24859     this.cursor = 0;
24860     
24861     if (this.ds) { 
24862         this.bind(this.ds);
24863     }
24864     
24865     if (Roo.bootstrap.version == 4) {
24866         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24867     } else {
24868         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24869     }
24870     
24871 };
24872
24873 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24874     /**
24875      * @cfg {Roo.data.Store} dataSource
24876      * The underlying data store providing the paged data
24877      */
24878     /**
24879      * @cfg {String/HTMLElement/Element} container
24880      * container The id or element that will contain the toolbar
24881      */
24882     /**
24883      * @cfg {Boolean} displayInfo
24884      * True to display the displayMsg (defaults to false)
24885      */
24886     /**
24887      * @cfg {Number} pageSize
24888      * The number of records to display per page (defaults to 20)
24889      */
24890     pageSize: 20,
24891     /**
24892      * @cfg {String} displayMsg
24893      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24894      */
24895     displayMsg : 'Displaying {0} - {1} of {2}',
24896     /**
24897      * @cfg {String} emptyMsg
24898      * The message to display when no records are found (defaults to "No data to display")
24899      */
24900     emptyMsg : 'No data to display',
24901     /**
24902      * Customizable piece of the default paging text (defaults to "Page")
24903      * @type String
24904      */
24905     beforePageText : "Page",
24906     /**
24907      * Customizable piece of the default paging text (defaults to "of %0")
24908      * @type String
24909      */
24910     afterPageText : "of {0}",
24911     /**
24912      * Customizable piece of the default paging text (defaults to "First Page")
24913      * @type String
24914      */
24915     firstText : "First Page",
24916     /**
24917      * Customizable piece of the default paging text (defaults to "Previous Page")
24918      * @type String
24919      */
24920     prevText : "Previous Page",
24921     /**
24922      * Customizable piece of the default paging text (defaults to "Next Page")
24923      * @type String
24924      */
24925     nextText : "Next Page",
24926     /**
24927      * Customizable piece of the default paging text (defaults to "Last Page")
24928      * @type String
24929      */
24930     lastText : "Last Page",
24931     /**
24932      * Customizable piece of the default paging text (defaults to "Refresh")
24933      * @type String
24934      */
24935     refreshText : "Refresh",
24936
24937     buttons : false,
24938     // private
24939     onRender : function(ct, position) 
24940     {
24941         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24942         this.navgroup.parentId = this.id;
24943         this.navgroup.onRender(this.el, null);
24944         // add the buttons to the navgroup
24945         
24946         if(this.displayInfo){
24947             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24948             this.displayEl = this.el.select('.x-paging-info', true).first();
24949 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24950 //            this.displayEl = navel.el.select('span',true).first();
24951         }
24952         
24953         var _this = this;
24954         
24955         if(this.buttons){
24956             Roo.each(_this.buttons, function(e){ // this might need to use render????
24957                Roo.factory(e).render(_this.el);
24958             });
24959         }
24960             
24961         Roo.each(_this.toolbarItems, function(e) {
24962             _this.navgroup.addItem(e);
24963         });
24964         
24965         
24966         this.first = this.navgroup.addItem({
24967             tooltip: this.firstText,
24968             cls: "prev btn-outline-secondary",
24969             html : ' <i class="fa fa-step-backward"></i>',
24970             disabled: true,
24971             preventDefault: true,
24972             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24973         });
24974         
24975         this.prev =  this.navgroup.addItem({
24976             tooltip: this.prevText,
24977             cls: "prev btn-outline-secondary",
24978             html : ' <i class="fa fa-backward"></i>',
24979             disabled: true,
24980             preventDefault: true,
24981             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24982         });
24983     //this.addSeparator();
24984         
24985         
24986         var field = this.navgroup.addItem( {
24987             tagtype : 'span',
24988             cls : 'x-paging-position  btn-outline-secondary',
24989              disabled: true,
24990             html : this.beforePageText  +
24991                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24992                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24993          } ); //?? escaped?
24994         
24995         this.field = field.el.select('input', true).first();
24996         this.field.on("keydown", this.onPagingKeydown, this);
24997         this.field.on("focus", function(){this.dom.select();});
24998     
24999     
25000         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
25001         //this.field.setHeight(18);
25002         //this.addSeparator();
25003         this.next = this.navgroup.addItem({
25004             tooltip: this.nextText,
25005             cls: "next btn-outline-secondary",
25006             html : ' <i class="fa fa-forward"></i>',
25007             disabled: true,
25008             preventDefault: true,
25009             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
25010         });
25011         this.last = this.navgroup.addItem({
25012             tooltip: this.lastText,
25013             html : ' <i class="fa fa-step-forward"></i>',
25014             cls: "next btn-outline-secondary",
25015             disabled: true,
25016             preventDefault: true,
25017             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
25018         });
25019     //this.addSeparator();
25020         this.loading = this.navgroup.addItem({
25021             tooltip: this.refreshText,
25022             cls: "btn-outline-secondary",
25023             html : ' <i class="fa fa-refresh"></i>',
25024             preventDefault: true,
25025             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25026         });
25027         
25028     },
25029
25030     // private
25031     updateInfo : function(){
25032         if(this.displayEl){
25033             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25034             var msg = count == 0 ?
25035                 this.emptyMsg :
25036                 String.format(
25037                     this.displayMsg,
25038                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
25039                 );
25040             this.displayEl.update(msg);
25041         }
25042     },
25043
25044     // private
25045     onLoad : function(ds, r, o)
25046     {
25047         this.cursor = o.params.start ? o.params.start : 0;
25048         
25049         var d = this.getPageData(),
25050             ap = d.activePage,
25051             ps = d.pages;
25052         
25053         
25054         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25055         this.field.dom.value = ap;
25056         this.first.setDisabled(ap == 1);
25057         this.prev.setDisabled(ap == 1);
25058         this.next.setDisabled(ap == ps);
25059         this.last.setDisabled(ap == ps);
25060         this.loading.enable();
25061         this.updateInfo();
25062     },
25063
25064     // private
25065     getPageData : function(){
25066         var total = this.ds.getTotalCount();
25067         return {
25068             total : total,
25069             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25070             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25071         };
25072     },
25073
25074     // private
25075     onLoadError : function(){
25076         this.loading.enable();
25077     },
25078
25079     // private
25080     onPagingKeydown : function(e){
25081         var k = e.getKey();
25082         var d = this.getPageData();
25083         if(k == e.RETURN){
25084             var v = this.field.dom.value, pageNum;
25085             if(!v || isNaN(pageNum = parseInt(v, 10))){
25086                 this.field.dom.value = d.activePage;
25087                 return;
25088             }
25089             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25090             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25091             e.stopEvent();
25092         }
25093         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))
25094         {
25095           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25096           this.field.dom.value = pageNum;
25097           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25098           e.stopEvent();
25099         }
25100         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25101         {
25102           var v = this.field.dom.value, pageNum; 
25103           var increment = (e.shiftKey) ? 10 : 1;
25104           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25105                 increment *= -1;
25106           }
25107           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25108             this.field.dom.value = d.activePage;
25109             return;
25110           }
25111           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25112           {
25113             this.field.dom.value = parseInt(v, 10) + increment;
25114             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25115             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25116           }
25117           e.stopEvent();
25118         }
25119     },
25120
25121     // private
25122     beforeLoad : function(){
25123         if(this.loading){
25124             this.loading.disable();
25125         }
25126     },
25127
25128     // private
25129     onClick : function(which){
25130         
25131         var ds = this.ds;
25132         if (!ds) {
25133             return;
25134         }
25135         
25136         switch(which){
25137             case "first":
25138                 ds.load({params:{start: 0, limit: this.pageSize}});
25139             break;
25140             case "prev":
25141                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25142             break;
25143             case "next":
25144                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25145             break;
25146             case "last":
25147                 var total = ds.getTotalCount();
25148                 var extra = total % this.pageSize;
25149                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25150                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25151             break;
25152             case "refresh":
25153                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25154             break;
25155         }
25156     },
25157
25158     /**
25159      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25160      * @param {Roo.data.Store} store The data store to unbind
25161      */
25162     unbind : function(ds){
25163         ds.un("beforeload", this.beforeLoad, this);
25164         ds.un("load", this.onLoad, this);
25165         ds.un("loadexception", this.onLoadError, this);
25166         ds.un("remove", this.updateInfo, this);
25167         ds.un("add", this.updateInfo, this);
25168         this.ds = undefined;
25169     },
25170
25171     /**
25172      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25173      * @param {Roo.data.Store} store The data store to bind
25174      */
25175     bind : function(ds){
25176         ds.on("beforeload", this.beforeLoad, this);
25177         ds.on("load", this.onLoad, this);
25178         ds.on("loadexception", this.onLoadError, this);
25179         ds.on("remove", this.updateInfo, this);
25180         ds.on("add", this.updateInfo, this);
25181         this.ds = ds;
25182     }
25183 });/*
25184  * - LGPL
25185  *
25186  * element
25187  * 
25188  */
25189
25190 /**
25191  * @class Roo.bootstrap.MessageBar
25192  * @extends Roo.bootstrap.Component
25193  * Bootstrap MessageBar class
25194  * @cfg {String} html contents of the MessageBar
25195  * @cfg {String} weight (info | success | warning | danger) default info
25196  * @cfg {String} beforeClass insert the bar before the given class
25197  * @cfg {Boolean} closable (true | false) default false
25198  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25199  * 
25200  * @constructor
25201  * Create a new Element
25202  * @param {Object} config The config object
25203  */
25204
25205 Roo.bootstrap.MessageBar = function(config){
25206     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25207 };
25208
25209 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25210     
25211     html: '',
25212     weight: 'info',
25213     closable: false,
25214     fixed: false,
25215     beforeClass: 'bootstrap-sticky-wrap',
25216     
25217     getAutoCreate : function(){
25218         
25219         var cfg = {
25220             tag: 'div',
25221             cls: 'alert alert-dismissable alert-' + this.weight,
25222             cn: [
25223                 {
25224                     tag: 'span',
25225                     cls: 'message',
25226                     html: this.html || ''
25227                 }
25228             ]
25229         };
25230         
25231         if(this.fixed){
25232             cfg.cls += ' alert-messages-fixed';
25233         }
25234         
25235         if(this.closable){
25236             cfg.cn.push({
25237                 tag: 'button',
25238                 cls: 'close',
25239                 html: 'x'
25240             });
25241         }
25242         
25243         return cfg;
25244     },
25245     
25246     onRender : function(ct, position)
25247     {
25248         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25249         
25250         if(!this.el){
25251             var cfg = Roo.apply({},  this.getAutoCreate());
25252             cfg.id = Roo.id();
25253             
25254             if (this.cls) {
25255                 cfg.cls += ' ' + this.cls;
25256             }
25257             if (this.style) {
25258                 cfg.style = this.style;
25259             }
25260             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25261             
25262             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25263         }
25264         
25265         this.el.select('>button.close').on('click', this.hide, this);
25266         
25267     },
25268     
25269     show : function()
25270     {
25271         if (!this.rendered) {
25272             this.render();
25273         }
25274         
25275         this.el.show();
25276         
25277         this.fireEvent('show', this);
25278         
25279     },
25280     
25281     hide : function()
25282     {
25283         if (!this.rendered) {
25284             this.render();
25285         }
25286         
25287         this.el.hide();
25288         
25289         this.fireEvent('hide', this);
25290     },
25291     
25292     update : function()
25293     {
25294 //        var e = this.el.dom.firstChild;
25295 //        
25296 //        if(this.closable){
25297 //            e = e.nextSibling;
25298 //        }
25299 //        
25300 //        e.data = this.html || '';
25301
25302         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25303     }
25304    
25305 });
25306
25307  
25308
25309      /*
25310  * - LGPL
25311  *
25312  * Graph
25313  * 
25314  */
25315
25316
25317 /**
25318  * @class Roo.bootstrap.Graph
25319  * @extends Roo.bootstrap.Component
25320  * Bootstrap Graph class
25321 > Prameters
25322  -sm {number} sm 4
25323  -md {number} md 5
25324  @cfg {String} graphtype  bar | vbar | pie
25325  @cfg {number} g_x coodinator | centre x (pie)
25326  @cfg {number} g_y coodinator | centre y (pie)
25327  @cfg {number} g_r radius (pie)
25328  @cfg {number} g_height height of the chart (respected by all elements in the set)
25329  @cfg {number} g_width width of the chart (respected by all elements in the set)
25330  @cfg {Object} title The title of the chart
25331     
25332  -{Array}  values
25333  -opts (object) options for the chart 
25334      o {
25335      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25336      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25337      o vgutter (number)
25338      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.
25339      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25340      o to
25341      o stretch (boolean)
25342      o }
25343  -opts (object) options for the pie
25344      o{
25345      o cut
25346      o startAngle (number)
25347      o endAngle (number)
25348      } 
25349  *
25350  * @constructor
25351  * Create a new Input
25352  * @param {Object} config The config object
25353  */
25354
25355 Roo.bootstrap.Graph = function(config){
25356     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25357     
25358     this.addEvents({
25359         // img events
25360         /**
25361          * @event click
25362          * The img click event for the img.
25363          * @param {Roo.EventObject} e
25364          */
25365         "click" : true
25366     });
25367 };
25368
25369 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25370     
25371     sm: 4,
25372     md: 5,
25373     graphtype: 'bar',
25374     g_height: 250,
25375     g_width: 400,
25376     g_x: 50,
25377     g_y: 50,
25378     g_r: 30,
25379     opts:{
25380         //g_colors: this.colors,
25381         g_type: 'soft',
25382         g_gutter: '20%'
25383
25384     },
25385     title : false,
25386
25387     getAutoCreate : function(){
25388         
25389         var cfg = {
25390             tag: 'div',
25391             html : null
25392         };
25393         
25394         
25395         return  cfg;
25396     },
25397
25398     onRender : function(ct,position){
25399         
25400         
25401         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25402         
25403         if (typeof(Raphael) == 'undefined') {
25404             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25405             return;
25406         }
25407         
25408         this.raphael = Raphael(this.el.dom);
25409         
25410                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25411                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25412                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25413                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25414                 /*
25415                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25416                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25417                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25418                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25419                 
25420                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25421                 r.barchart(330, 10, 300, 220, data1);
25422                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25423                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25424                 */
25425                 
25426                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25427                 // r.barchart(30, 30, 560, 250,  xdata, {
25428                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25429                 //     axis : "0 0 1 1",
25430                 //     axisxlabels :  xdata
25431                 //     //yvalues : cols,
25432                    
25433                 // });
25434 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25435 //        
25436 //        this.load(null,xdata,{
25437 //                axis : "0 0 1 1",
25438 //                axisxlabels :  xdata
25439 //                });
25440
25441     },
25442
25443     load : function(graphtype,xdata,opts)
25444     {
25445         this.raphael.clear();
25446         if(!graphtype) {
25447             graphtype = this.graphtype;
25448         }
25449         if(!opts){
25450             opts = this.opts;
25451         }
25452         var r = this.raphael,
25453             fin = function () {
25454                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25455             },
25456             fout = function () {
25457                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25458             },
25459             pfin = function() {
25460                 this.sector.stop();
25461                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25462
25463                 if (this.label) {
25464                     this.label[0].stop();
25465                     this.label[0].attr({ r: 7.5 });
25466                     this.label[1].attr({ "font-weight": 800 });
25467                 }
25468             },
25469             pfout = function() {
25470                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25471
25472                 if (this.label) {
25473                     this.label[0].animate({ r: 5 }, 500, "bounce");
25474                     this.label[1].attr({ "font-weight": 400 });
25475                 }
25476             };
25477
25478         switch(graphtype){
25479             case 'bar':
25480                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25481                 break;
25482             case 'hbar':
25483                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25484                 break;
25485             case 'pie':
25486 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25487 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25488 //            
25489                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25490                 
25491                 break;
25492
25493         }
25494         
25495         if(this.title){
25496             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25497         }
25498         
25499     },
25500     
25501     setTitle: function(o)
25502     {
25503         this.title = o;
25504     },
25505     
25506     initEvents: function() {
25507         
25508         if(!this.href){
25509             this.el.on('click', this.onClick, this);
25510         }
25511     },
25512     
25513     onClick : function(e)
25514     {
25515         Roo.log('img onclick');
25516         this.fireEvent('click', this, e);
25517     }
25518    
25519 });
25520
25521  
25522 /*
25523  * - LGPL
25524  *
25525  * numberBox
25526  * 
25527  */
25528 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25529
25530 /**
25531  * @class Roo.bootstrap.dash.NumberBox
25532  * @extends Roo.bootstrap.Component
25533  * Bootstrap NumberBox class
25534  * @cfg {String} headline Box headline
25535  * @cfg {String} content Box content
25536  * @cfg {String} icon Box icon
25537  * @cfg {String} footer Footer text
25538  * @cfg {String} fhref Footer href
25539  * 
25540  * @constructor
25541  * Create a new NumberBox
25542  * @param {Object} config The config object
25543  */
25544
25545
25546 Roo.bootstrap.dash.NumberBox = function(config){
25547     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25548     
25549 };
25550
25551 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25552     
25553     headline : '',
25554     content : '',
25555     icon : '',
25556     footer : '',
25557     fhref : '',
25558     ficon : '',
25559     
25560     getAutoCreate : function(){
25561         
25562         var cfg = {
25563             tag : 'div',
25564             cls : 'small-box ',
25565             cn : [
25566                 {
25567                     tag : 'div',
25568                     cls : 'inner',
25569                     cn :[
25570                         {
25571                             tag : 'h3',
25572                             cls : 'roo-headline',
25573                             html : this.headline
25574                         },
25575                         {
25576                             tag : 'p',
25577                             cls : 'roo-content',
25578                             html : this.content
25579                         }
25580                     ]
25581                 }
25582             ]
25583         };
25584         
25585         if(this.icon){
25586             cfg.cn.push({
25587                 tag : 'div',
25588                 cls : 'icon',
25589                 cn :[
25590                     {
25591                         tag : 'i',
25592                         cls : 'ion ' + this.icon
25593                     }
25594                 ]
25595             });
25596         }
25597         
25598         if(this.footer){
25599             var footer = {
25600                 tag : 'a',
25601                 cls : 'small-box-footer',
25602                 href : this.fhref || '#',
25603                 html : this.footer
25604             };
25605             
25606             cfg.cn.push(footer);
25607             
25608         }
25609         
25610         return  cfg;
25611     },
25612
25613     onRender : function(ct,position){
25614         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25615
25616
25617        
25618                 
25619     },
25620
25621     setHeadline: function (value)
25622     {
25623         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25624     },
25625     
25626     setFooter: function (value, href)
25627     {
25628         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25629         
25630         if(href){
25631             this.el.select('a.small-box-footer',true).first().attr('href', href);
25632         }
25633         
25634     },
25635
25636     setContent: function (value)
25637     {
25638         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25639     },
25640
25641     initEvents: function() 
25642     {   
25643         
25644     }
25645     
25646 });
25647
25648  
25649 /*
25650  * - LGPL
25651  *
25652  * TabBox
25653  * 
25654  */
25655 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25656
25657 /**
25658  * @class Roo.bootstrap.dash.TabBox
25659  * @extends Roo.bootstrap.Component
25660  * Bootstrap TabBox class
25661  * @cfg {String} title Title of the TabBox
25662  * @cfg {String} icon Icon of the TabBox
25663  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25664  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25665  * 
25666  * @constructor
25667  * Create a new TabBox
25668  * @param {Object} config The config object
25669  */
25670
25671
25672 Roo.bootstrap.dash.TabBox = function(config){
25673     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25674     this.addEvents({
25675         // raw events
25676         /**
25677          * @event addpane
25678          * When a pane is added
25679          * @param {Roo.bootstrap.dash.TabPane} pane
25680          */
25681         "addpane" : true,
25682         /**
25683          * @event activatepane
25684          * When a pane is activated
25685          * @param {Roo.bootstrap.dash.TabPane} pane
25686          */
25687         "activatepane" : true
25688         
25689          
25690     });
25691     
25692     this.panes = [];
25693 };
25694
25695 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25696
25697     title : '',
25698     icon : false,
25699     showtabs : true,
25700     tabScrollable : false,
25701     
25702     getChildContainer : function()
25703     {
25704         return this.el.select('.tab-content', true).first();
25705     },
25706     
25707     getAutoCreate : function(){
25708         
25709         var header = {
25710             tag: 'li',
25711             cls: 'pull-left header',
25712             html: this.title,
25713             cn : []
25714         };
25715         
25716         if(this.icon){
25717             header.cn.push({
25718                 tag: 'i',
25719                 cls: 'fa ' + this.icon
25720             });
25721         }
25722         
25723         var h = {
25724             tag: 'ul',
25725             cls: 'nav nav-tabs pull-right',
25726             cn: [
25727                 header
25728             ]
25729         };
25730         
25731         if(this.tabScrollable){
25732             h = {
25733                 tag: 'div',
25734                 cls: 'tab-header',
25735                 cn: [
25736                     {
25737                         tag: 'ul',
25738                         cls: 'nav nav-tabs pull-right',
25739                         cn: [
25740                             header
25741                         ]
25742                     }
25743                 ]
25744             };
25745         }
25746         
25747         var cfg = {
25748             tag: 'div',
25749             cls: 'nav-tabs-custom',
25750             cn: [
25751                 h,
25752                 {
25753                     tag: 'div',
25754                     cls: 'tab-content no-padding',
25755                     cn: []
25756                 }
25757             ]
25758         };
25759
25760         return  cfg;
25761     },
25762     initEvents : function()
25763     {
25764         //Roo.log('add add pane handler');
25765         this.on('addpane', this.onAddPane, this);
25766     },
25767      /**
25768      * Updates the box title
25769      * @param {String} html to set the title to.
25770      */
25771     setTitle : function(value)
25772     {
25773         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25774     },
25775     onAddPane : function(pane)
25776     {
25777         this.panes.push(pane);
25778         //Roo.log('addpane');
25779         //Roo.log(pane);
25780         // tabs are rendere left to right..
25781         if(!this.showtabs){
25782             return;
25783         }
25784         
25785         var ctr = this.el.select('.nav-tabs', true).first();
25786          
25787          
25788         var existing = ctr.select('.nav-tab',true);
25789         var qty = existing.getCount();;
25790         
25791         
25792         var tab = ctr.createChild({
25793             tag : 'li',
25794             cls : 'nav-tab' + (qty ? '' : ' active'),
25795             cn : [
25796                 {
25797                     tag : 'a',
25798                     href:'#',
25799                     html : pane.title
25800                 }
25801             ]
25802         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25803         pane.tab = tab;
25804         
25805         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25806         if (!qty) {
25807             pane.el.addClass('active');
25808         }
25809         
25810                 
25811     },
25812     onTabClick : function(ev,un,ob,pane)
25813     {
25814         //Roo.log('tab - prev default');
25815         ev.preventDefault();
25816         
25817         
25818         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25819         pane.tab.addClass('active');
25820         //Roo.log(pane.title);
25821         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25822         // technically we should have a deactivate event.. but maybe add later.
25823         // and it should not de-activate the selected tab...
25824         this.fireEvent('activatepane', pane);
25825         pane.el.addClass('active');
25826         pane.fireEvent('activate');
25827         
25828         
25829     },
25830     
25831     getActivePane : function()
25832     {
25833         var r = false;
25834         Roo.each(this.panes, function(p) {
25835             if(p.el.hasClass('active')){
25836                 r = p;
25837                 return false;
25838             }
25839             
25840             return;
25841         });
25842         
25843         return r;
25844     }
25845     
25846     
25847 });
25848
25849  
25850 /*
25851  * - LGPL
25852  *
25853  * Tab pane
25854  * 
25855  */
25856 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25857 /**
25858  * @class Roo.bootstrap.TabPane
25859  * @extends Roo.bootstrap.Component
25860  * Bootstrap TabPane class
25861  * @cfg {Boolean} active (false | true) Default false
25862  * @cfg {String} title title of panel
25863
25864  * 
25865  * @constructor
25866  * Create a new TabPane
25867  * @param {Object} config The config object
25868  */
25869
25870 Roo.bootstrap.dash.TabPane = function(config){
25871     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25872     
25873     this.addEvents({
25874         // raw events
25875         /**
25876          * @event activate
25877          * When a pane is activated
25878          * @param {Roo.bootstrap.dash.TabPane} pane
25879          */
25880         "activate" : true
25881          
25882     });
25883 };
25884
25885 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25886     
25887     active : false,
25888     title : '',
25889     
25890     // the tabBox that this is attached to.
25891     tab : false,
25892      
25893     getAutoCreate : function() 
25894     {
25895         var cfg = {
25896             tag: 'div',
25897             cls: 'tab-pane'
25898         };
25899         
25900         if(this.active){
25901             cfg.cls += ' active';
25902         }
25903         
25904         return cfg;
25905     },
25906     initEvents  : function()
25907     {
25908         //Roo.log('trigger add pane handler');
25909         this.parent().fireEvent('addpane', this)
25910     },
25911     
25912      /**
25913      * Updates the tab title 
25914      * @param {String} html to set the title to.
25915      */
25916     setTitle: function(str)
25917     {
25918         if (!this.tab) {
25919             return;
25920         }
25921         this.title = str;
25922         this.tab.select('a', true).first().dom.innerHTML = str;
25923         
25924     }
25925     
25926     
25927     
25928 });
25929
25930  
25931
25932
25933  /*
25934  * - LGPL
25935  *
25936  * menu
25937  * 
25938  */
25939 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25940
25941 /**
25942  * @class Roo.bootstrap.menu.Menu
25943  * @extends Roo.bootstrap.Component
25944  * Bootstrap Menu class - container for Menu
25945  * @cfg {String} html Text of the menu
25946  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25947  * @cfg {String} icon Font awesome icon
25948  * @cfg {String} pos Menu align to (top | bottom) default bottom
25949  * 
25950  * 
25951  * @constructor
25952  * Create a new Menu
25953  * @param {Object} config The config object
25954  */
25955
25956
25957 Roo.bootstrap.menu.Menu = function(config){
25958     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25959     
25960     this.addEvents({
25961         /**
25962          * @event beforeshow
25963          * Fires before this menu is displayed
25964          * @param {Roo.bootstrap.menu.Menu} this
25965          */
25966         beforeshow : true,
25967         /**
25968          * @event beforehide
25969          * Fires before this menu is hidden
25970          * @param {Roo.bootstrap.menu.Menu} this
25971          */
25972         beforehide : true,
25973         /**
25974          * @event show
25975          * Fires after this menu is displayed
25976          * @param {Roo.bootstrap.menu.Menu} this
25977          */
25978         show : true,
25979         /**
25980          * @event hide
25981          * Fires after this menu is hidden
25982          * @param {Roo.bootstrap.menu.Menu} this
25983          */
25984         hide : true,
25985         /**
25986          * @event click
25987          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25988          * @param {Roo.bootstrap.menu.Menu} this
25989          * @param {Roo.EventObject} e
25990          */
25991         click : true
25992     });
25993     
25994 };
25995
25996 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25997     
25998     submenu : false,
25999     html : '',
26000     weight : 'default',
26001     icon : false,
26002     pos : 'bottom',
26003     
26004     
26005     getChildContainer : function() {
26006         if(this.isSubMenu){
26007             return this.el;
26008         }
26009         
26010         return this.el.select('ul.dropdown-menu', true).first();  
26011     },
26012     
26013     getAutoCreate : function()
26014     {
26015         var text = [
26016             {
26017                 tag : 'span',
26018                 cls : 'roo-menu-text',
26019                 html : this.html
26020             }
26021         ];
26022         
26023         if(this.icon){
26024             text.unshift({
26025                 tag : 'i',
26026                 cls : 'fa ' + this.icon
26027             })
26028         }
26029         
26030         
26031         var cfg = {
26032             tag : 'div',
26033             cls : 'btn-group',
26034             cn : [
26035                 {
26036                     tag : 'button',
26037                     cls : 'dropdown-button btn btn-' + this.weight,
26038                     cn : text
26039                 },
26040                 {
26041                     tag : 'button',
26042                     cls : 'dropdown-toggle btn btn-' + this.weight,
26043                     cn : [
26044                         {
26045                             tag : 'span',
26046                             cls : 'caret'
26047                         }
26048                     ]
26049                 },
26050                 {
26051                     tag : 'ul',
26052                     cls : 'dropdown-menu'
26053                 }
26054             ]
26055             
26056         };
26057         
26058         if(this.pos == 'top'){
26059             cfg.cls += ' dropup';
26060         }
26061         
26062         if(this.isSubMenu){
26063             cfg = {
26064                 tag : 'ul',
26065                 cls : 'dropdown-menu'
26066             }
26067         }
26068         
26069         return cfg;
26070     },
26071     
26072     onRender : function(ct, position)
26073     {
26074         this.isSubMenu = ct.hasClass('dropdown-submenu');
26075         
26076         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26077     },
26078     
26079     initEvents : function() 
26080     {
26081         if(this.isSubMenu){
26082             return;
26083         }
26084         
26085         this.hidden = true;
26086         
26087         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26088         this.triggerEl.on('click', this.onTriggerPress, this);
26089         
26090         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26091         this.buttonEl.on('click', this.onClick, this);
26092         
26093     },
26094     
26095     list : function()
26096     {
26097         if(this.isSubMenu){
26098             return this.el;
26099         }
26100         
26101         return this.el.select('ul.dropdown-menu', true).first();
26102     },
26103     
26104     onClick : function(e)
26105     {
26106         this.fireEvent("click", this, e);
26107     },
26108     
26109     onTriggerPress  : function(e)
26110     {   
26111         if (this.isVisible()) {
26112             this.hide();
26113         } else {
26114             this.show();
26115         }
26116     },
26117     
26118     isVisible : function(){
26119         return !this.hidden;
26120     },
26121     
26122     show : function()
26123     {
26124         this.fireEvent("beforeshow", this);
26125         
26126         this.hidden = false;
26127         this.el.addClass('open');
26128         
26129         Roo.get(document).on("mouseup", this.onMouseUp, this);
26130         
26131         this.fireEvent("show", this);
26132         
26133         
26134     },
26135     
26136     hide : function()
26137     {
26138         this.fireEvent("beforehide", this);
26139         
26140         this.hidden = true;
26141         this.el.removeClass('open');
26142         
26143         Roo.get(document).un("mouseup", this.onMouseUp);
26144         
26145         this.fireEvent("hide", this);
26146     },
26147     
26148     onMouseUp : function()
26149     {
26150         this.hide();
26151     }
26152     
26153 });
26154
26155  
26156  /*
26157  * - LGPL
26158  *
26159  * menu item
26160  * 
26161  */
26162 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26163
26164 /**
26165  * @class Roo.bootstrap.menu.Item
26166  * @extends Roo.bootstrap.Component
26167  * Bootstrap MenuItem class
26168  * @cfg {Boolean} submenu (true | false) default false
26169  * @cfg {String} html text of the item
26170  * @cfg {String} href the link
26171  * @cfg {Boolean} disable (true | false) default false
26172  * @cfg {Boolean} preventDefault (true | false) default true
26173  * @cfg {String} icon Font awesome icon
26174  * @cfg {String} pos Submenu align to (left | right) default right 
26175  * 
26176  * 
26177  * @constructor
26178  * Create a new Item
26179  * @param {Object} config The config object
26180  */
26181
26182
26183 Roo.bootstrap.menu.Item = function(config){
26184     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26185     this.addEvents({
26186         /**
26187          * @event mouseover
26188          * Fires when the mouse is hovering over this menu
26189          * @param {Roo.bootstrap.menu.Item} this
26190          * @param {Roo.EventObject} e
26191          */
26192         mouseover : true,
26193         /**
26194          * @event mouseout
26195          * Fires when the mouse exits this menu
26196          * @param {Roo.bootstrap.menu.Item} this
26197          * @param {Roo.EventObject} e
26198          */
26199         mouseout : true,
26200         // raw events
26201         /**
26202          * @event click
26203          * The raw click event for the entire grid.
26204          * @param {Roo.EventObject} e
26205          */
26206         click : true
26207     });
26208 };
26209
26210 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26211     
26212     submenu : false,
26213     href : '',
26214     html : '',
26215     preventDefault: true,
26216     disable : false,
26217     icon : false,
26218     pos : 'right',
26219     
26220     getAutoCreate : function()
26221     {
26222         var text = [
26223             {
26224                 tag : 'span',
26225                 cls : 'roo-menu-item-text',
26226                 html : this.html
26227             }
26228         ];
26229         
26230         if(this.icon){
26231             text.unshift({
26232                 tag : 'i',
26233                 cls : 'fa ' + this.icon
26234             })
26235         }
26236         
26237         var cfg = {
26238             tag : 'li',
26239             cn : [
26240                 {
26241                     tag : 'a',
26242                     href : this.href || '#',
26243                     cn : text
26244                 }
26245             ]
26246         };
26247         
26248         if(this.disable){
26249             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26250         }
26251         
26252         if(this.submenu){
26253             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26254             
26255             if(this.pos == 'left'){
26256                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26257             }
26258         }
26259         
26260         return cfg;
26261     },
26262     
26263     initEvents : function() 
26264     {
26265         this.el.on('mouseover', this.onMouseOver, this);
26266         this.el.on('mouseout', this.onMouseOut, this);
26267         
26268         this.el.select('a', true).first().on('click', this.onClick, this);
26269         
26270     },
26271     
26272     onClick : function(e)
26273     {
26274         if(this.preventDefault){
26275             e.preventDefault();
26276         }
26277         
26278         this.fireEvent("click", this, e);
26279     },
26280     
26281     onMouseOver : function(e)
26282     {
26283         if(this.submenu && this.pos == 'left'){
26284             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26285         }
26286         
26287         this.fireEvent("mouseover", this, e);
26288     },
26289     
26290     onMouseOut : function(e)
26291     {
26292         this.fireEvent("mouseout", this, e);
26293     }
26294 });
26295
26296  
26297
26298  /*
26299  * - LGPL
26300  *
26301  * menu separator
26302  * 
26303  */
26304 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26305
26306 /**
26307  * @class Roo.bootstrap.menu.Separator
26308  * @extends Roo.bootstrap.Component
26309  * Bootstrap Separator class
26310  * 
26311  * @constructor
26312  * Create a new Separator
26313  * @param {Object} config The config object
26314  */
26315
26316
26317 Roo.bootstrap.menu.Separator = function(config){
26318     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26319 };
26320
26321 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26322     
26323     getAutoCreate : function(){
26324         var cfg = {
26325             tag : 'li',
26326             cls: 'divider'
26327         };
26328         
26329         return cfg;
26330     }
26331    
26332 });
26333
26334  
26335
26336  /*
26337  * - LGPL
26338  *
26339  * Tooltip
26340  * 
26341  */
26342
26343 /**
26344  * @class Roo.bootstrap.Tooltip
26345  * Bootstrap Tooltip class
26346  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26347  * to determine which dom element triggers the tooltip.
26348  * 
26349  * It needs to add support for additional attributes like tooltip-position
26350  * 
26351  * @constructor
26352  * Create a new Toolti
26353  * @param {Object} config The config object
26354  */
26355
26356 Roo.bootstrap.Tooltip = function(config){
26357     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26358     
26359     this.alignment = Roo.bootstrap.Tooltip.alignment;
26360     
26361     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26362         this.alignment = config.alignment;
26363     }
26364     
26365 };
26366
26367 Roo.apply(Roo.bootstrap.Tooltip, {
26368     /**
26369      * @function init initialize tooltip monitoring.
26370      * @static
26371      */
26372     currentEl : false,
26373     currentTip : false,
26374     currentRegion : false,
26375     
26376     //  init : delay?
26377     
26378     init : function()
26379     {
26380         Roo.get(document).on('mouseover', this.enter ,this);
26381         Roo.get(document).on('mouseout', this.leave, this);
26382          
26383         
26384         this.currentTip = new Roo.bootstrap.Tooltip();
26385     },
26386     
26387     enter : function(ev)
26388     {
26389         var dom = ev.getTarget();
26390         
26391         //Roo.log(['enter',dom]);
26392         var el = Roo.fly(dom);
26393         if (this.currentEl) {
26394             //Roo.log(dom);
26395             //Roo.log(this.currentEl);
26396             //Roo.log(this.currentEl.contains(dom));
26397             if (this.currentEl == el) {
26398                 return;
26399             }
26400             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26401                 return;
26402             }
26403
26404         }
26405         
26406         if (this.currentTip.el) {
26407             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26408         }    
26409         //Roo.log(ev);
26410         
26411         if(!el || el.dom == document){
26412             return;
26413         }
26414         
26415         var bindEl = el;
26416         
26417         // you can not look for children, as if el is the body.. then everythign is the child..
26418         if (!el.attr('tooltip')) { //
26419             if (!el.select("[tooltip]").elements.length) {
26420                 return;
26421             }
26422             // is the mouse over this child...?
26423             bindEl = el.select("[tooltip]").first();
26424             var xy = ev.getXY();
26425             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26426                 //Roo.log("not in region.");
26427                 return;
26428             }
26429             //Roo.log("child element over..");
26430             
26431         }
26432         this.currentEl = bindEl;
26433         this.currentTip.bind(bindEl);
26434         this.currentRegion = Roo.lib.Region.getRegion(dom);
26435         this.currentTip.enter();
26436         
26437     },
26438     leave : function(ev)
26439     {
26440         var dom = ev.getTarget();
26441         //Roo.log(['leave',dom]);
26442         if (!this.currentEl) {
26443             return;
26444         }
26445         
26446         
26447         if (dom != this.currentEl.dom) {
26448             return;
26449         }
26450         var xy = ev.getXY();
26451         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26452             return;
26453         }
26454         // only activate leave if mouse cursor is outside... bounding box..
26455         
26456         
26457         
26458         
26459         if (this.currentTip) {
26460             this.currentTip.leave();
26461         }
26462         //Roo.log('clear currentEl');
26463         this.currentEl = false;
26464         
26465         
26466     },
26467     alignment : {
26468         'left' : ['r-l', [-2,0], 'right'],
26469         'right' : ['l-r', [2,0], 'left'],
26470         'bottom' : ['t-b', [0,2], 'top'],
26471         'top' : [ 'b-t', [0,-2], 'bottom']
26472     }
26473     
26474 });
26475
26476
26477 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26478     
26479     
26480     bindEl : false,
26481     
26482     delay : null, // can be { show : 300 , hide: 500}
26483     
26484     timeout : null,
26485     
26486     hoverState : null, //???
26487     
26488     placement : 'bottom', 
26489     
26490     alignment : false,
26491     
26492     getAutoCreate : function(){
26493     
26494         var cfg = {
26495            cls : 'tooltip',
26496            role : 'tooltip',
26497            cn : [
26498                 {
26499                     cls : 'tooltip-arrow'
26500                 },
26501                 {
26502                     cls : 'tooltip-inner'
26503                 }
26504            ]
26505         };
26506         
26507         return cfg;
26508     },
26509     bind : function(el)
26510     {
26511         this.bindEl = el;
26512     },
26513       
26514     
26515     enter : function () {
26516        
26517         if (this.timeout != null) {
26518             clearTimeout(this.timeout);
26519         }
26520         
26521         this.hoverState = 'in';
26522          //Roo.log("enter - show");
26523         if (!this.delay || !this.delay.show) {
26524             this.show();
26525             return;
26526         }
26527         var _t = this;
26528         this.timeout = setTimeout(function () {
26529             if (_t.hoverState == 'in') {
26530                 _t.show();
26531             }
26532         }, this.delay.show);
26533     },
26534     leave : function()
26535     {
26536         clearTimeout(this.timeout);
26537     
26538         this.hoverState = 'out';
26539          if (!this.delay || !this.delay.hide) {
26540             this.hide();
26541             return;
26542         }
26543        
26544         var _t = this;
26545         this.timeout = setTimeout(function () {
26546             //Roo.log("leave - timeout");
26547             
26548             if (_t.hoverState == 'out') {
26549                 _t.hide();
26550                 Roo.bootstrap.Tooltip.currentEl = false;
26551             }
26552         }, delay);
26553     },
26554     
26555     show : function (msg)
26556     {
26557         if (!this.el) {
26558             this.render(document.body);
26559         }
26560         // set content.
26561         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26562         
26563         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26564         
26565         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26566         
26567         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26568         
26569         var placement = typeof this.placement == 'function' ?
26570             this.placement.call(this, this.el, on_el) :
26571             this.placement;
26572             
26573         var autoToken = /\s?auto?\s?/i;
26574         var autoPlace = autoToken.test(placement);
26575         if (autoPlace) {
26576             placement = placement.replace(autoToken, '') || 'top';
26577         }
26578         
26579         //this.el.detach()
26580         //this.el.setXY([0,0]);
26581         this.el.show();
26582         //this.el.dom.style.display='block';
26583         
26584         //this.el.appendTo(on_el);
26585         
26586         var p = this.getPosition();
26587         var box = this.el.getBox();
26588         
26589         if (autoPlace) {
26590             // fixme..
26591         }
26592         
26593         var align = this.alignment[placement];
26594         
26595         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26596         
26597         if(placement == 'top' || placement == 'bottom'){
26598             if(xy[0] < 0){
26599                 placement = 'right';
26600             }
26601             
26602             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26603                 placement = 'left';
26604             }
26605             
26606             var scroll = Roo.select('body', true).first().getScroll();
26607             
26608             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26609                 placement = 'top';
26610             }
26611             
26612             align = this.alignment[placement];
26613         }
26614         
26615         this.el.alignTo(this.bindEl, align[0],align[1]);
26616         //var arrow = this.el.select('.arrow',true).first();
26617         //arrow.set(align[2], 
26618         
26619         this.el.addClass(placement);
26620         
26621         this.el.addClass('in fade');
26622         
26623         this.hoverState = null;
26624         
26625         if (this.el.hasClass('fade')) {
26626             // fade it?
26627         }
26628         
26629     },
26630     hide : function()
26631     {
26632          
26633         if (!this.el) {
26634             return;
26635         }
26636         //this.el.setXY([0,0]);
26637         this.el.removeClass('in');
26638         //this.el.hide();
26639         
26640     }
26641     
26642 });
26643  
26644
26645  /*
26646  * - LGPL
26647  *
26648  * Location Picker
26649  * 
26650  */
26651
26652 /**
26653  * @class Roo.bootstrap.LocationPicker
26654  * @extends Roo.bootstrap.Component
26655  * Bootstrap LocationPicker class
26656  * @cfg {Number} latitude Position when init default 0
26657  * @cfg {Number} longitude Position when init default 0
26658  * @cfg {Number} zoom default 15
26659  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26660  * @cfg {Boolean} mapTypeControl default false
26661  * @cfg {Boolean} disableDoubleClickZoom default false
26662  * @cfg {Boolean} scrollwheel default true
26663  * @cfg {Boolean} streetViewControl default false
26664  * @cfg {Number} radius default 0
26665  * @cfg {String} locationName
26666  * @cfg {Boolean} draggable default true
26667  * @cfg {Boolean} enableAutocomplete default false
26668  * @cfg {Boolean} enableReverseGeocode default true
26669  * @cfg {String} markerTitle
26670  * 
26671  * @constructor
26672  * Create a new LocationPicker
26673  * @param {Object} config The config object
26674  */
26675
26676
26677 Roo.bootstrap.LocationPicker = function(config){
26678     
26679     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26680     
26681     this.addEvents({
26682         /**
26683          * @event initial
26684          * Fires when the picker initialized.
26685          * @param {Roo.bootstrap.LocationPicker} this
26686          * @param {Google Location} location
26687          */
26688         initial : true,
26689         /**
26690          * @event positionchanged
26691          * Fires when the picker position changed.
26692          * @param {Roo.bootstrap.LocationPicker} this
26693          * @param {Google Location} location
26694          */
26695         positionchanged : true,
26696         /**
26697          * @event resize
26698          * Fires when the map resize.
26699          * @param {Roo.bootstrap.LocationPicker} this
26700          */
26701         resize : true,
26702         /**
26703          * @event show
26704          * Fires when the map show.
26705          * @param {Roo.bootstrap.LocationPicker} this
26706          */
26707         show : true,
26708         /**
26709          * @event hide
26710          * Fires when the map hide.
26711          * @param {Roo.bootstrap.LocationPicker} this
26712          */
26713         hide : true,
26714         /**
26715          * @event mapClick
26716          * Fires when click the map.
26717          * @param {Roo.bootstrap.LocationPicker} this
26718          * @param {Map event} e
26719          */
26720         mapClick : true,
26721         /**
26722          * @event mapRightClick
26723          * Fires when right click the map.
26724          * @param {Roo.bootstrap.LocationPicker} this
26725          * @param {Map event} e
26726          */
26727         mapRightClick : true,
26728         /**
26729          * @event markerClick
26730          * Fires when click the marker.
26731          * @param {Roo.bootstrap.LocationPicker} this
26732          * @param {Map event} e
26733          */
26734         markerClick : true,
26735         /**
26736          * @event markerRightClick
26737          * Fires when right click the marker.
26738          * @param {Roo.bootstrap.LocationPicker} this
26739          * @param {Map event} e
26740          */
26741         markerRightClick : true,
26742         /**
26743          * @event OverlayViewDraw
26744          * Fires when OverlayView Draw
26745          * @param {Roo.bootstrap.LocationPicker} this
26746          */
26747         OverlayViewDraw : true,
26748         /**
26749          * @event OverlayViewOnAdd
26750          * Fires when OverlayView Draw
26751          * @param {Roo.bootstrap.LocationPicker} this
26752          */
26753         OverlayViewOnAdd : true,
26754         /**
26755          * @event OverlayViewOnRemove
26756          * Fires when OverlayView Draw
26757          * @param {Roo.bootstrap.LocationPicker} this
26758          */
26759         OverlayViewOnRemove : true,
26760         /**
26761          * @event OverlayViewShow
26762          * Fires when OverlayView Draw
26763          * @param {Roo.bootstrap.LocationPicker} this
26764          * @param {Pixel} cpx
26765          */
26766         OverlayViewShow : true,
26767         /**
26768          * @event OverlayViewHide
26769          * Fires when OverlayView Draw
26770          * @param {Roo.bootstrap.LocationPicker} this
26771          */
26772         OverlayViewHide : true,
26773         /**
26774          * @event loadexception
26775          * Fires when load google lib failed.
26776          * @param {Roo.bootstrap.LocationPicker} this
26777          */
26778         loadexception : true
26779     });
26780         
26781 };
26782
26783 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26784     
26785     gMapContext: false,
26786     
26787     latitude: 0,
26788     longitude: 0,
26789     zoom: 15,
26790     mapTypeId: false,
26791     mapTypeControl: false,
26792     disableDoubleClickZoom: false,
26793     scrollwheel: true,
26794     streetViewControl: false,
26795     radius: 0,
26796     locationName: '',
26797     draggable: true,
26798     enableAutocomplete: false,
26799     enableReverseGeocode: true,
26800     markerTitle: '',
26801     
26802     getAutoCreate: function()
26803     {
26804
26805         var cfg = {
26806             tag: 'div',
26807             cls: 'roo-location-picker'
26808         };
26809         
26810         return cfg
26811     },
26812     
26813     initEvents: function(ct, position)
26814     {       
26815         if(!this.el.getWidth() || this.isApplied()){
26816             return;
26817         }
26818         
26819         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26820         
26821         this.initial();
26822     },
26823     
26824     initial: function()
26825     {
26826         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26827             this.fireEvent('loadexception', this);
26828             return;
26829         }
26830         
26831         if(!this.mapTypeId){
26832             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26833         }
26834         
26835         this.gMapContext = this.GMapContext();
26836         
26837         this.initOverlayView();
26838         
26839         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26840         
26841         var _this = this;
26842                 
26843         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26844             _this.setPosition(_this.gMapContext.marker.position);
26845         });
26846         
26847         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26848             _this.fireEvent('mapClick', this, event);
26849             
26850         });
26851
26852         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26853             _this.fireEvent('mapRightClick', this, event);
26854             
26855         });
26856         
26857         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26858             _this.fireEvent('markerClick', this, event);
26859             
26860         });
26861
26862         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26863             _this.fireEvent('markerRightClick', this, event);
26864             
26865         });
26866         
26867         this.setPosition(this.gMapContext.location);
26868         
26869         this.fireEvent('initial', this, this.gMapContext.location);
26870     },
26871     
26872     initOverlayView: function()
26873     {
26874         var _this = this;
26875         
26876         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26877             
26878             draw: function()
26879             {
26880                 _this.fireEvent('OverlayViewDraw', _this);
26881             },
26882             
26883             onAdd: function()
26884             {
26885                 _this.fireEvent('OverlayViewOnAdd', _this);
26886             },
26887             
26888             onRemove: function()
26889             {
26890                 _this.fireEvent('OverlayViewOnRemove', _this);
26891             },
26892             
26893             show: function(cpx)
26894             {
26895                 _this.fireEvent('OverlayViewShow', _this, cpx);
26896             },
26897             
26898             hide: function()
26899             {
26900                 _this.fireEvent('OverlayViewHide', _this);
26901             }
26902             
26903         });
26904     },
26905     
26906     fromLatLngToContainerPixel: function(event)
26907     {
26908         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26909     },
26910     
26911     isApplied: function() 
26912     {
26913         return this.getGmapContext() == false ? false : true;
26914     },
26915     
26916     getGmapContext: function() 
26917     {
26918         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26919     },
26920     
26921     GMapContext: function() 
26922     {
26923         var position = new google.maps.LatLng(this.latitude, this.longitude);
26924         
26925         var _map = new google.maps.Map(this.el.dom, {
26926             center: position,
26927             zoom: this.zoom,
26928             mapTypeId: this.mapTypeId,
26929             mapTypeControl: this.mapTypeControl,
26930             disableDoubleClickZoom: this.disableDoubleClickZoom,
26931             scrollwheel: this.scrollwheel,
26932             streetViewControl: this.streetViewControl,
26933             locationName: this.locationName,
26934             draggable: this.draggable,
26935             enableAutocomplete: this.enableAutocomplete,
26936             enableReverseGeocode: this.enableReverseGeocode
26937         });
26938         
26939         var _marker = new google.maps.Marker({
26940             position: position,
26941             map: _map,
26942             title: this.markerTitle,
26943             draggable: this.draggable
26944         });
26945         
26946         return {
26947             map: _map,
26948             marker: _marker,
26949             circle: null,
26950             location: position,
26951             radius: this.radius,
26952             locationName: this.locationName,
26953             addressComponents: {
26954                 formatted_address: null,
26955                 addressLine1: null,
26956                 addressLine2: null,
26957                 streetName: null,
26958                 streetNumber: null,
26959                 city: null,
26960                 district: null,
26961                 state: null,
26962                 stateOrProvince: null
26963             },
26964             settings: this,
26965             domContainer: this.el.dom,
26966             geodecoder: new google.maps.Geocoder()
26967         };
26968     },
26969     
26970     drawCircle: function(center, radius, options) 
26971     {
26972         if (this.gMapContext.circle != null) {
26973             this.gMapContext.circle.setMap(null);
26974         }
26975         if (radius > 0) {
26976             radius *= 1;
26977             options = Roo.apply({}, options, {
26978                 strokeColor: "#0000FF",
26979                 strokeOpacity: .35,
26980                 strokeWeight: 2,
26981                 fillColor: "#0000FF",
26982                 fillOpacity: .2
26983             });
26984             
26985             options.map = this.gMapContext.map;
26986             options.radius = radius;
26987             options.center = center;
26988             this.gMapContext.circle = new google.maps.Circle(options);
26989             return this.gMapContext.circle;
26990         }
26991         
26992         return null;
26993     },
26994     
26995     setPosition: function(location) 
26996     {
26997         this.gMapContext.location = location;
26998         this.gMapContext.marker.setPosition(location);
26999         this.gMapContext.map.panTo(location);
27000         this.drawCircle(location, this.gMapContext.radius, {});
27001         
27002         var _this = this;
27003         
27004         if (this.gMapContext.settings.enableReverseGeocode) {
27005             this.gMapContext.geodecoder.geocode({
27006                 latLng: this.gMapContext.location
27007             }, function(results, status) {
27008                 
27009                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27010                     _this.gMapContext.locationName = results[0].formatted_address;
27011                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27012                     
27013                     _this.fireEvent('positionchanged', this, location);
27014                 }
27015             });
27016             
27017             return;
27018         }
27019         
27020         this.fireEvent('positionchanged', this, location);
27021     },
27022     
27023     resize: function()
27024     {
27025         google.maps.event.trigger(this.gMapContext.map, "resize");
27026         
27027         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27028         
27029         this.fireEvent('resize', this);
27030     },
27031     
27032     setPositionByLatLng: function(latitude, longitude)
27033     {
27034         this.setPosition(new google.maps.LatLng(latitude, longitude));
27035     },
27036     
27037     getCurrentPosition: function() 
27038     {
27039         return {
27040             latitude: this.gMapContext.location.lat(),
27041             longitude: this.gMapContext.location.lng()
27042         };
27043     },
27044     
27045     getAddressName: function() 
27046     {
27047         return this.gMapContext.locationName;
27048     },
27049     
27050     getAddressComponents: function() 
27051     {
27052         return this.gMapContext.addressComponents;
27053     },
27054     
27055     address_component_from_google_geocode: function(address_components) 
27056     {
27057         var result = {};
27058         
27059         for (var i = 0; i < address_components.length; i++) {
27060             var component = address_components[i];
27061             if (component.types.indexOf("postal_code") >= 0) {
27062                 result.postalCode = component.short_name;
27063             } else if (component.types.indexOf("street_number") >= 0) {
27064                 result.streetNumber = component.short_name;
27065             } else if (component.types.indexOf("route") >= 0) {
27066                 result.streetName = component.short_name;
27067             } else if (component.types.indexOf("neighborhood") >= 0) {
27068                 result.city = component.short_name;
27069             } else if (component.types.indexOf("locality") >= 0) {
27070                 result.city = component.short_name;
27071             } else if (component.types.indexOf("sublocality") >= 0) {
27072                 result.district = component.short_name;
27073             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27074                 result.stateOrProvince = component.short_name;
27075             } else if (component.types.indexOf("country") >= 0) {
27076                 result.country = component.short_name;
27077             }
27078         }
27079         
27080         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27081         result.addressLine2 = "";
27082         return result;
27083     },
27084     
27085     setZoomLevel: function(zoom)
27086     {
27087         this.gMapContext.map.setZoom(zoom);
27088     },
27089     
27090     show: function()
27091     {
27092         if(!this.el){
27093             return;
27094         }
27095         
27096         this.el.show();
27097         
27098         this.resize();
27099         
27100         this.fireEvent('show', this);
27101     },
27102     
27103     hide: function()
27104     {
27105         if(!this.el){
27106             return;
27107         }
27108         
27109         this.el.hide();
27110         
27111         this.fireEvent('hide', this);
27112     }
27113     
27114 });
27115
27116 Roo.apply(Roo.bootstrap.LocationPicker, {
27117     
27118     OverlayView : function(map, options)
27119     {
27120         options = options || {};
27121         
27122         this.setMap(map);
27123     }
27124     
27125     
27126 });/*
27127  * - LGPL
27128  *
27129  * Alert
27130  * 
27131  */
27132
27133 /**
27134  * @class Roo.bootstrap.Alert
27135  * @extends Roo.bootstrap.Component
27136  * Bootstrap Alert class
27137  * @cfg {String} title The title of alert
27138  * @cfg {String} html The content of alert
27139  * @cfg {String} weight (  success | info | warning | danger )
27140  * @cfg {String} faicon font-awesomeicon
27141  * 
27142  * @constructor
27143  * Create a new alert
27144  * @param {Object} config The config object
27145  */
27146
27147
27148 Roo.bootstrap.Alert = function(config){
27149     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27150     
27151 };
27152
27153 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27154     
27155     title: '',
27156     html: '',
27157     weight: false,
27158     faicon: false,
27159     
27160     getAutoCreate : function()
27161     {
27162         
27163         var cfg = {
27164             tag : 'div',
27165             cls : 'alert',
27166             cn : [
27167                 {
27168                     tag : 'i',
27169                     cls : 'roo-alert-icon'
27170                     
27171                 },
27172                 {
27173                     tag : 'b',
27174                     cls : 'roo-alert-title',
27175                     html : this.title
27176                 },
27177                 {
27178                     tag : 'span',
27179                     cls : 'roo-alert-text',
27180                     html : this.html
27181                 }
27182             ]
27183         };
27184         
27185         if(this.faicon){
27186             cfg.cn[0].cls += ' fa ' + this.faicon;
27187         }
27188         
27189         if(this.weight){
27190             cfg.cls += ' alert-' + this.weight;
27191         }
27192         
27193         return cfg;
27194     },
27195     
27196     initEvents: function() 
27197     {
27198         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27199     },
27200     
27201     setTitle : function(str)
27202     {
27203         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27204     },
27205     
27206     setText : function(str)
27207     {
27208         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27209     },
27210     
27211     setWeight : function(weight)
27212     {
27213         if(this.weight){
27214             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27215         }
27216         
27217         this.weight = weight;
27218         
27219         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27220     },
27221     
27222     setIcon : function(icon)
27223     {
27224         if(this.faicon){
27225             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27226         }
27227         
27228         this.faicon = icon;
27229         
27230         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27231     },
27232     
27233     hide: function() 
27234     {
27235         this.el.hide();   
27236     },
27237     
27238     show: function() 
27239     {  
27240         this.el.show();   
27241     }
27242     
27243 });
27244
27245  
27246 /*
27247 * Licence: LGPL
27248 */
27249
27250 /**
27251  * @class Roo.bootstrap.UploadCropbox
27252  * @extends Roo.bootstrap.Component
27253  * Bootstrap UploadCropbox class
27254  * @cfg {String} emptyText show when image has been loaded
27255  * @cfg {String} rotateNotify show when image too small to rotate
27256  * @cfg {Number} errorTimeout default 3000
27257  * @cfg {Number} minWidth default 300
27258  * @cfg {Number} minHeight default 300
27259  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27260  * @cfg {Boolean} isDocument (true|false) default false
27261  * @cfg {String} url action url
27262  * @cfg {String} paramName default 'imageUpload'
27263  * @cfg {String} method default POST
27264  * @cfg {Boolean} loadMask (true|false) default true
27265  * @cfg {Boolean} loadingText default 'Loading...'
27266  * 
27267  * @constructor
27268  * Create a new UploadCropbox
27269  * @param {Object} config The config object
27270  */
27271
27272 Roo.bootstrap.UploadCropbox = function(config){
27273     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27274     
27275     this.addEvents({
27276         /**
27277          * @event beforeselectfile
27278          * Fire before select file
27279          * @param {Roo.bootstrap.UploadCropbox} this
27280          */
27281         "beforeselectfile" : true,
27282         /**
27283          * @event initial
27284          * Fire after initEvent
27285          * @param {Roo.bootstrap.UploadCropbox} this
27286          */
27287         "initial" : true,
27288         /**
27289          * @event crop
27290          * Fire after initEvent
27291          * @param {Roo.bootstrap.UploadCropbox} this
27292          * @param {String} data
27293          */
27294         "crop" : true,
27295         /**
27296          * @event prepare
27297          * Fire when preparing the file data
27298          * @param {Roo.bootstrap.UploadCropbox} this
27299          * @param {Object} file
27300          */
27301         "prepare" : true,
27302         /**
27303          * @event exception
27304          * Fire when get exception
27305          * @param {Roo.bootstrap.UploadCropbox} this
27306          * @param {XMLHttpRequest} xhr
27307          */
27308         "exception" : true,
27309         /**
27310          * @event beforeloadcanvas
27311          * Fire before load the canvas
27312          * @param {Roo.bootstrap.UploadCropbox} this
27313          * @param {String} src
27314          */
27315         "beforeloadcanvas" : true,
27316         /**
27317          * @event trash
27318          * Fire when trash image
27319          * @param {Roo.bootstrap.UploadCropbox} this
27320          */
27321         "trash" : true,
27322         /**
27323          * @event download
27324          * Fire when download the image
27325          * @param {Roo.bootstrap.UploadCropbox} this
27326          */
27327         "download" : true,
27328         /**
27329          * @event footerbuttonclick
27330          * Fire when footerbuttonclick
27331          * @param {Roo.bootstrap.UploadCropbox} this
27332          * @param {String} type
27333          */
27334         "footerbuttonclick" : true,
27335         /**
27336          * @event resize
27337          * Fire when resize
27338          * @param {Roo.bootstrap.UploadCropbox} this
27339          */
27340         "resize" : true,
27341         /**
27342          * @event rotate
27343          * Fire when rotate the image
27344          * @param {Roo.bootstrap.UploadCropbox} this
27345          * @param {String} pos
27346          */
27347         "rotate" : true,
27348         /**
27349          * @event inspect
27350          * Fire when inspect the file
27351          * @param {Roo.bootstrap.UploadCropbox} this
27352          * @param {Object} file
27353          */
27354         "inspect" : true,
27355         /**
27356          * @event upload
27357          * Fire when xhr upload the file
27358          * @param {Roo.bootstrap.UploadCropbox} this
27359          * @param {Object} data
27360          */
27361         "upload" : true,
27362         /**
27363          * @event arrange
27364          * Fire when arrange the file data
27365          * @param {Roo.bootstrap.UploadCropbox} this
27366          * @param {Object} formData
27367          */
27368         "arrange" : true
27369     });
27370     
27371     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27372 };
27373
27374 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27375     
27376     emptyText : 'Click to upload image',
27377     rotateNotify : 'Image is too small to rotate',
27378     errorTimeout : 3000,
27379     scale : 0,
27380     baseScale : 1,
27381     rotate : 0,
27382     dragable : false,
27383     pinching : false,
27384     mouseX : 0,
27385     mouseY : 0,
27386     cropData : false,
27387     minWidth : 300,
27388     minHeight : 300,
27389     file : false,
27390     exif : {},
27391     baseRotate : 1,
27392     cropType : 'image/jpeg',
27393     buttons : false,
27394     canvasLoaded : false,
27395     isDocument : false,
27396     method : 'POST',
27397     paramName : 'imageUpload',
27398     loadMask : true,
27399     loadingText : 'Loading...',
27400     maskEl : false,
27401     
27402     getAutoCreate : function()
27403     {
27404         var cfg = {
27405             tag : 'div',
27406             cls : 'roo-upload-cropbox',
27407             cn : [
27408                 {
27409                     tag : 'input',
27410                     cls : 'roo-upload-cropbox-selector',
27411                     type : 'file'
27412                 },
27413                 {
27414                     tag : 'div',
27415                     cls : 'roo-upload-cropbox-body',
27416                     style : 'cursor:pointer',
27417                     cn : [
27418                         {
27419                             tag : 'div',
27420                             cls : 'roo-upload-cropbox-preview'
27421                         },
27422                         {
27423                             tag : 'div',
27424                             cls : 'roo-upload-cropbox-thumb'
27425                         },
27426                         {
27427                             tag : 'div',
27428                             cls : 'roo-upload-cropbox-empty-notify',
27429                             html : this.emptyText
27430                         },
27431                         {
27432                             tag : 'div',
27433                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27434                             html : this.rotateNotify
27435                         }
27436                     ]
27437                 },
27438                 {
27439                     tag : 'div',
27440                     cls : 'roo-upload-cropbox-footer',
27441                     cn : {
27442                         tag : 'div',
27443                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27444                         cn : []
27445                     }
27446                 }
27447             ]
27448         };
27449         
27450         return cfg;
27451     },
27452     
27453     onRender : function(ct, position)
27454     {
27455         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27456         
27457         if (this.buttons.length) {
27458             
27459             Roo.each(this.buttons, function(bb) {
27460                 
27461                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27462                 
27463                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27464                 
27465             }, this);
27466         }
27467         
27468         if(this.loadMask){
27469             this.maskEl = this.el;
27470         }
27471     },
27472     
27473     initEvents : function()
27474     {
27475         this.urlAPI = (window.createObjectURL && window) || 
27476                                 (window.URL && URL.revokeObjectURL && URL) || 
27477                                 (window.webkitURL && webkitURL);
27478                         
27479         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27480         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27481         
27482         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27483         this.selectorEl.hide();
27484         
27485         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27486         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27487         
27488         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27489         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27490         this.thumbEl.hide();
27491         
27492         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27493         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27494         
27495         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27496         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27497         this.errorEl.hide();
27498         
27499         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27500         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27501         this.footerEl.hide();
27502         
27503         this.setThumbBoxSize();
27504         
27505         this.bind();
27506         
27507         this.resize();
27508         
27509         this.fireEvent('initial', this);
27510     },
27511
27512     bind : function()
27513     {
27514         var _this = this;
27515         
27516         window.addEventListener("resize", function() { _this.resize(); } );
27517         
27518         this.bodyEl.on('click', this.beforeSelectFile, this);
27519         
27520         if(Roo.isTouch){
27521             this.bodyEl.on('touchstart', this.onTouchStart, this);
27522             this.bodyEl.on('touchmove', this.onTouchMove, this);
27523             this.bodyEl.on('touchend', this.onTouchEnd, this);
27524         }
27525         
27526         if(!Roo.isTouch){
27527             this.bodyEl.on('mousedown', this.onMouseDown, this);
27528             this.bodyEl.on('mousemove', this.onMouseMove, this);
27529             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27530             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27531             Roo.get(document).on('mouseup', this.onMouseUp, this);
27532         }
27533         
27534         this.selectorEl.on('change', this.onFileSelected, this);
27535     },
27536     
27537     reset : function()
27538     {    
27539         this.scale = 0;
27540         this.baseScale = 1;
27541         this.rotate = 0;
27542         this.baseRotate = 1;
27543         this.dragable = false;
27544         this.pinching = false;
27545         this.mouseX = 0;
27546         this.mouseY = 0;
27547         this.cropData = false;
27548         this.notifyEl.dom.innerHTML = this.emptyText;
27549         
27550         this.selectorEl.dom.value = '';
27551         
27552     },
27553     
27554     resize : function()
27555     {
27556         if(this.fireEvent('resize', this) != false){
27557             this.setThumbBoxPosition();
27558             this.setCanvasPosition();
27559         }
27560     },
27561     
27562     onFooterButtonClick : function(e, el, o, type)
27563     {
27564         switch (type) {
27565             case 'rotate-left' :
27566                 this.onRotateLeft(e);
27567                 break;
27568             case 'rotate-right' :
27569                 this.onRotateRight(e);
27570                 break;
27571             case 'picture' :
27572                 this.beforeSelectFile(e);
27573                 break;
27574             case 'trash' :
27575                 this.trash(e);
27576                 break;
27577             case 'crop' :
27578                 this.crop(e);
27579                 break;
27580             case 'download' :
27581                 this.download(e);
27582                 break;
27583             default :
27584                 break;
27585         }
27586         
27587         this.fireEvent('footerbuttonclick', this, type);
27588     },
27589     
27590     beforeSelectFile : function(e)
27591     {
27592         e.preventDefault();
27593         
27594         if(this.fireEvent('beforeselectfile', this) != false){
27595             this.selectorEl.dom.click();
27596         }
27597     },
27598     
27599     onFileSelected : function(e)
27600     {
27601         e.preventDefault();
27602         
27603         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27604             return;
27605         }
27606         
27607         var file = this.selectorEl.dom.files[0];
27608         
27609         if(this.fireEvent('inspect', this, file) != false){
27610             this.prepare(file);
27611         }
27612         
27613     },
27614     
27615     trash : function(e)
27616     {
27617         this.fireEvent('trash', this);
27618     },
27619     
27620     download : function(e)
27621     {
27622         this.fireEvent('download', this);
27623     },
27624     
27625     loadCanvas : function(src)
27626     {   
27627         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27628             
27629             this.reset();
27630             
27631             this.imageEl = document.createElement('img');
27632             
27633             var _this = this;
27634             
27635             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27636             
27637             this.imageEl.src = src;
27638         }
27639     },
27640     
27641     onLoadCanvas : function()
27642     {   
27643         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27644         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27645         
27646         this.bodyEl.un('click', this.beforeSelectFile, this);
27647         
27648         this.notifyEl.hide();
27649         this.thumbEl.show();
27650         this.footerEl.show();
27651         
27652         this.baseRotateLevel();
27653         
27654         if(this.isDocument){
27655             this.setThumbBoxSize();
27656         }
27657         
27658         this.setThumbBoxPosition();
27659         
27660         this.baseScaleLevel();
27661         
27662         this.draw();
27663         
27664         this.resize();
27665         
27666         this.canvasLoaded = true;
27667         
27668         if(this.loadMask){
27669             this.maskEl.unmask();
27670         }
27671         
27672     },
27673     
27674     setCanvasPosition : function()
27675     {   
27676         if(!this.canvasEl){
27677             return;
27678         }
27679         
27680         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27681         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27682         
27683         this.previewEl.setLeft(pw);
27684         this.previewEl.setTop(ph);
27685         
27686     },
27687     
27688     onMouseDown : function(e)
27689     {   
27690         e.stopEvent();
27691         
27692         this.dragable = true;
27693         this.pinching = false;
27694         
27695         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27696             this.dragable = false;
27697             return;
27698         }
27699         
27700         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27701         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27702         
27703     },
27704     
27705     onMouseMove : function(e)
27706     {   
27707         e.stopEvent();
27708         
27709         if(!this.canvasLoaded){
27710             return;
27711         }
27712         
27713         if (!this.dragable){
27714             return;
27715         }
27716         
27717         var minX = Math.ceil(this.thumbEl.getLeft(true));
27718         var minY = Math.ceil(this.thumbEl.getTop(true));
27719         
27720         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27721         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27722         
27723         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27724         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27725         
27726         x = x - this.mouseX;
27727         y = y - this.mouseY;
27728         
27729         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27730         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27731         
27732         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27733         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27734         
27735         this.previewEl.setLeft(bgX);
27736         this.previewEl.setTop(bgY);
27737         
27738         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27739         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27740     },
27741     
27742     onMouseUp : function(e)
27743     {   
27744         e.stopEvent();
27745         
27746         this.dragable = false;
27747     },
27748     
27749     onMouseWheel : function(e)
27750     {   
27751         e.stopEvent();
27752         
27753         this.startScale = this.scale;
27754         
27755         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27756         
27757         if(!this.zoomable()){
27758             this.scale = this.startScale;
27759             return;
27760         }
27761         
27762         this.draw();
27763         
27764         return;
27765     },
27766     
27767     zoomable : function()
27768     {
27769         var minScale = this.thumbEl.getWidth() / this.minWidth;
27770         
27771         if(this.minWidth < this.minHeight){
27772             minScale = this.thumbEl.getHeight() / this.minHeight;
27773         }
27774         
27775         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27776         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27777         
27778         if(
27779                 this.isDocument &&
27780                 (this.rotate == 0 || this.rotate == 180) && 
27781                 (
27782                     width > this.imageEl.OriginWidth || 
27783                     height > this.imageEl.OriginHeight ||
27784                     (width < this.minWidth && height < this.minHeight)
27785                 )
27786         ){
27787             return false;
27788         }
27789         
27790         if(
27791                 this.isDocument &&
27792                 (this.rotate == 90 || this.rotate == 270) && 
27793                 (
27794                     width > this.imageEl.OriginWidth || 
27795                     height > this.imageEl.OriginHeight ||
27796                     (width < this.minHeight && height < this.minWidth)
27797                 )
27798         ){
27799             return false;
27800         }
27801         
27802         if(
27803                 !this.isDocument &&
27804                 (this.rotate == 0 || this.rotate == 180) && 
27805                 (
27806                     width < this.minWidth || 
27807                     width > this.imageEl.OriginWidth || 
27808                     height < this.minHeight || 
27809                     height > this.imageEl.OriginHeight
27810                 )
27811         ){
27812             return false;
27813         }
27814         
27815         if(
27816                 !this.isDocument &&
27817                 (this.rotate == 90 || this.rotate == 270) && 
27818                 (
27819                     width < this.minHeight || 
27820                     width > this.imageEl.OriginWidth || 
27821                     height < this.minWidth || 
27822                     height > this.imageEl.OriginHeight
27823                 )
27824         ){
27825             return false;
27826         }
27827         
27828         return true;
27829         
27830     },
27831     
27832     onRotateLeft : function(e)
27833     {   
27834         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27835             
27836             var minScale = this.thumbEl.getWidth() / this.minWidth;
27837             
27838             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27839             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27840             
27841             this.startScale = this.scale;
27842             
27843             while (this.getScaleLevel() < minScale){
27844             
27845                 this.scale = this.scale + 1;
27846                 
27847                 if(!this.zoomable()){
27848                     break;
27849                 }
27850                 
27851                 if(
27852                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27853                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27854                 ){
27855                     continue;
27856                 }
27857                 
27858                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27859
27860                 this.draw();
27861                 
27862                 return;
27863             }
27864             
27865             this.scale = this.startScale;
27866             
27867             this.onRotateFail();
27868             
27869             return false;
27870         }
27871         
27872         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27873
27874         if(this.isDocument){
27875             this.setThumbBoxSize();
27876             this.setThumbBoxPosition();
27877             this.setCanvasPosition();
27878         }
27879         
27880         this.draw();
27881         
27882         this.fireEvent('rotate', this, 'left');
27883         
27884     },
27885     
27886     onRotateRight : function(e)
27887     {
27888         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27889             
27890             var minScale = this.thumbEl.getWidth() / this.minWidth;
27891         
27892             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27893             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27894             
27895             this.startScale = this.scale;
27896             
27897             while (this.getScaleLevel() < minScale){
27898             
27899                 this.scale = this.scale + 1;
27900                 
27901                 if(!this.zoomable()){
27902                     break;
27903                 }
27904                 
27905                 if(
27906                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27907                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27908                 ){
27909                     continue;
27910                 }
27911                 
27912                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27913
27914                 this.draw();
27915                 
27916                 return;
27917             }
27918             
27919             this.scale = this.startScale;
27920             
27921             this.onRotateFail();
27922             
27923             return false;
27924         }
27925         
27926         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27927
27928         if(this.isDocument){
27929             this.setThumbBoxSize();
27930             this.setThumbBoxPosition();
27931             this.setCanvasPosition();
27932         }
27933         
27934         this.draw();
27935         
27936         this.fireEvent('rotate', this, 'right');
27937     },
27938     
27939     onRotateFail : function()
27940     {
27941         this.errorEl.show(true);
27942         
27943         var _this = this;
27944         
27945         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27946     },
27947     
27948     draw : function()
27949     {
27950         this.previewEl.dom.innerHTML = '';
27951         
27952         var canvasEl = document.createElement("canvas");
27953         
27954         var contextEl = canvasEl.getContext("2d");
27955         
27956         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27957         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27958         var center = this.imageEl.OriginWidth / 2;
27959         
27960         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27961             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27962             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27963             center = this.imageEl.OriginHeight / 2;
27964         }
27965         
27966         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27967         
27968         contextEl.translate(center, center);
27969         contextEl.rotate(this.rotate * Math.PI / 180);
27970
27971         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27972         
27973         this.canvasEl = document.createElement("canvas");
27974         
27975         this.contextEl = this.canvasEl.getContext("2d");
27976         
27977         switch (this.rotate) {
27978             case 0 :
27979                 
27980                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27981                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27982                 
27983                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27984                 
27985                 break;
27986             case 90 : 
27987                 
27988                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27989                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27990                 
27991                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27992                     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);
27993                     break;
27994                 }
27995                 
27996                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27997                 
27998                 break;
27999             case 180 :
28000                 
28001                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28002                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28003                 
28004                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28005                     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);
28006                     break;
28007                 }
28008                 
28009                 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);
28010                 
28011                 break;
28012             case 270 :
28013                 
28014                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28015                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28016         
28017                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28018                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28019                     break;
28020                 }
28021                 
28022                 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);
28023                 
28024                 break;
28025             default : 
28026                 break;
28027         }
28028         
28029         this.previewEl.appendChild(this.canvasEl);
28030         
28031         this.setCanvasPosition();
28032     },
28033     
28034     crop : function()
28035     {
28036         if(!this.canvasLoaded){
28037             return;
28038         }
28039         
28040         var imageCanvas = document.createElement("canvas");
28041         
28042         var imageContext = imageCanvas.getContext("2d");
28043         
28044         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28045         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28046         
28047         var center = imageCanvas.width / 2;
28048         
28049         imageContext.translate(center, center);
28050         
28051         imageContext.rotate(this.rotate * Math.PI / 180);
28052         
28053         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28054         
28055         var canvas = document.createElement("canvas");
28056         
28057         var context = canvas.getContext("2d");
28058                 
28059         canvas.width = this.minWidth;
28060         canvas.height = this.minHeight;
28061
28062         switch (this.rotate) {
28063             case 0 :
28064                 
28065                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28066                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28067                 
28068                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28069                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28070                 
28071                 var targetWidth = this.minWidth - 2 * x;
28072                 var targetHeight = this.minHeight - 2 * y;
28073                 
28074                 var scale = 1;
28075                 
28076                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28077                     scale = targetWidth / width;
28078                 }
28079                 
28080                 if(x > 0 && y == 0){
28081                     scale = targetHeight / height;
28082                 }
28083                 
28084                 if(x > 0 && y > 0){
28085                     scale = targetWidth / width;
28086                     
28087                     if(width < height){
28088                         scale = targetHeight / height;
28089                     }
28090                 }
28091                 
28092                 context.scale(scale, scale);
28093                 
28094                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28095                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28096
28097                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28098                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28099
28100                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28101                 
28102                 break;
28103             case 90 : 
28104                 
28105                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28106                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28107                 
28108                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28109                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28110                 
28111                 var targetWidth = this.minWidth - 2 * x;
28112                 var targetHeight = this.minHeight - 2 * y;
28113                 
28114                 var scale = 1;
28115                 
28116                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28117                     scale = targetWidth / width;
28118                 }
28119                 
28120                 if(x > 0 && y == 0){
28121                     scale = targetHeight / height;
28122                 }
28123                 
28124                 if(x > 0 && y > 0){
28125                     scale = targetWidth / width;
28126                     
28127                     if(width < height){
28128                         scale = targetHeight / height;
28129                     }
28130                 }
28131                 
28132                 context.scale(scale, scale);
28133                 
28134                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28135                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28136
28137                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28138                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28139                 
28140                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28141                 
28142                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28143                 
28144                 break;
28145             case 180 :
28146                 
28147                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28148                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28149                 
28150                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28151                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28152                 
28153                 var targetWidth = this.minWidth - 2 * x;
28154                 var targetHeight = this.minHeight - 2 * y;
28155                 
28156                 var scale = 1;
28157                 
28158                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28159                     scale = targetWidth / width;
28160                 }
28161                 
28162                 if(x > 0 && y == 0){
28163                     scale = targetHeight / height;
28164                 }
28165                 
28166                 if(x > 0 && y > 0){
28167                     scale = targetWidth / width;
28168                     
28169                     if(width < height){
28170                         scale = targetHeight / height;
28171                     }
28172                 }
28173                 
28174                 context.scale(scale, scale);
28175                 
28176                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28177                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28178
28179                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28180                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28181
28182                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28183                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28184                 
28185                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28186                 
28187                 break;
28188             case 270 :
28189                 
28190                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28191                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28192                 
28193                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28194                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28195                 
28196                 var targetWidth = this.minWidth - 2 * x;
28197                 var targetHeight = this.minHeight - 2 * y;
28198                 
28199                 var scale = 1;
28200                 
28201                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28202                     scale = targetWidth / width;
28203                 }
28204                 
28205                 if(x > 0 && y == 0){
28206                     scale = targetHeight / height;
28207                 }
28208                 
28209                 if(x > 0 && y > 0){
28210                     scale = targetWidth / width;
28211                     
28212                     if(width < height){
28213                         scale = targetHeight / height;
28214                     }
28215                 }
28216                 
28217                 context.scale(scale, scale);
28218                 
28219                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28220                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28221
28222                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28223                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28224                 
28225                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28226                 
28227                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28228                 
28229                 break;
28230             default : 
28231                 break;
28232         }
28233         
28234         this.cropData = canvas.toDataURL(this.cropType);
28235         
28236         if(this.fireEvent('crop', this, this.cropData) !== false){
28237             this.process(this.file, this.cropData);
28238         }
28239         
28240         return;
28241         
28242     },
28243     
28244     setThumbBoxSize : function()
28245     {
28246         var width, height;
28247         
28248         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28249             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28250             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28251             
28252             this.minWidth = width;
28253             this.minHeight = height;
28254             
28255             if(this.rotate == 90 || this.rotate == 270){
28256                 this.minWidth = height;
28257                 this.minHeight = width;
28258             }
28259         }
28260         
28261         height = 300;
28262         width = Math.ceil(this.minWidth * height / this.minHeight);
28263         
28264         if(this.minWidth > this.minHeight){
28265             width = 300;
28266             height = Math.ceil(this.minHeight * width / this.minWidth);
28267         }
28268         
28269         this.thumbEl.setStyle({
28270             width : width + 'px',
28271             height : height + 'px'
28272         });
28273
28274         return;
28275             
28276     },
28277     
28278     setThumbBoxPosition : function()
28279     {
28280         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28281         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28282         
28283         this.thumbEl.setLeft(x);
28284         this.thumbEl.setTop(y);
28285         
28286     },
28287     
28288     baseRotateLevel : function()
28289     {
28290         this.baseRotate = 1;
28291         
28292         if(
28293                 typeof(this.exif) != 'undefined' &&
28294                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28295                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28296         ){
28297             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28298         }
28299         
28300         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28301         
28302     },
28303     
28304     baseScaleLevel : function()
28305     {
28306         var width, height;
28307         
28308         if(this.isDocument){
28309             
28310             if(this.baseRotate == 6 || this.baseRotate == 8){
28311             
28312                 height = this.thumbEl.getHeight();
28313                 this.baseScale = height / this.imageEl.OriginWidth;
28314
28315                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28316                     width = this.thumbEl.getWidth();
28317                     this.baseScale = width / this.imageEl.OriginHeight;
28318                 }
28319
28320                 return;
28321             }
28322
28323             height = this.thumbEl.getHeight();
28324             this.baseScale = height / this.imageEl.OriginHeight;
28325
28326             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28327                 width = this.thumbEl.getWidth();
28328                 this.baseScale = width / this.imageEl.OriginWidth;
28329             }
28330
28331             return;
28332         }
28333         
28334         if(this.baseRotate == 6 || this.baseRotate == 8){
28335             
28336             width = this.thumbEl.getHeight();
28337             this.baseScale = width / this.imageEl.OriginHeight;
28338             
28339             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28340                 height = this.thumbEl.getWidth();
28341                 this.baseScale = height / this.imageEl.OriginHeight;
28342             }
28343             
28344             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28345                 height = this.thumbEl.getWidth();
28346                 this.baseScale = height / this.imageEl.OriginHeight;
28347                 
28348                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28349                     width = this.thumbEl.getHeight();
28350                     this.baseScale = width / this.imageEl.OriginWidth;
28351                 }
28352             }
28353             
28354             return;
28355         }
28356         
28357         width = this.thumbEl.getWidth();
28358         this.baseScale = width / this.imageEl.OriginWidth;
28359         
28360         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28361             height = this.thumbEl.getHeight();
28362             this.baseScale = height / this.imageEl.OriginHeight;
28363         }
28364         
28365         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28366             
28367             height = this.thumbEl.getHeight();
28368             this.baseScale = height / this.imageEl.OriginHeight;
28369             
28370             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28371                 width = this.thumbEl.getWidth();
28372                 this.baseScale = width / this.imageEl.OriginWidth;
28373             }
28374             
28375         }
28376         
28377         return;
28378     },
28379     
28380     getScaleLevel : function()
28381     {
28382         return this.baseScale * Math.pow(1.1, this.scale);
28383     },
28384     
28385     onTouchStart : function(e)
28386     {
28387         if(!this.canvasLoaded){
28388             this.beforeSelectFile(e);
28389             return;
28390         }
28391         
28392         var touches = e.browserEvent.touches;
28393         
28394         if(!touches){
28395             return;
28396         }
28397         
28398         if(touches.length == 1){
28399             this.onMouseDown(e);
28400             return;
28401         }
28402         
28403         if(touches.length != 2){
28404             return;
28405         }
28406         
28407         var coords = [];
28408         
28409         for(var i = 0, finger; finger = touches[i]; i++){
28410             coords.push(finger.pageX, finger.pageY);
28411         }
28412         
28413         var x = Math.pow(coords[0] - coords[2], 2);
28414         var y = Math.pow(coords[1] - coords[3], 2);
28415         
28416         this.startDistance = Math.sqrt(x + y);
28417         
28418         this.startScale = this.scale;
28419         
28420         this.pinching = true;
28421         this.dragable = false;
28422         
28423     },
28424     
28425     onTouchMove : function(e)
28426     {
28427         if(!this.pinching && !this.dragable){
28428             return;
28429         }
28430         
28431         var touches = e.browserEvent.touches;
28432         
28433         if(!touches){
28434             return;
28435         }
28436         
28437         if(this.dragable){
28438             this.onMouseMove(e);
28439             return;
28440         }
28441         
28442         var coords = [];
28443         
28444         for(var i = 0, finger; finger = touches[i]; i++){
28445             coords.push(finger.pageX, finger.pageY);
28446         }
28447         
28448         var x = Math.pow(coords[0] - coords[2], 2);
28449         var y = Math.pow(coords[1] - coords[3], 2);
28450         
28451         this.endDistance = Math.sqrt(x + y);
28452         
28453         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28454         
28455         if(!this.zoomable()){
28456             this.scale = this.startScale;
28457             return;
28458         }
28459         
28460         this.draw();
28461         
28462     },
28463     
28464     onTouchEnd : function(e)
28465     {
28466         this.pinching = false;
28467         this.dragable = false;
28468         
28469     },
28470     
28471     process : function(file, crop)
28472     {
28473         if(this.loadMask){
28474             this.maskEl.mask(this.loadingText);
28475         }
28476         
28477         this.xhr = new XMLHttpRequest();
28478         
28479         file.xhr = this.xhr;
28480
28481         this.xhr.open(this.method, this.url, true);
28482         
28483         var headers = {
28484             "Accept": "application/json",
28485             "Cache-Control": "no-cache",
28486             "X-Requested-With": "XMLHttpRequest"
28487         };
28488         
28489         for (var headerName in headers) {
28490             var headerValue = headers[headerName];
28491             if (headerValue) {
28492                 this.xhr.setRequestHeader(headerName, headerValue);
28493             }
28494         }
28495         
28496         var _this = this;
28497         
28498         this.xhr.onload = function()
28499         {
28500             _this.xhrOnLoad(_this.xhr);
28501         }
28502         
28503         this.xhr.onerror = function()
28504         {
28505             _this.xhrOnError(_this.xhr);
28506         }
28507         
28508         var formData = new FormData();
28509
28510         formData.append('returnHTML', 'NO');
28511         
28512         if(crop){
28513             formData.append('crop', crop);
28514         }
28515         
28516         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28517             formData.append(this.paramName, file, file.name);
28518         }
28519         
28520         if(typeof(file.filename) != 'undefined'){
28521             formData.append('filename', file.filename);
28522         }
28523         
28524         if(typeof(file.mimetype) != 'undefined'){
28525             formData.append('mimetype', file.mimetype);
28526         }
28527         
28528         if(this.fireEvent('arrange', this, formData) != false){
28529             this.xhr.send(formData);
28530         };
28531     },
28532     
28533     xhrOnLoad : function(xhr)
28534     {
28535         if(this.loadMask){
28536             this.maskEl.unmask();
28537         }
28538         
28539         if (xhr.readyState !== 4) {
28540             this.fireEvent('exception', this, xhr);
28541             return;
28542         }
28543
28544         var response = Roo.decode(xhr.responseText);
28545         
28546         if(!response.success){
28547             this.fireEvent('exception', this, xhr);
28548             return;
28549         }
28550         
28551         var response = Roo.decode(xhr.responseText);
28552         
28553         this.fireEvent('upload', this, response);
28554         
28555     },
28556     
28557     xhrOnError : function()
28558     {
28559         if(this.loadMask){
28560             this.maskEl.unmask();
28561         }
28562         
28563         Roo.log('xhr on error');
28564         
28565         var response = Roo.decode(xhr.responseText);
28566           
28567         Roo.log(response);
28568         
28569     },
28570     
28571     prepare : function(file)
28572     {   
28573         if(this.loadMask){
28574             this.maskEl.mask(this.loadingText);
28575         }
28576         
28577         this.file = false;
28578         this.exif = {};
28579         
28580         if(typeof(file) === 'string'){
28581             this.loadCanvas(file);
28582             return;
28583         }
28584         
28585         if(!file || !this.urlAPI){
28586             return;
28587         }
28588         
28589         this.file = file;
28590         this.cropType = file.type;
28591         
28592         var _this = this;
28593         
28594         if(this.fireEvent('prepare', this, this.file) != false){
28595             
28596             var reader = new FileReader();
28597             
28598             reader.onload = function (e) {
28599                 if (e.target.error) {
28600                     Roo.log(e.target.error);
28601                     return;
28602                 }
28603                 
28604                 var buffer = e.target.result,
28605                     dataView = new DataView(buffer),
28606                     offset = 2,
28607                     maxOffset = dataView.byteLength - 4,
28608                     markerBytes,
28609                     markerLength;
28610                 
28611                 if (dataView.getUint16(0) === 0xffd8) {
28612                     while (offset < maxOffset) {
28613                         markerBytes = dataView.getUint16(offset);
28614                         
28615                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28616                             markerLength = dataView.getUint16(offset + 2) + 2;
28617                             if (offset + markerLength > dataView.byteLength) {
28618                                 Roo.log('Invalid meta data: Invalid segment size.');
28619                                 break;
28620                             }
28621                             
28622                             if(markerBytes == 0xffe1){
28623                                 _this.parseExifData(
28624                                     dataView,
28625                                     offset,
28626                                     markerLength
28627                                 );
28628                             }
28629                             
28630                             offset += markerLength;
28631                             
28632                             continue;
28633                         }
28634                         
28635                         break;
28636                     }
28637                     
28638                 }
28639                 
28640                 var url = _this.urlAPI.createObjectURL(_this.file);
28641                 
28642                 _this.loadCanvas(url);
28643                 
28644                 return;
28645             }
28646             
28647             reader.readAsArrayBuffer(this.file);
28648             
28649         }
28650         
28651     },
28652     
28653     parseExifData : function(dataView, offset, length)
28654     {
28655         var tiffOffset = offset + 10,
28656             littleEndian,
28657             dirOffset;
28658     
28659         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28660             // No Exif data, might be XMP data instead
28661             return;
28662         }
28663         
28664         // Check for the ASCII code for "Exif" (0x45786966):
28665         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28666             // No Exif data, might be XMP data instead
28667             return;
28668         }
28669         if (tiffOffset + 8 > dataView.byteLength) {
28670             Roo.log('Invalid Exif data: Invalid segment size.');
28671             return;
28672         }
28673         // Check for the two null bytes:
28674         if (dataView.getUint16(offset + 8) !== 0x0000) {
28675             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28676             return;
28677         }
28678         // Check the byte alignment:
28679         switch (dataView.getUint16(tiffOffset)) {
28680         case 0x4949:
28681             littleEndian = true;
28682             break;
28683         case 0x4D4D:
28684             littleEndian = false;
28685             break;
28686         default:
28687             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28688             return;
28689         }
28690         // Check for the TIFF tag marker (0x002A):
28691         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28692             Roo.log('Invalid Exif data: Missing TIFF marker.');
28693             return;
28694         }
28695         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28696         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28697         
28698         this.parseExifTags(
28699             dataView,
28700             tiffOffset,
28701             tiffOffset + dirOffset,
28702             littleEndian
28703         );
28704     },
28705     
28706     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28707     {
28708         var tagsNumber,
28709             dirEndOffset,
28710             i;
28711         if (dirOffset + 6 > dataView.byteLength) {
28712             Roo.log('Invalid Exif data: Invalid directory offset.');
28713             return;
28714         }
28715         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28716         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28717         if (dirEndOffset + 4 > dataView.byteLength) {
28718             Roo.log('Invalid Exif data: Invalid directory size.');
28719             return;
28720         }
28721         for (i = 0; i < tagsNumber; i += 1) {
28722             this.parseExifTag(
28723                 dataView,
28724                 tiffOffset,
28725                 dirOffset + 2 + 12 * i, // tag offset
28726                 littleEndian
28727             );
28728         }
28729         // Return the offset to the next directory:
28730         return dataView.getUint32(dirEndOffset, littleEndian);
28731     },
28732     
28733     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28734     {
28735         var tag = dataView.getUint16(offset, littleEndian);
28736         
28737         this.exif[tag] = this.getExifValue(
28738             dataView,
28739             tiffOffset,
28740             offset,
28741             dataView.getUint16(offset + 2, littleEndian), // tag type
28742             dataView.getUint32(offset + 4, littleEndian), // tag length
28743             littleEndian
28744         );
28745     },
28746     
28747     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28748     {
28749         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28750             tagSize,
28751             dataOffset,
28752             values,
28753             i,
28754             str,
28755             c;
28756     
28757         if (!tagType) {
28758             Roo.log('Invalid Exif data: Invalid tag type.');
28759             return;
28760         }
28761         
28762         tagSize = tagType.size * length;
28763         // Determine if the value is contained in the dataOffset bytes,
28764         // or if the value at the dataOffset is a pointer to the actual data:
28765         dataOffset = tagSize > 4 ?
28766                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28767         if (dataOffset + tagSize > dataView.byteLength) {
28768             Roo.log('Invalid Exif data: Invalid data offset.');
28769             return;
28770         }
28771         if (length === 1) {
28772             return tagType.getValue(dataView, dataOffset, littleEndian);
28773         }
28774         values = [];
28775         for (i = 0; i < length; i += 1) {
28776             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28777         }
28778         
28779         if (tagType.ascii) {
28780             str = '';
28781             // Concatenate the chars:
28782             for (i = 0; i < values.length; i += 1) {
28783                 c = values[i];
28784                 // Ignore the terminating NULL byte(s):
28785                 if (c === '\u0000') {
28786                     break;
28787                 }
28788                 str += c;
28789             }
28790             return str;
28791         }
28792         return values;
28793     }
28794     
28795 });
28796
28797 Roo.apply(Roo.bootstrap.UploadCropbox, {
28798     tags : {
28799         'Orientation': 0x0112
28800     },
28801     
28802     Orientation: {
28803             1: 0, //'top-left',
28804 //            2: 'top-right',
28805             3: 180, //'bottom-right',
28806 //            4: 'bottom-left',
28807 //            5: 'left-top',
28808             6: 90, //'right-top',
28809 //            7: 'right-bottom',
28810             8: 270 //'left-bottom'
28811     },
28812     
28813     exifTagTypes : {
28814         // byte, 8-bit unsigned int:
28815         1: {
28816             getValue: function (dataView, dataOffset) {
28817                 return dataView.getUint8(dataOffset);
28818             },
28819             size: 1
28820         },
28821         // ascii, 8-bit byte:
28822         2: {
28823             getValue: function (dataView, dataOffset) {
28824                 return String.fromCharCode(dataView.getUint8(dataOffset));
28825             },
28826             size: 1,
28827             ascii: true
28828         },
28829         // short, 16 bit int:
28830         3: {
28831             getValue: function (dataView, dataOffset, littleEndian) {
28832                 return dataView.getUint16(dataOffset, littleEndian);
28833             },
28834             size: 2
28835         },
28836         // long, 32 bit int:
28837         4: {
28838             getValue: function (dataView, dataOffset, littleEndian) {
28839                 return dataView.getUint32(dataOffset, littleEndian);
28840             },
28841             size: 4
28842         },
28843         // rational = two long values, first is numerator, second is denominator:
28844         5: {
28845             getValue: function (dataView, dataOffset, littleEndian) {
28846                 return dataView.getUint32(dataOffset, littleEndian) /
28847                     dataView.getUint32(dataOffset + 4, littleEndian);
28848             },
28849             size: 8
28850         },
28851         // slong, 32 bit signed int:
28852         9: {
28853             getValue: function (dataView, dataOffset, littleEndian) {
28854                 return dataView.getInt32(dataOffset, littleEndian);
28855             },
28856             size: 4
28857         },
28858         // srational, two slongs, first is numerator, second is denominator:
28859         10: {
28860             getValue: function (dataView, dataOffset, littleEndian) {
28861                 return dataView.getInt32(dataOffset, littleEndian) /
28862                     dataView.getInt32(dataOffset + 4, littleEndian);
28863             },
28864             size: 8
28865         }
28866     },
28867     
28868     footer : {
28869         STANDARD : [
28870             {
28871                 tag : 'div',
28872                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28873                 action : 'rotate-left',
28874                 cn : [
28875                     {
28876                         tag : 'button',
28877                         cls : 'btn btn-default',
28878                         html : '<i class="fa fa-undo"></i>'
28879                     }
28880                 ]
28881             },
28882             {
28883                 tag : 'div',
28884                 cls : 'btn-group roo-upload-cropbox-picture',
28885                 action : 'picture',
28886                 cn : [
28887                     {
28888                         tag : 'button',
28889                         cls : 'btn btn-default',
28890                         html : '<i class="fa fa-picture-o"></i>'
28891                     }
28892                 ]
28893             },
28894             {
28895                 tag : 'div',
28896                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28897                 action : 'rotate-right',
28898                 cn : [
28899                     {
28900                         tag : 'button',
28901                         cls : 'btn btn-default',
28902                         html : '<i class="fa fa-repeat"></i>'
28903                     }
28904                 ]
28905             }
28906         ],
28907         DOCUMENT : [
28908             {
28909                 tag : 'div',
28910                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28911                 action : 'rotate-left',
28912                 cn : [
28913                     {
28914                         tag : 'button',
28915                         cls : 'btn btn-default',
28916                         html : '<i class="fa fa-undo"></i>'
28917                     }
28918                 ]
28919             },
28920             {
28921                 tag : 'div',
28922                 cls : 'btn-group roo-upload-cropbox-download',
28923                 action : 'download',
28924                 cn : [
28925                     {
28926                         tag : 'button',
28927                         cls : 'btn btn-default',
28928                         html : '<i class="fa fa-download"></i>'
28929                     }
28930                 ]
28931             },
28932             {
28933                 tag : 'div',
28934                 cls : 'btn-group roo-upload-cropbox-crop',
28935                 action : 'crop',
28936                 cn : [
28937                     {
28938                         tag : 'button',
28939                         cls : 'btn btn-default',
28940                         html : '<i class="fa fa-crop"></i>'
28941                     }
28942                 ]
28943             },
28944             {
28945                 tag : 'div',
28946                 cls : 'btn-group roo-upload-cropbox-trash',
28947                 action : 'trash',
28948                 cn : [
28949                     {
28950                         tag : 'button',
28951                         cls : 'btn btn-default',
28952                         html : '<i class="fa fa-trash"></i>'
28953                     }
28954                 ]
28955             },
28956             {
28957                 tag : 'div',
28958                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28959                 action : 'rotate-right',
28960                 cn : [
28961                     {
28962                         tag : 'button',
28963                         cls : 'btn btn-default',
28964                         html : '<i class="fa fa-repeat"></i>'
28965                     }
28966                 ]
28967             }
28968         ],
28969         ROTATOR : [
28970             {
28971                 tag : 'div',
28972                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28973                 action : 'rotate-left',
28974                 cn : [
28975                     {
28976                         tag : 'button',
28977                         cls : 'btn btn-default',
28978                         html : '<i class="fa fa-undo"></i>'
28979                     }
28980                 ]
28981             },
28982             {
28983                 tag : 'div',
28984                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28985                 action : 'rotate-right',
28986                 cn : [
28987                     {
28988                         tag : 'button',
28989                         cls : 'btn btn-default',
28990                         html : '<i class="fa fa-repeat"></i>'
28991                     }
28992                 ]
28993             }
28994         ]
28995     }
28996 });
28997
28998 /*
28999 * Licence: LGPL
29000 */
29001
29002 /**
29003  * @class Roo.bootstrap.DocumentManager
29004  * @extends Roo.bootstrap.Component
29005  * Bootstrap DocumentManager class
29006  * @cfg {String} paramName default 'imageUpload'
29007  * @cfg {String} toolTipName default 'filename'
29008  * @cfg {String} method default POST
29009  * @cfg {String} url action url
29010  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29011  * @cfg {Boolean} multiple multiple upload default true
29012  * @cfg {Number} thumbSize default 300
29013  * @cfg {String} fieldLabel
29014  * @cfg {Number} labelWidth default 4
29015  * @cfg {String} labelAlign (left|top) default left
29016  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29017 * @cfg {Number} labellg set the width of label (1-12)
29018  * @cfg {Number} labelmd set the width of label (1-12)
29019  * @cfg {Number} labelsm set the width of label (1-12)
29020  * @cfg {Number} labelxs set the width of label (1-12)
29021  * 
29022  * @constructor
29023  * Create a new DocumentManager
29024  * @param {Object} config The config object
29025  */
29026
29027 Roo.bootstrap.DocumentManager = function(config){
29028     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29029     
29030     this.files = [];
29031     this.delegates = [];
29032     
29033     this.addEvents({
29034         /**
29035          * @event initial
29036          * Fire when initial the DocumentManager
29037          * @param {Roo.bootstrap.DocumentManager} this
29038          */
29039         "initial" : true,
29040         /**
29041          * @event inspect
29042          * inspect selected file
29043          * @param {Roo.bootstrap.DocumentManager} this
29044          * @param {File} file
29045          */
29046         "inspect" : true,
29047         /**
29048          * @event exception
29049          * Fire when xhr load exception
29050          * @param {Roo.bootstrap.DocumentManager} this
29051          * @param {XMLHttpRequest} xhr
29052          */
29053         "exception" : true,
29054         /**
29055          * @event afterupload
29056          * Fire when xhr load exception
29057          * @param {Roo.bootstrap.DocumentManager} this
29058          * @param {XMLHttpRequest} xhr
29059          */
29060         "afterupload" : true,
29061         /**
29062          * @event prepare
29063          * prepare the form data
29064          * @param {Roo.bootstrap.DocumentManager} this
29065          * @param {Object} formData
29066          */
29067         "prepare" : true,
29068         /**
29069          * @event remove
29070          * Fire when remove the file
29071          * @param {Roo.bootstrap.DocumentManager} this
29072          * @param {Object} file
29073          */
29074         "remove" : true,
29075         /**
29076          * @event refresh
29077          * Fire after refresh the file
29078          * @param {Roo.bootstrap.DocumentManager} this
29079          */
29080         "refresh" : true,
29081         /**
29082          * @event click
29083          * Fire after click the image
29084          * @param {Roo.bootstrap.DocumentManager} this
29085          * @param {Object} file
29086          */
29087         "click" : true,
29088         /**
29089          * @event edit
29090          * Fire when upload a image and editable set to true
29091          * @param {Roo.bootstrap.DocumentManager} this
29092          * @param {Object} file
29093          */
29094         "edit" : true,
29095         /**
29096          * @event beforeselectfile
29097          * Fire before select file
29098          * @param {Roo.bootstrap.DocumentManager} this
29099          */
29100         "beforeselectfile" : true,
29101         /**
29102          * @event process
29103          * Fire before process file
29104          * @param {Roo.bootstrap.DocumentManager} this
29105          * @param {Object} file
29106          */
29107         "process" : true,
29108         /**
29109          * @event previewrendered
29110          * Fire when preview rendered
29111          * @param {Roo.bootstrap.DocumentManager} this
29112          * @param {Object} file
29113          */
29114         "previewrendered" : true,
29115         /**
29116          */
29117         "previewResize" : true
29118         
29119     });
29120 };
29121
29122 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29123     
29124     boxes : 0,
29125     inputName : '',
29126     thumbSize : 300,
29127     multiple : true,
29128     files : false,
29129     method : 'POST',
29130     url : '',
29131     paramName : 'imageUpload',
29132     toolTipName : 'filename',
29133     fieldLabel : '',
29134     labelWidth : 4,
29135     labelAlign : 'left',
29136     editable : true,
29137     delegates : false,
29138     xhr : false, 
29139     
29140     labellg : 0,
29141     labelmd : 0,
29142     labelsm : 0,
29143     labelxs : 0,
29144     
29145     getAutoCreate : function()
29146     {   
29147         var managerWidget = {
29148             tag : 'div',
29149             cls : 'roo-document-manager',
29150             cn : [
29151                 {
29152                     tag : 'input',
29153                     cls : 'roo-document-manager-selector',
29154                     type : 'file'
29155                 },
29156                 {
29157                     tag : 'div',
29158                     cls : 'roo-document-manager-uploader',
29159                     cn : [
29160                         {
29161                             tag : 'div',
29162                             cls : 'roo-document-manager-upload-btn',
29163                             html : '<i class="fa fa-plus"></i>'
29164                         }
29165                     ]
29166                     
29167                 }
29168             ]
29169         };
29170         
29171         var content = [
29172             {
29173                 tag : 'div',
29174                 cls : 'column col-md-12',
29175                 cn : managerWidget
29176             }
29177         ];
29178         
29179         if(this.fieldLabel.length){
29180             
29181             content = [
29182                 {
29183                     tag : 'div',
29184                     cls : 'column col-md-12',
29185                     html : this.fieldLabel
29186                 },
29187                 {
29188                     tag : 'div',
29189                     cls : 'column col-md-12',
29190                     cn : managerWidget
29191                 }
29192             ];
29193
29194             if(this.labelAlign == 'left'){
29195                 content = [
29196                     {
29197                         tag : 'div',
29198                         cls : 'column',
29199                         html : this.fieldLabel
29200                     },
29201                     {
29202                         tag : 'div',
29203                         cls : 'column',
29204                         cn : managerWidget
29205                     }
29206                 ];
29207                 
29208                 if(this.labelWidth > 12){
29209                     content[0].style = "width: " + this.labelWidth + 'px';
29210                 }
29211
29212                 if(this.labelWidth < 13 && this.labelmd == 0){
29213                     this.labelmd = this.labelWidth;
29214                 }
29215
29216                 if(this.labellg > 0){
29217                     content[0].cls += ' col-lg-' + this.labellg;
29218                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29219                 }
29220
29221                 if(this.labelmd > 0){
29222                     content[0].cls += ' col-md-' + this.labelmd;
29223                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29224                 }
29225
29226                 if(this.labelsm > 0){
29227                     content[0].cls += ' col-sm-' + this.labelsm;
29228                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29229                 }
29230
29231                 if(this.labelxs > 0){
29232                     content[0].cls += ' col-xs-' + this.labelxs;
29233                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29234                 }
29235                 
29236             }
29237         }
29238         
29239         var cfg = {
29240             tag : 'div',
29241             cls : 'row clearfix',
29242             cn : content
29243         };
29244         
29245         return cfg;
29246         
29247     },
29248     
29249     initEvents : function()
29250     {
29251         this.managerEl = this.el.select('.roo-document-manager', true).first();
29252         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29253         
29254         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29255         this.selectorEl.hide();
29256         
29257         if(this.multiple){
29258             this.selectorEl.attr('multiple', 'multiple');
29259         }
29260         
29261         this.selectorEl.on('change', this.onFileSelected, this);
29262         
29263         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29264         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29265         
29266         this.uploader.on('click', this.onUploaderClick, this);
29267         
29268         this.renderProgressDialog();
29269         
29270         var _this = this;
29271         
29272         window.addEventListener("resize", function() { _this.refresh(); } );
29273         
29274         this.fireEvent('initial', this);
29275     },
29276     
29277     renderProgressDialog : function()
29278     {
29279         var _this = this;
29280         
29281         this.progressDialog = new Roo.bootstrap.Modal({
29282             cls : 'roo-document-manager-progress-dialog',
29283             allow_close : false,
29284             animate : false,
29285             title : '',
29286             buttons : [
29287                 {
29288                     name  :'cancel',
29289                     weight : 'danger',
29290                     html : 'Cancel'
29291                 }
29292             ], 
29293             listeners : { 
29294                 btnclick : function() {
29295                     _this.uploadCancel();
29296                     this.hide();
29297                 }
29298             }
29299         });
29300          
29301         this.progressDialog.render(Roo.get(document.body));
29302          
29303         this.progress = new Roo.bootstrap.Progress({
29304             cls : 'roo-document-manager-progress',
29305             active : true,
29306             striped : true
29307         });
29308         
29309         this.progress.render(this.progressDialog.getChildContainer());
29310         
29311         this.progressBar = new Roo.bootstrap.ProgressBar({
29312             cls : 'roo-document-manager-progress-bar',
29313             aria_valuenow : 0,
29314             aria_valuemin : 0,
29315             aria_valuemax : 12,
29316             panel : 'success'
29317         });
29318         
29319         this.progressBar.render(this.progress.getChildContainer());
29320     },
29321     
29322     onUploaderClick : function(e)
29323     {
29324         e.preventDefault();
29325      
29326         if(this.fireEvent('beforeselectfile', this) != false){
29327             this.selectorEl.dom.click();
29328         }
29329         
29330     },
29331     
29332     onFileSelected : function(e)
29333     {
29334         e.preventDefault();
29335         
29336         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29337             return;
29338         }
29339         
29340         Roo.each(this.selectorEl.dom.files, function(file){
29341             if(this.fireEvent('inspect', this, file) != false){
29342                 this.files.push(file);
29343             }
29344         }, this);
29345         
29346         this.queue();
29347         
29348     },
29349     
29350     queue : function()
29351     {
29352         this.selectorEl.dom.value = '';
29353         
29354         if(!this.files || !this.files.length){
29355             return;
29356         }
29357         
29358         if(this.boxes > 0 && this.files.length > this.boxes){
29359             this.files = this.files.slice(0, this.boxes);
29360         }
29361         
29362         this.uploader.show();
29363         
29364         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29365             this.uploader.hide();
29366         }
29367         
29368         var _this = this;
29369         
29370         var files = [];
29371         
29372         var docs = [];
29373         
29374         Roo.each(this.files, function(file){
29375             
29376             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29377                 var f = this.renderPreview(file);
29378                 files.push(f);
29379                 return;
29380             }
29381             
29382             if(file.type.indexOf('image') != -1){
29383                 this.delegates.push(
29384                     (function(){
29385                         _this.process(file);
29386                     }).createDelegate(this)
29387                 );
29388         
29389                 return;
29390             }
29391             
29392             docs.push(
29393                 (function(){
29394                     _this.process(file);
29395                 }).createDelegate(this)
29396             );
29397             
29398         }, this);
29399         
29400         this.files = files;
29401         
29402         this.delegates = this.delegates.concat(docs);
29403         
29404         if(!this.delegates.length){
29405             this.refresh();
29406             return;
29407         }
29408         
29409         this.progressBar.aria_valuemax = this.delegates.length;
29410         
29411         this.arrange();
29412         
29413         return;
29414     },
29415     
29416     arrange : function()
29417     {
29418         if(!this.delegates.length){
29419             this.progressDialog.hide();
29420             this.refresh();
29421             return;
29422         }
29423         
29424         var delegate = this.delegates.shift();
29425         
29426         this.progressDialog.show();
29427         
29428         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29429         
29430         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29431         
29432         delegate();
29433     },
29434     
29435     refresh : function()
29436     {
29437         this.uploader.show();
29438         
29439         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29440             this.uploader.hide();
29441         }
29442         
29443         Roo.isTouch ? this.closable(false) : this.closable(true);
29444         
29445         this.fireEvent('refresh', this);
29446     },
29447     
29448     onRemove : function(e, el, o)
29449     {
29450         e.preventDefault();
29451         
29452         this.fireEvent('remove', this, o);
29453         
29454     },
29455     
29456     remove : function(o)
29457     {
29458         var files = [];
29459         
29460         Roo.each(this.files, function(file){
29461             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29462                 files.push(file);
29463                 return;
29464             }
29465
29466             o.target.remove();
29467
29468         }, this);
29469         
29470         this.files = files;
29471         
29472         this.refresh();
29473     },
29474     
29475     clear : function()
29476     {
29477         Roo.each(this.files, function(file){
29478             if(!file.target){
29479                 return;
29480             }
29481             
29482             file.target.remove();
29483
29484         }, this);
29485         
29486         this.files = [];
29487         
29488         this.refresh();
29489     },
29490     
29491     onClick : function(e, el, o)
29492     {
29493         e.preventDefault();
29494         
29495         this.fireEvent('click', this, o);
29496         
29497     },
29498     
29499     closable : function(closable)
29500     {
29501         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29502             
29503             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29504             
29505             if(closable){
29506                 el.show();
29507                 return;
29508             }
29509             
29510             el.hide();
29511             
29512         }, this);
29513     },
29514     
29515     xhrOnLoad : function(xhr)
29516     {
29517         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29518             el.remove();
29519         }, this);
29520         
29521         if (xhr.readyState !== 4) {
29522             this.arrange();
29523             this.fireEvent('exception', this, xhr);
29524             return;
29525         }
29526
29527         var response = Roo.decode(xhr.responseText);
29528         
29529         if(!response.success){
29530             this.arrange();
29531             this.fireEvent('exception', this, xhr);
29532             return;
29533         }
29534         
29535         var file = this.renderPreview(response.data);
29536         
29537         this.files.push(file);
29538         
29539         this.arrange();
29540         
29541         this.fireEvent('afterupload', this, xhr);
29542         
29543     },
29544     
29545     xhrOnError : function(xhr)
29546     {
29547         Roo.log('xhr on error');
29548         
29549         var response = Roo.decode(xhr.responseText);
29550           
29551         Roo.log(response);
29552         
29553         this.arrange();
29554     },
29555     
29556     process : function(file)
29557     {
29558         if(this.fireEvent('process', this, file) !== false){
29559             if(this.editable && file.type.indexOf('image') != -1){
29560                 this.fireEvent('edit', this, file);
29561                 return;
29562             }
29563
29564             this.uploadStart(file, false);
29565
29566             return;
29567         }
29568         
29569     },
29570     
29571     uploadStart : function(file, crop)
29572     {
29573         this.xhr = new XMLHttpRequest();
29574         
29575         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29576             this.arrange();
29577             return;
29578         }
29579         
29580         file.xhr = this.xhr;
29581             
29582         this.managerEl.createChild({
29583             tag : 'div',
29584             cls : 'roo-document-manager-loading',
29585             cn : [
29586                 {
29587                     tag : 'div',
29588                     tooltip : file.name,
29589                     cls : 'roo-document-manager-thumb',
29590                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29591                 }
29592             ]
29593
29594         });
29595
29596         this.xhr.open(this.method, this.url, true);
29597         
29598         var headers = {
29599             "Accept": "application/json",
29600             "Cache-Control": "no-cache",
29601             "X-Requested-With": "XMLHttpRequest"
29602         };
29603         
29604         for (var headerName in headers) {
29605             var headerValue = headers[headerName];
29606             if (headerValue) {
29607                 this.xhr.setRequestHeader(headerName, headerValue);
29608             }
29609         }
29610         
29611         var _this = this;
29612         
29613         this.xhr.onload = function()
29614         {
29615             _this.xhrOnLoad(_this.xhr);
29616         }
29617         
29618         this.xhr.onerror = function()
29619         {
29620             _this.xhrOnError(_this.xhr);
29621         }
29622         
29623         var formData = new FormData();
29624
29625         formData.append('returnHTML', 'NO');
29626         
29627         if(crop){
29628             formData.append('crop', crop);
29629         }
29630         
29631         formData.append(this.paramName, file, file.name);
29632         
29633         var options = {
29634             file : file, 
29635             manually : false
29636         };
29637         
29638         if(this.fireEvent('prepare', this, formData, options) != false){
29639             
29640             if(options.manually){
29641                 return;
29642             }
29643             
29644             this.xhr.send(formData);
29645             return;
29646         };
29647         
29648         this.uploadCancel();
29649     },
29650     
29651     uploadCancel : function()
29652     {
29653         if (this.xhr) {
29654             this.xhr.abort();
29655         }
29656         
29657         this.delegates = [];
29658         
29659         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29660             el.remove();
29661         }, this);
29662         
29663         this.arrange();
29664     },
29665     
29666     renderPreview : function(file)
29667     {
29668         if(typeof(file.target) != 'undefined' && file.target){
29669             return file;
29670         }
29671         
29672         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29673         
29674         var previewEl = this.managerEl.createChild({
29675             tag : 'div',
29676             cls : 'roo-document-manager-preview',
29677             cn : [
29678                 {
29679                     tag : 'div',
29680                     tooltip : file[this.toolTipName],
29681                     cls : 'roo-document-manager-thumb',
29682                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29683                 },
29684                 {
29685                     tag : 'button',
29686                     cls : 'close',
29687                     html : '<i class="fa fa-times-circle"></i>'
29688                 }
29689             ]
29690         });
29691
29692         var close = previewEl.select('button.close', true).first();
29693
29694         close.on('click', this.onRemove, this, file);
29695
29696         file.target = previewEl;
29697
29698         var image = previewEl.select('img', true).first();
29699         
29700         var _this = this;
29701         
29702         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29703         
29704         image.on('click', this.onClick, this, file);
29705         
29706         this.fireEvent('previewrendered', this, file);
29707         
29708         return file;
29709         
29710     },
29711     
29712     onPreviewLoad : function(file, image)
29713     {
29714         if(typeof(file.target) == 'undefined' || !file.target){
29715             return;
29716         }
29717         
29718         var width = image.dom.naturalWidth || image.dom.width;
29719         var height = image.dom.naturalHeight || image.dom.height;
29720         
29721         if(!this.previewResize) {
29722             return;
29723         }
29724         
29725         if(width > height){
29726             file.target.addClass('wide');
29727             return;
29728         }
29729         
29730         file.target.addClass('tall');
29731         return;
29732         
29733     },
29734     
29735     uploadFromSource : function(file, crop)
29736     {
29737         this.xhr = new XMLHttpRequest();
29738         
29739         this.managerEl.createChild({
29740             tag : 'div',
29741             cls : 'roo-document-manager-loading',
29742             cn : [
29743                 {
29744                     tag : 'div',
29745                     tooltip : file.name,
29746                     cls : 'roo-document-manager-thumb',
29747                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29748                 }
29749             ]
29750
29751         });
29752
29753         this.xhr.open(this.method, this.url, true);
29754         
29755         var headers = {
29756             "Accept": "application/json",
29757             "Cache-Control": "no-cache",
29758             "X-Requested-With": "XMLHttpRequest"
29759         };
29760         
29761         for (var headerName in headers) {
29762             var headerValue = headers[headerName];
29763             if (headerValue) {
29764                 this.xhr.setRequestHeader(headerName, headerValue);
29765             }
29766         }
29767         
29768         var _this = this;
29769         
29770         this.xhr.onload = function()
29771         {
29772             _this.xhrOnLoad(_this.xhr);
29773         }
29774         
29775         this.xhr.onerror = function()
29776         {
29777             _this.xhrOnError(_this.xhr);
29778         }
29779         
29780         var formData = new FormData();
29781
29782         formData.append('returnHTML', 'NO');
29783         
29784         formData.append('crop', crop);
29785         
29786         if(typeof(file.filename) != 'undefined'){
29787             formData.append('filename', file.filename);
29788         }
29789         
29790         if(typeof(file.mimetype) != 'undefined'){
29791             formData.append('mimetype', file.mimetype);
29792         }
29793         
29794         Roo.log(formData);
29795         
29796         if(this.fireEvent('prepare', this, formData) != false){
29797             this.xhr.send(formData);
29798         };
29799     }
29800 });
29801
29802 /*
29803 * Licence: LGPL
29804 */
29805
29806 /**
29807  * @class Roo.bootstrap.DocumentViewer
29808  * @extends Roo.bootstrap.Component
29809  * Bootstrap DocumentViewer class
29810  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29811  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29812  * 
29813  * @constructor
29814  * Create a new DocumentViewer
29815  * @param {Object} config The config object
29816  */
29817
29818 Roo.bootstrap.DocumentViewer = function(config){
29819     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29820     
29821     this.addEvents({
29822         /**
29823          * @event initial
29824          * Fire after initEvent
29825          * @param {Roo.bootstrap.DocumentViewer} this
29826          */
29827         "initial" : true,
29828         /**
29829          * @event click
29830          * Fire after click
29831          * @param {Roo.bootstrap.DocumentViewer} this
29832          */
29833         "click" : true,
29834         /**
29835          * @event download
29836          * Fire after download button
29837          * @param {Roo.bootstrap.DocumentViewer} this
29838          */
29839         "download" : true,
29840         /**
29841          * @event trash
29842          * Fire after trash button
29843          * @param {Roo.bootstrap.DocumentViewer} this
29844          */
29845         "trash" : true
29846         
29847     });
29848 };
29849
29850 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29851     
29852     showDownload : true,
29853     
29854     showTrash : true,
29855     
29856     getAutoCreate : function()
29857     {
29858         var cfg = {
29859             tag : 'div',
29860             cls : 'roo-document-viewer',
29861             cn : [
29862                 {
29863                     tag : 'div',
29864                     cls : 'roo-document-viewer-body',
29865                     cn : [
29866                         {
29867                             tag : 'div',
29868                             cls : 'roo-document-viewer-thumb',
29869                             cn : [
29870                                 {
29871                                     tag : 'img',
29872                                     cls : 'roo-document-viewer-image'
29873                                 }
29874                             ]
29875                         }
29876                     ]
29877                 },
29878                 {
29879                     tag : 'div',
29880                     cls : 'roo-document-viewer-footer',
29881                     cn : {
29882                         tag : 'div',
29883                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29884                         cn : [
29885                             {
29886                                 tag : 'div',
29887                                 cls : 'btn-group roo-document-viewer-download',
29888                                 cn : [
29889                                     {
29890                                         tag : 'button',
29891                                         cls : 'btn btn-default',
29892                                         html : '<i class="fa fa-download"></i>'
29893                                     }
29894                                 ]
29895                             },
29896                             {
29897                                 tag : 'div',
29898                                 cls : 'btn-group roo-document-viewer-trash',
29899                                 cn : [
29900                                     {
29901                                         tag : 'button',
29902                                         cls : 'btn btn-default',
29903                                         html : '<i class="fa fa-trash"></i>'
29904                                     }
29905                                 ]
29906                             }
29907                         ]
29908                     }
29909                 }
29910             ]
29911         };
29912         
29913         return cfg;
29914     },
29915     
29916     initEvents : function()
29917     {
29918         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29919         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29920         
29921         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29922         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29923         
29924         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29925         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29926         
29927         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29928         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29929         
29930         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29931         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29932         
29933         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29934         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29935         
29936         this.bodyEl.on('click', this.onClick, this);
29937         this.downloadBtn.on('click', this.onDownload, this);
29938         this.trashBtn.on('click', this.onTrash, this);
29939         
29940         this.downloadBtn.hide();
29941         this.trashBtn.hide();
29942         
29943         if(this.showDownload){
29944             this.downloadBtn.show();
29945         }
29946         
29947         if(this.showTrash){
29948             this.trashBtn.show();
29949         }
29950         
29951         if(!this.showDownload && !this.showTrash) {
29952             this.footerEl.hide();
29953         }
29954         
29955     },
29956     
29957     initial : function()
29958     {
29959         this.fireEvent('initial', this);
29960         
29961     },
29962     
29963     onClick : function(e)
29964     {
29965         e.preventDefault();
29966         
29967         this.fireEvent('click', this);
29968     },
29969     
29970     onDownload : function(e)
29971     {
29972         e.preventDefault();
29973         
29974         this.fireEvent('download', this);
29975     },
29976     
29977     onTrash : function(e)
29978     {
29979         e.preventDefault();
29980         
29981         this.fireEvent('trash', this);
29982     }
29983     
29984 });
29985 /*
29986  * - LGPL
29987  *
29988  * nav progress bar
29989  * 
29990  */
29991
29992 /**
29993  * @class Roo.bootstrap.NavProgressBar
29994  * @extends Roo.bootstrap.Component
29995  * Bootstrap NavProgressBar class
29996  * 
29997  * @constructor
29998  * Create a new nav progress bar
29999  * @param {Object} config The config object
30000  */
30001
30002 Roo.bootstrap.NavProgressBar = function(config){
30003     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30004
30005     this.bullets = this.bullets || [];
30006    
30007 //    Roo.bootstrap.NavProgressBar.register(this);
30008      this.addEvents({
30009         /**
30010              * @event changed
30011              * Fires when the active item changes
30012              * @param {Roo.bootstrap.NavProgressBar} this
30013              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30014              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
30015          */
30016         'changed': true
30017      });
30018     
30019 };
30020
30021 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
30022     
30023     bullets : [],
30024     barItems : [],
30025     
30026     getAutoCreate : function()
30027     {
30028         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30029         
30030         cfg = {
30031             tag : 'div',
30032             cls : 'roo-navigation-bar-group',
30033             cn : [
30034                 {
30035                     tag : 'div',
30036                     cls : 'roo-navigation-top-bar'
30037                 },
30038                 {
30039                     tag : 'div',
30040                     cls : 'roo-navigation-bullets-bar',
30041                     cn : [
30042                         {
30043                             tag : 'ul',
30044                             cls : 'roo-navigation-bar'
30045                         }
30046                     ]
30047                 },
30048                 
30049                 {
30050                     tag : 'div',
30051                     cls : 'roo-navigation-bottom-bar'
30052                 }
30053             ]
30054             
30055         };
30056         
30057         return cfg;
30058         
30059     },
30060     
30061     initEvents: function() 
30062     {
30063         
30064     },
30065     
30066     onRender : function(ct, position) 
30067     {
30068         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30069         
30070         if(this.bullets.length){
30071             Roo.each(this.bullets, function(b){
30072                this.addItem(b);
30073             }, this);
30074         }
30075         
30076         this.format();
30077         
30078     },
30079     
30080     addItem : function(cfg)
30081     {
30082         var item = new Roo.bootstrap.NavProgressItem(cfg);
30083         
30084         item.parentId = this.id;
30085         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30086         
30087         if(cfg.html){
30088             var top = new Roo.bootstrap.Element({
30089                 tag : 'div',
30090                 cls : 'roo-navigation-bar-text'
30091             });
30092             
30093             var bottom = new Roo.bootstrap.Element({
30094                 tag : 'div',
30095                 cls : 'roo-navigation-bar-text'
30096             });
30097             
30098             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30099             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30100             
30101             var topText = new Roo.bootstrap.Element({
30102                 tag : 'span',
30103                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30104             });
30105             
30106             var bottomText = new Roo.bootstrap.Element({
30107                 tag : 'span',
30108                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30109             });
30110             
30111             topText.onRender(top.el, null);
30112             bottomText.onRender(bottom.el, null);
30113             
30114             item.topEl = top;
30115             item.bottomEl = bottom;
30116         }
30117         
30118         this.barItems.push(item);
30119         
30120         return item;
30121     },
30122     
30123     getActive : function()
30124     {
30125         var active = false;
30126         
30127         Roo.each(this.barItems, function(v){
30128             
30129             if (!v.isActive()) {
30130                 return;
30131             }
30132             
30133             active = v;
30134             return false;
30135             
30136         });
30137         
30138         return active;
30139     },
30140     
30141     setActiveItem : function(item)
30142     {
30143         var prev = false;
30144         
30145         Roo.each(this.barItems, function(v){
30146             if (v.rid == item.rid) {
30147                 return ;
30148             }
30149             
30150             if (v.isActive()) {
30151                 v.setActive(false);
30152                 prev = v;
30153             }
30154         });
30155
30156         item.setActive(true);
30157         
30158         this.fireEvent('changed', this, item, prev);
30159     },
30160     
30161     getBarItem: function(rid)
30162     {
30163         var ret = false;
30164         
30165         Roo.each(this.barItems, function(e) {
30166             if (e.rid != rid) {
30167                 return;
30168             }
30169             
30170             ret =  e;
30171             return false;
30172         });
30173         
30174         return ret;
30175     },
30176     
30177     indexOfItem : function(item)
30178     {
30179         var index = false;
30180         
30181         Roo.each(this.barItems, function(v, i){
30182             
30183             if (v.rid != item.rid) {
30184                 return;
30185             }
30186             
30187             index = i;
30188             return false
30189         });
30190         
30191         return index;
30192     },
30193     
30194     setActiveNext : function()
30195     {
30196         var i = this.indexOfItem(this.getActive());
30197         
30198         if (i > this.barItems.length) {
30199             return;
30200         }
30201         
30202         this.setActiveItem(this.barItems[i+1]);
30203     },
30204     
30205     setActivePrev : function()
30206     {
30207         var i = this.indexOfItem(this.getActive());
30208         
30209         if (i  < 1) {
30210             return;
30211         }
30212         
30213         this.setActiveItem(this.barItems[i-1]);
30214     },
30215     
30216     format : function()
30217     {
30218         if(!this.barItems.length){
30219             return;
30220         }
30221      
30222         var width = 100 / this.barItems.length;
30223         
30224         Roo.each(this.barItems, function(i){
30225             i.el.setStyle('width', width + '%');
30226             i.topEl.el.setStyle('width', width + '%');
30227             i.bottomEl.el.setStyle('width', width + '%');
30228         }, this);
30229         
30230     }
30231     
30232 });
30233 /*
30234  * - LGPL
30235  *
30236  * Nav Progress Item
30237  * 
30238  */
30239
30240 /**
30241  * @class Roo.bootstrap.NavProgressItem
30242  * @extends Roo.bootstrap.Component
30243  * Bootstrap NavProgressItem class
30244  * @cfg {String} rid the reference id
30245  * @cfg {Boolean} active (true|false) Is item active default false
30246  * @cfg {Boolean} disabled (true|false) Is item active default false
30247  * @cfg {String} html
30248  * @cfg {String} position (top|bottom) text position default bottom
30249  * @cfg {String} icon show icon instead of number
30250  * 
30251  * @constructor
30252  * Create a new NavProgressItem
30253  * @param {Object} config The config object
30254  */
30255 Roo.bootstrap.NavProgressItem = function(config){
30256     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30257     this.addEvents({
30258         // raw events
30259         /**
30260          * @event click
30261          * The raw click event for the entire grid.
30262          * @param {Roo.bootstrap.NavProgressItem} this
30263          * @param {Roo.EventObject} e
30264          */
30265         "click" : true
30266     });
30267    
30268 };
30269
30270 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30271     
30272     rid : '',
30273     active : false,
30274     disabled : false,
30275     html : '',
30276     position : 'bottom',
30277     icon : false,
30278     
30279     getAutoCreate : function()
30280     {
30281         var iconCls = 'roo-navigation-bar-item-icon';
30282         
30283         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30284         
30285         var cfg = {
30286             tag: 'li',
30287             cls: 'roo-navigation-bar-item',
30288             cn : [
30289                 {
30290                     tag : 'i',
30291                     cls : iconCls
30292                 }
30293             ]
30294         };
30295         
30296         if(this.active){
30297             cfg.cls += ' active';
30298         }
30299         if(this.disabled){
30300             cfg.cls += ' disabled';
30301         }
30302         
30303         return cfg;
30304     },
30305     
30306     disable : function()
30307     {
30308         this.setDisabled(true);
30309     },
30310     
30311     enable : function()
30312     {
30313         this.setDisabled(false);
30314     },
30315     
30316     initEvents: function() 
30317     {
30318         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30319         
30320         this.iconEl.on('click', this.onClick, this);
30321     },
30322     
30323     onClick : function(e)
30324     {
30325         e.preventDefault();
30326         
30327         if(this.disabled){
30328             return;
30329         }
30330         
30331         if(this.fireEvent('click', this, e) === false){
30332             return;
30333         };
30334         
30335         this.parent().setActiveItem(this);
30336     },
30337     
30338     isActive: function () 
30339     {
30340         return this.active;
30341     },
30342     
30343     setActive : function(state)
30344     {
30345         if(this.active == state){
30346             return;
30347         }
30348         
30349         this.active = state;
30350         
30351         if (state) {
30352             this.el.addClass('active');
30353             return;
30354         }
30355         
30356         this.el.removeClass('active');
30357         
30358         return;
30359     },
30360     
30361     setDisabled : function(state)
30362     {
30363         if(this.disabled == state){
30364             return;
30365         }
30366         
30367         this.disabled = state;
30368         
30369         if (state) {
30370             this.el.addClass('disabled');
30371             return;
30372         }
30373         
30374         this.el.removeClass('disabled');
30375     },
30376     
30377     tooltipEl : function()
30378     {
30379         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30380     }
30381 });
30382  
30383
30384  /*
30385  * - LGPL
30386  *
30387  * FieldLabel
30388  * 
30389  */
30390
30391 /**
30392  * @class Roo.bootstrap.FieldLabel
30393  * @extends Roo.bootstrap.Component
30394  * Bootstrap FieldLabel class
30395  * @cfg {String} html contents of the element
30396  * @cfg {String} tag tag of the element default label
30397  * @cfg {String} cls class of the element
30398  * @cfg {String} target label target 
30399  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30400  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30401  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30402  * @cfg {String} iconTooltip default "This field is required"
30403  * @cfg {String} indicatorpos (left|right) default left
30404  * 
30405  * @constructor
30406  * Create a new FieldLabel
30407  * @param {Object} config The config object
30408  */
30409
30410 Roo.bootstrap.FieldLabel = function(config){
30411     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30412     
30413     this.addEvents({
30414             /**
30415              * @event invalid
30416              * Fires after the field has been marked as invalid.
30417              * @param {Roo.form.FieldLabel} this
30418              * @param {String} msg The validation message
30419              */
30420             invalid : true,
30421             /**
30422              * @event valid
30423              * Fires after the field has been validated with no errors.
30424              * @param {Roo.form.FieldLabel} this
30425              */
30426             valid : true
30427         });
30428 };
30429
30430 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30431     
30432     tag: 'label',
30433     cls: '',
30434     html: '',
30435     target: '',
30436     allowBlank : true,
30437     invalidClass : 'has-warning',
30438     validClass : 'has-success',
30439     iconTooltip : 'This field is required',
30440     indicatorpos : 'left',
30441     
30442     getAutoCreate : function(){
30443         
30444         var cls = "";
30445         if (!this.allowBlank) {
30446             cls  = "visible";
30447         }
30448         
30449         var cfg = {
30450             tag : this.tag,
30451             cls : 'roo-bootstrap-field-label ' + this.cls,
30452             for : this.target,
30453             cn : [
30454                 {
30455                     tag : 'i',
30456                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30457                     tooltip : this.iconTooltip
30458                 },
30459                 {
30460                     tag : 'span',
30461                     html : this.html
30462                 }
30463             ] 
30464         };
30465         
30466         if(this.indicatorpos == 'right'){
30467             var cfg = {
30468                 tag : this.tag,
30469                 cls : 'roo-bootstrap-field-label ' + this.cls,
30470                 for : this.target,
30471                 cn : [
30472                     {
30473                         tag : 'span',
30474                         html : this.html
30475                     },
30476                     {
30477                         tag : 'i',
30478                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30479                         tooltip : this.iconTooltip
30480                     }
30481                 ] 
30482             };
30483         }
30484         
30485         return cfg;
30486     },
30487     
30488     initEvents: function() 
30489     {
30490         Roo.bootstrap.Element.superclass.initEvents.call(this);
30491         
30492         this.indicator = this.indicatorEl();
30493         
30494         if(this.indicator){
30495             this.indicator.removeClass('visible');
30496             this.indicator.addClass('invisible');
30497         }
30498         
30499         Roo.bootstrap.FieldLabel.register(this);
30500     },
30501     
30502     indicatorEl : function()
30503     {
30504         var indicator = this.el.select('i.roo-required-indicator',true).first();
30505         
30506         if(!indicator){
30507             return false;
30508         }
30509         
30510         return indicator;
30511         
30512     },
30513     
30514     /**
30515      * Mark this field as valid
30516      */
30517     markValid : function()
30518     {
30519         if(this.indicator){
30520             this.indicator.removeClass('visible');
30521             this.indicator.addClass('invisible');
30522         }
30523         if (Roo.bootstrap.version == 3) {
30524             this.el.removeClass(this.invalidClass);
30525             this.el.addClass(this.validClass);
30526         } else {
30527             this.el.removeClass('is-invalid');
30528             this.el.addClass('is-valid');
30529         }
30530         
30531         
30532         this.fireEvent('valid', this);
30533     },
30534     
30535     /**
30536      * Mark this field as invalid
30537      * @param {String} msg The validation message
30538      */
30539     markInvalid : function(msg)
30540     {
30541         if(this.indicator){
30542             this.indicator.removeClass('invisible');
30543             this.indicator.addClass('visible');
30544         }
30545           if (Roo.bootstrap.version == 3) {
30546             this.el.removeClass(this.validClass);
30547             this.el.addClass(this.invalidClass);
30548         } else {
30549             this.el.removeClass('is-valid');
30550             this.el.addClass('is-invalid');
30551         }
30552         
30553         
30554         this.fireEvent('invalid', this, msg);
30555     }
30556     
30557    
30558 });
30559
30560 Roo.apply(Roo.bootstrap.FieldLabel, {
30561     
30562     groups: {},
30563     
30564      /**
30565     * register a FieldLabel Group
30566     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30567     */
30568     register : function(label)
30569     {
30570         if(this.groups.hasOwnProperty(label.target)){
30571             return;
30572         }
30573      
30574         this.groups[label.target] = label;
30575         
30576     },
30577     /**
30578     * fetch a FieldLabel Group based on the target
30579     * @param {string} target
30580     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30581     */
30582     get: function(target) {
30583         if (typeof(this.groups[target]) == 'undefined') {
30584             return false;
30585         }
30586         
30587         return this.groups[target] ;
30588     }
30589 });
30590
30591  
30592
30593  /*
30594  * - LGPL
30595  *
30596  * page DateSplitField.
30597  * 
30598  */
30599
30600
30601 /**
30602  * @class Roo.bootstrap.DateSplitField
30603  * @extends Roo.bootstrap.Component
30604  * Bootstrap DateSplitField class
30605  * @cfg {string} fieldLabel - the label associated
30606  * @cfg {Number} labelWidth set the width of label (0-12)
30607  * @cfg {String} labelAlign (top|left)
30608  * @cfg {Boolean} dayAllowBlank (true|false) default false
30609  * @cfg {Boolean} monthAllowBlank (true|false) default false
30610  * @cfg {Boolean} yearAllowBlank (true|false) default false
30611  * @cfg {string} dayPlaceholder 
30612  * @cfg {string} monthPlaceholder
30613  * @cfg {string} yearPlaceholder
30614  * @cfg {string} dayFormat default 'd'
30615  * @cfg {string} monthFormat default 'm'
30616  * @cfg {string} yearFormat default 'Y'
30617  * @cfg {Number} labellg set the width of label (1-12)
30618  * @cfg {Number} labelmd set the width of label (1-12)
30619  * @cfg {Number} labelsm set the width of label (1-12)
30620  * @cfg {Number} labelxs set the width of label (1-12)
30621
30622  *     
30623  * @constructor
30624  * Create a new DateSplitField
30625  * @param {Object} config The config object
30626  */
30627
30628 Roo.bootstrap.DateSplitField = function(config){
30629     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30630     
30631     this.addEvents({
30632         // raw events
30633          /**
30634          * @event years
30635          * getting the data of years
30636          * @param {Roo.bootstrap.DateSplitField} this
30637          * @param {Object} years
30638          */
30639         "years" : true,
30640         /**
30641          * @event days
30642          * getting the data of days
30643          * @param {Roo.bootstrap.DateSplitField} this
30644          * @param {Object} days
30645          */
30646         "days" : true,
30647         /**
30648          * @event invalid
30649          * Fires after the field has been marked as invalid.
30650          * @param {Roo.form.Field} this
30651          * @param {String} msg The validation message
30652          */
30653         invalid : true,
30654        /**
30655          * @event valid
30656          * Fires after the field has been validated with no errors.
30657          * @param {Roo.form.Field} this
30658          */
30659         valid : true
30660     });
30661 };
30662
30663 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30664     
30665     fieldLabel : '',
30666     labelAlign : 'top',
30667     labelWidth : 3,
30668     dayAllowBlank : false,
30669     monthAllowBlank : false,
30670     yearAllowBlank : false,
30671     dayPlaceholder : '',
30672     monthPlaceholder : '',
30673     yearPlaceholder : '',
30674     dayFormat : 'd',
30675     monthFormat : 'm',
30676     yearFormat : 'Y',
30677     isFormField : true,
30678     labellg : 0,
30679     labelmd : 0,
30680     labelsm : 0,
30681     labelxs : 0,
30682     
30683     getAutoCreate : function()
30684     {
30685         var cfg = {
30686             tag : 'div',
30687             cls : 'row roo-date-split-field-group',
30688             cn : [
30689                 {
30690                     tag : 'input',
30691                     type : 'hidden',
30692                     cls : 'form-hidden-field roo-date-split-field-group-value',
30693                     name : this.name
30694                 }
30695             ]
30696         };
30697         
30698         var labelCls = 'col-md-12';
30699         var contentCls = 'col-md-4';
30700         
30701         if(this.fieldLabel){
30702             
30703             var label = {
30704                 tag : 'div',
30705                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30706                 cn : [
30707                     {
30708                         tag : 'label',
30709                         html : this.fieldLabel
30710                     }
30711                 ]
30712             };
30713             
30714             if(this.labelAlign == 'left'){
30715             
30716                 if(this.labelWidth > 12){
30717                     label.style = "width: " + this.labelWidth + 'px';
30718                 }
30719
30720                 if(this.labelWidth < 13 && this.labelmd == 0){
30721                     this.labelmd = this.labelWidth;
30722                 }
30723
30724                 if(this.labellg > 0){
30725                     labelCls = ' col-lg-' + this.labellg;
30726                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30727                 }
30728
30729                 if(this.labelmd > 0){
30730                     labelCls = ' col-md-' + this.labelmd;
30731                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30732                 }
30733
30734                 if(this.labelsm > 0){
30735                     labelCls = ' col-sm-' + this.labelsm;
30736                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30737                 }
30738
30739                 if(this.labelxs > 0){
30740                     labelCls = ' col-xs-' + this.labelxs;
30741                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30742                 }
30743             }
30744             
30745             label.cls += ' ' + labelCls;
30746             
30747             cfg.cn.push(label);
30748         }
30749         
30750         Roo.each(['day', 'month', 'year'], function(t){
30751             cfg.cn.push({
30752                 tag : 'div',
30753                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30754             });
30755         }, this);
30756         
30757         return cfg;
30758     },
30759     
30760     inputEl: function ()
30761     {
30762         return this.el.select('.roo-date-split-field-group-value', true).first();
30763     },
30764     
30765     onRender : function(ct, position) 
30766     {
30767         var _this = this;
30768         
30769         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30770         
30771         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30772         
30773         this.dayField = new Roo.bootstrap.ComboBox({
30774             allowBlank : this.dayAllowBlank,
30775             alwaysQuery : true,
30776             displayField : 'value',
30777             editable : false,
30778             fieldLabel : '',
30779             forceSelection : true,
30780             mode : 'local',
30781             placeholder : this.dayPlaceholder,
30782             selectOnFocus : true,
30783             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30784             triggerAction : 'all',
30785             typeAhead : true,
30786             valueField : 'value',
30787             store : new Roo.data.SimpleStore({
30788                 data : (function() {    
30789                     var days = [];
30790                     _this.fireEvent('days', _this, days);
30791                     return days;
30792                 })(),
30793                 fields : [ 'value' ]
30794             }),
30795             listeners : {
30796                 select : function (_self, record, index)
30797                 {
30798                     _this.setValue(_this.getValue());
30799                 }
30800             }
30801         });
30802
30803         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30804         
30805         this.monthField = new Roo.bootstrap.MonthField({
30806             after : '<i class=\"fa fa-calendar\"></i>',
30807             allowBlank : this.monthAllowBlank,
30808             placeholder : this.monthPlaceholder,
30809             readOnly : true,
30810             listeners : {
30811                 render : function (_self)
30812                 {
30813                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30814                         e.preventDefault();
30815                         _self.focus();
30816                     });
30817                 },
30818                 select : function (_self, oldvalue, newvalue)
30819                 {
30820                     _this.setValue(_this.getValue());
30821                 }
30822             }
30823         });
30824         
30825         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30826         
30827         this.yearField = new Roo.bootstrap.ComboBox({
30828             allowBlank : this.yearAllowBlank,
30829             alwaysQuery : true,
30830             displayField : 'value',
30831             editable : false,
30832             fieldLabel : '',
30833             forceSelection : true,
30834             mode : 'local',
30835             placeholder : this.yearPlaceholder,
30836             selectOnFocus : true,
30837             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30838             triggerAction : 'all',
30839             typeAhead : true,
30840             valueField : 'value',
30841             store : new Roo.data.SimpleStore({
30842                 data : (function() {
30843                     var years = [];
30844                     _this.fireEvent('years', _this, years);
30845                     return years;
30846                 })(),
30847                 fields : [ 'value' ]
30848             }),
30849             listeners : {
30850                 select : function (_self, record, index)
30851                 {
30852                     _this.setValue(_this.getValue());
30853                 }
30854             }
30855         });
30856
30857         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30858     },
30859     
30860     setValue : function(v, format)
30861     {
30862         this.inputEl.dom.value = v;
30863         
30864         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30865         
30866         var d = Date.parseDate(v, f);
30867         
30868         if(!d){
30869             this.validate();
30870             return;
30871         }
30872         
30873         this.setDay(d.format(this.dayFormat));
30874         this.setMonth(d.format(this.monthFormat));
30875         this.setYear(d.format(this.yearFormat));
30876         
30877         this.validate();
30878         
30879         return;
30880     },
30881     
30882     setDay : function(v)
30883     {
30884         this.dayField.setValue(v);
30885         this.inputEl.dom.value = this.getValue();
30886         this.validate();
30887         return;
30888     },
30889     
30890     setMonth : function(v)
30891     {
30892         this.monthField.setValue(v, true);
30893         this.inputEl.dom.value = this.getValue();
30894         this.validate();
30895         return;
30896     },
30897     
30898     setYear : function(v)
30899     {
30900         this.yearField.setValue(v);
30901         this.inputEl.dom.value = this.getValue();
30902         this.validate();
30903         return;
30904     },
30905     
30906     getDay : function()
30907     {
30908         return this.dayField.getValue();
30909     },
30910     
30911     getMonth : function()
30912     {
30913         return this.monthField.getValue();
30914     },
30915     
30916     getYear : function()
30917     {
30918         return this.yearField.getValue();
30919     },
30920     
30921     getValue : function()
30922     {
30923         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30924         
30925         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30926         
30927         return date;
30928     },
30929     
30930     reset : function()
30931     {
30932         this.setDay('');
30933         this.setMonth('');
30934         this.setYear('');
30935         this.inputEl.dom.value = '';
30936         this.validate();
30937         return;
30938     },
30939     
30940     validate : function()
30941     {
30942         var d = this.dayField.validate();
30943         var m = this.monthField.validate();
30944         var y = this.yearField.validate();
30945         
30946         var valid = true;
30947         
30948         if(
30949                 (!this.dayAllowBlank && !d) ||
30950                 (!this.monthAllowBlank && !m) ||
30951                 (!this.yearAllowBlank && !y)
30952         ){
30953             valid = false;
30954         }
30955         
30956         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30957             return valid;
30958         }
30959         
30960         if(valid){
30961             this.markValid();
30962             return valid;
30963         }
30964         
30965         this.markInvalid();
30966         
30967         return valid;
30968     },
30969     
30970     markValid : function()
30971     {
30972         
30973         var label = this.el.select('label', true).first();
30974         var icon = this.el.select('i.fa-star', true).first();
30975
30976         if(label && icon){
30977             icon.remove();
30978         }
30979         
30980         this.fireEvent('valid', this);
30981     },
30982     
30983      /**
30984      * Mark this field as invalid
30985      * @param {String} msg The validation message
30986      */
30987     markInvalid : function(msg)
30988     {
30989         
30990         var label = this.el.select('label', true).first();
30991         var icon = this.el.select('i.fa-star', true).first();
30992
30993         if(label && !icon){
30994             this.el.select('.roo-date-split-field-label', true).createChild({
30995                 tag : 'i',
30996                 cls : 'text-danger fa fa-lg fa-star',
30997                 tooltip : 'This field is required',
30998                 style : 'margin-right:5px;'
30999             }, label, true);
31000         }
31001         
31002         this.fireEvent('invalid', this, msg);
31003     },
31004     
31005     clearInvalid : function()
31006     {
31007         var label = this.el.select('label', true).first();
31008         var icon = this.el.select('i.fa-star', true).first();
31009
31010         if(label && icon){
31011             icon.remove();
31012         }
31013         
31014         this.fireEvent('valid', this);
31015     },
31016     
31017     getName: function()
31018     {
31019         return this.name;
31020     }
31021     
31022 });
31023
31024  /**
31025  *
31026  * This is based on 
31027  * http://masonry.desandro.com
31028  *
31029  * The idea is to render all the bricks based on vertical width...
31030  *
31031  * The original code extends 'outlayer' - we might need to use that....
31032  * 
31033  */
31034
31035
31036 /**
31037  * @class Roo.bootstrap.LayoutMasonry
31038  * @extends Roo.bootstrap.Component
31039  * Bootstrap Layout Masonry class
31040  * 
31041  * @constructor
31042  * Create a new Element
31043  * @param {Object} config The config object
31044  */
31045
31046 Roo.bootstrap.LayoutMasonry = function(config){
31047     
31048     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31049     
31050     this.bricks = [];
31051     
31052     Roo.bootstrap.LayoutMasonry.register(this);
31053     
31054     this.addEvents({
31055         // raw events
31056         /**
31057          * @event layout
31058          * Fire after layout the items
31059          * @param {Roo.bootstrap.LayoutMasonry} this
31060          * @param {Roo.EventObject} e
31061          */
31062         "layout" : true
31063     });
31064     
31065 };
31066
31067 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31068     
31069     /**
31070      * @cfg {Boolean} isLayoutInstant = no animation?
31071      */   
31072     isLayoutInstant : false, // needed?
31073    
31074     /**
31075      * @cfg {Number} boxWidth  width of the columns
31076      */   
31077     boxWidth : 450,
31078     
31079       /**
31080      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31081      */   
31082     boxHeight : 0,
31083     
31084     /**
31085      * @cfg {Number} padWidth padding below box..
31086      */   
31087     padWidth : 10, 
31088     
31089     /**
31090      * @cfg {Number} gutter gutter width..
31091      */   
31092     gutter : 10,
31093     
31094      /**
31095      * @cfg {Number} maxCols maximum number of columns
31096      */   
31097     
31098     maxCols: 0,
31099     
31100     /**
31101      * @cfg {Boolean} isAutoInitial defalut true
31102      */   
31103     isAutoInitial : true, 
31104     
31105     containerWidth: 0,
31106     
31107     /**
31108      * @cfg {Boolean} isHorizontal defalut false
31109      */   
31110     isHorizontal : false, 
31111
31112     currentSize : null,
31113     
31114     tag: 'div',
31115     
31116     cls: '',
31117     
31118     bricks: null, //CompositeElement
31119     
31120     cols : 1,
31121     
31122     _isLayoutInited : false,
31123     
31124 //    isAlternative : false, // only use for vertical layout...
31125     
31126     /**
31127      * @cfg {Number} alternativePadWidth padding below box..
31128      */   
31129     alternativePadWidth : 50,
31130     
31131     selectedBrick : [],
31132     
31133     getAutoCreate : function(){
31134         
31135         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31136         
31137         var cfg = {
31138             tag: this.tag,
31139             cls: 'blog-masonary-wrapper ' + this.cls,
31140             cn : {
31141                 cls : 'mas-boxes masonary'
31142             }
31143         };
31144         
31145         return cfg;
31146     },
31147     
31148     getChildContainer: function( )
31149     {
31150         if (this.boxesEl) {
31151             return this.boxesEl;
31152         }
31153         
31154         this.boxesEl = this.el.select('.mas-boxes').first();
31155         
31156         return this.boxesEl;
31157     },
31158     
31159     
31160     initEvents : function()
31161     {
31162         var _this = this;
31163         
31164         if(this.isAutoInitial){
31165             Roo.log('hook children rendered');
31166             this.on('childrenrendered', function() {
31167                 Roo.log('children rendered');
31168                 _this.initial();
31169             } ,this);
31170         }
31171     },
31172     
31173     initial : function()
31174     {
31175         this.selectedBrick = [];
31176         
31177         this.currentSize = this.el.getBox(true);
31178         
31179         Roo.EventManager.onWindowResize(this.resize, this); 
31180
31181         if(!this.isAutoInitial){
31182             this.layout();
31183             return;
31184         }
31185         
31186         this.layout();
31187         
31188         return;
31189         //this.layout.defer(500,this);
31190         
31191     },
31192     
31193     resize : function()
31194     {
31195         var cs = this.el.getBox(true);
31196         
31197         if (
31198                 this.currentSize.width == cs.width && 
31199                 this.currentSize.x == cs.x && 
31200                 this.currentSize.height == cs.height && 
31201                 this.currentSize.y == cs.y 
31202         ) {
31203             Roo.log("no change in with or X or Y");
31204             return;
31205         }
31206         
31207         this.currentSize = cs;
31208         
31209         this.layout();
31210         
31211     },
31212     
31213     layout : function()
31214     {   
31215         this._resetLayout();
31216         
31217         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31218         
31219         this.layoutItems( isInstant );
31220       
31221         this._isLayoutInited = true;
31222         
31223         this.fireEvent('layout', this);
31224         
31225     },
31226     
31227     _resetLayout : function()
31228     {
31229         if(this.isHorizontal){
31230             this.horizontalMeasureColumns();
31231             return;
31232         }
31233         
31234         this.verticalMeasureColumns();
31235         
31236     },
31237     
31238     verticalMeasureColumns : function()
31239     {
31240         this.getContainerWidth();
31241         
31242 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31243 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31244 //            return;
31245 //        }
31246         
31247         var boxWidth = this.boxWidth + this.padWidth;
31248         
31249         if(this.containerWidth < this.boxWidth){
31250             boxWidth = this.containerWidth
31251         }
31252         
31253         var containerWidth = this.containerWidth;
31254         
31255         var cols = Math.floor(containerWidth / boxWidth);
31256         
31257         this.cols = Math.max( cols, 1 );
31258         
31259         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31260         
31261         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31262         
31263         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31264         
31265         this.colWidth = boxWidth + avail - this.padWidth;
31266         
31267         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31268         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31269     },
31270     
31271     horizontalMeasureColumns : function()
31272     {
31273         this.getContainerWidth();
31274         
31275         var boxWidth = this.boxWidth;
31276         
31277         if(this.containerWidth < boxWidth){
31278             boxWidth = this.containerWidth;
31279         }
31280         
31281         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31282         
31283         this.el.setHeight(boxWidth);
31284         
31285     },
31286     
31287     getContainerWidth : function()
31288     {
31289         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31290     },
31291     
31292     layoutItems : function( isInstant )
31293     {
31294         Roo.log(this.bricks);
31295         
31296         var items = Roo.apply([], this.bricks);
31297         
31298         if(this.isHorizontal){
31299             this._horizontalLayoutItems( items , isInstant );
31300             return;
31301         }
31302         
31303 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31304 //            this._verticalAlternativeLayoutItems( items , isInstant );
31305 //            return;
31306 //        }
31307         
31308         this._verticalLayoutItems( items , isInstant );
31309         
31310     },
31311     
31312     _verticalLayoutItems : function ( items , isInstant)
31313     {
31314         if ( !items || !items.length ) {
31315             return;
31316         }
31317         
31318         var standard = [
31319             ['xs', 'xs', 'xs', 'tall'],
31320             ['xs', 'xs', 'tall'],
31321             ['xs', 'xs', 'sm'],
31322             ['xs', 'xs', 'xs'],
31323             ['xs', 'tall'],
31324             ['xs', 'sm'],
31325             ['xs', 'xs'],
31326             ['xs'],
31327             
31328             ['sm', 'xs', 'xs'],
31329             ['sm', 'xs'],
31330             ['sm'],
31331             
31332             ['tall', 'xs', 'xs', 'xs'],
31333             ['tall', 'xs', 'xs'],
31334             ['tall', 'xs'],
31335             ['tall']
31336             
31337         ];
31338         
31339         var queue = [];
31340         
31341         var boxes = [];
31342         
31343         var box = [];
31344         
31345         Roo.each(items, function(item, k){
31346             
31347             switch (item.size) {
31348                 // these layouts take up a full box,
31349                 case 'md' :
31350                 case 'md-left' :
31351                 case 'md-right' :
31352                 case 'wide' :
31353                     
31354                     if(box.length){
31355                         boxes.push(box);
31356                         box = [];
31357                     }
31358                     
31359                     boxes.push([item]);
31360                     
31361                     break;
31362                     
31363                 case 'xs' :
31364                 case 'sm' :
31365                 case 'tall' :
31366                     
31367                     box.push(item);
31368                     
31369                     break;
31370                 default :
31371                     break;
31372                     
31373             }
31374             
31375         }, this);
31376         
31377         if(box.length){
31378             boxes.push(box);
31379             box = [];
31380         }
31381         
31382         var filterPattern = function(box, length)
31383         {
31384             if(!box.length){
31385                 return;
31386             }
31387             
31388             var match = false;
31389             
31390             var pattern = box.slice(0, length);
31391             
31392             var format = [];
31393             
31394             Roo.each(pattern, function(i){
31395                 format.push(i.size);
31396             }, this);
31397             
31398             Roo.each(standard, function(s){
31399                 
31400                 if(String(s) != String(format)){
31401                     return;
31402                 }
31403                 
31404                 match = true;
31405                 return false;
31406                 
31407             }, this);
31408             
31409             if(!match && length == 1){
31410                 return;
31411             }
31412             
31413             if(!match){
31414                 filterPattern(box, length - 1);
31415                 return;
31416             }
31417                 
31418             queue.push(pattern);
31419
31420             box = box.slice(length, box.length);
31421
31422             filterPattern(box, 4);
31423
31424             return;
31425             
31426         }
31427         
31428         Roo.each(boxes, function(box, k){
31429             
31430             if(!box.length){
31431                 return;
31432             }
31433             
31434             if(box.length == 1){
31435                 queue.push(box);
31436                 return;
31437             }
31438             
31439             filterPattern(box, 4);
31440             
31441         }, this);
31442         
31443         this._processVerticalLayoutQueue( queue, isInstant );
31444         
31445     },
31446     
31447 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31448 //    {
31449 //        if ( !items || !items.length ) {
31450 //            return;
31451 //        }
31452 //
31453 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31454 //        
31455 //    },
31456     
31457     _horizontalLayoutItems : function ( items , isInstant)
31458     {
31459         if ( !items || !items.length || items.length < 3) {
31460             return;
31461         }
31462         
31463         items.reverse();
31464         
31465         var eItems = items.slice(0, 3);
31466         
31467         items = items.slice(3, items.length);
31468         
31469         var standard = [
31470             ['xs', 'xs', 'xs', 'wide'],
31471             ['xs', 'xs', 'wide'],
31472             ['xs', 'xs', 'sm'],
31473             ['xs', 'xs', 'xs'],
31474             ['xs', 'wide'],
31475             ['xs', 'sm'],
31476             ['xs', 'xs'],
31477             ['xs'],
31478             
31479             ['sm', 'xs', 'xs'],
31480             ['sm', 'xs'],
31481             ['sm'],
31482             
31483             ['wide', 'xs', 'xs', 'xs'],
31484             ['wide', 'xs', 'xs'],
31485             ['wide', 'xs'],
31486             ['wide'],
31487             
31488             ['wide-thin']
31489         ];
31490         
31491         var queue = [];
31492         
31493         var boxes = [];
31494         
31495         var box = [];
31496         
31497         Roo.each(items, function(item, k){
31498             
31499             switch (item.size) {
31500                 case 'md' :
31501                 case 'md-left' :
31502                 case 'md-right' :
31503                 case 'tall' :
31504                     
31505                     if(box.length){
31506                         boxes.push(box);
31507                         box = [];
31508                     }
31509                     
31510                     boxes.push([item]);
31511                     
31512                     break;
31513                     
31514                 case 'xs' :
31515                 case 'sm' :
31516                 case 'wide' :
31517                 case 'wide-thin' :
31518                     
31519                     box.push(item);
31520                     
31521                     break;
31522                 default :
31523                     break;
31524                     
31525             }
31526             
31527         }, this);
31528         
31529         if(box.length){
31530             boxes.push(box);
31531             box = [];
31532         }
31533         
31534         var filterPattern = function(box, length)
31535         {
31536             if(!box.length){
31537                 return;
31538             }
31539             
31540             var match = false;
31541             
31542             var pattern = box.slice(0, length);
31543             
31544             var format = [];
31545             
31546             Roo.each(pattern, function(i){
31547                 format.push(i.size);
31548             }, this);
31549             
31550             Roo.each(standard, function(s){
31551                 
31552                 if(String(s) != String(format)){
31553                     return;
31554                 }
31555                 
31556                 match = true;
31557                 return false;
31558                 
31559             }, this);
31560             
31561             if(!match && length == 1){
31562                 return;
31563             }
31564             
31565             if(!match){
31566                 filterPattern(box, length - 1);
31567                 return;
31568             }
31569                 
31570             queue.push(pattern);
31571
31572             box = box.slice(length, box.length);
31573
31574             filterPattern(box, 4);
31575
31576             return;
31577             
31578         }
31579         
31580         Roo.each(boxes, function(box, k){
31581             
31582             if(!box.length){
31583                 return;
31584             }
31585             
31586             if(box.length == 1){
31587                 queue.push(box);
31588                 return;
31589             }
31590             
31591             filterPattern(box, 4);
31592             
31593         }, this);
31594         
31595         
31596         var prune = [];
31597         
31598         var pos = this.el.getBox(true);
31599         
31600         var minX = pos.x;
31601         
31602         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31603         
31604         var hit_end = false;
31605         
31606         Roo.each(queue, function(box){
31607             
31608             if(hit_end){
31609                 
31610                 Roo.each(box, function(b){
31611                 
31612                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31613                     b.el.hide();
31614
31615                 }, this);
31616
31617                 return;
31618             }
31619             
31620             var mx = 0;
31621             
31622             Roo.each(box, function(b){
31623                 
31624                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31625                 b.el.show();
31626
31627                 mx = Math.max(mx, b.x);
31628                 
31629             }, this);
31630             
31631             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31632             
31633             if(maxX < minX){
31634                 
31635                 Roo.each(box, function(b){
31636                 
31637                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31638                     b.el.hide();
31639                     
31640                 }, this);
31641                 
31642                 hit_end = true;
31643                 
31644                 return;
31645             }
31646             
31647             prune.push(box);
31648             
31649         }, this);
31650         
31651         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31652     },
31653     
31654     /** Sets position of item in DOM
31655     * @param {Element} item
31656     * @param {Number} x - horizontal position
31657     * @param {Number} y - vertical position
31658     * @param {Boolean} isInstant - disables transitions
31659     */
31660     _processVerticalLayoutQueue : function( queue, isInstant )
31661     {
31662         var pos = this.el.getBox(true);
31663         var x = pos.x;
31664         var y = pos.y;
31665         var maxY = [];
31666         
31667         for (var i = 0; i < this.cols; i++){
31668             maxY[i] = pos.y;
31669         }
31670         
31671         Roo.each(queue, function(box, k){
31672             
31673             var col = k % this.cols;
31674             
31675             Roo.each(box, function(b,kk){
31676                 
31677                 b.el.position('absolute');
31678                 
31679                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31680                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31681                 
31682                 if(b.size == 'md-left' || b.size == 'md-right'){
31683                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31684                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31685                 }
31686                 
31687                 b.el.setWidth(width);
31688                 b.el.setHeight(height);
31689                 // iframe?
31690                 b.el.select('iframe',true).setSize(width,height);
31691                 
31692             }, this);
31693             
31694             for (var i = 0; i < this.cols; i++){
31695                 
31696                 if(maxY[i] < maxY[col]){
31697                     col = i;
31698                     continue;
31699                 }
31700                 
31701                 col = Math.min(col, i);
31702                 
31703             }
31704             
31705             x = pos.x + col * (this.colWidth + this.padWidth);
31706             
31707             y = maxY[col];
31708             
31709             var positions = [];
31710             
31711             switch (box.length){
31712                 case 1 :
31713                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31714                     break;
31715                 case 2 :
31716                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31717                     break;
31718                 case 3 :
31719                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31720                     break;
31721                 case 4 :
31722                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31723                     break;
31724                 default :
31725                     break;
31726             }
31727             
31728             Roo.each(box, function(b,kk){
31729                 
31730                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31731                 
31732                 var sz = b.el.getSize();
31733                 
31734                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31735                 
31736             }, this);
31737             
31738         }, this);
31739         
31740         var mY = 0;
31741         
31742         for (var i = 0; i < this.cols; i++){
31743             mY = Math.max(mY, maxY[i]);
31744         }
31745         
31746         this.el.setHeight(mY - pos.y);
31747         
31748     },
31749     
31750 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31751 //    {
31752 //        var pos = this.el.getBox(true);
31753 //        var x = pos.x;
31754 //        var y = pos.y;
31755 //        var maxX = pos.right;
31756 //        
31757 //        var maxHeight = 0;
31758 //        
31759 //        Roo.each(items, function(item, k){
31760 //            
31761 //            var c = k % 2;
31762 //            
31763 //            item.el.position('absolute');
31764 //                
31765 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31766 //
31767 //            item.el.setWidth(width);
31768 //
31769 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31770 //
31771 //            item.el.setHeight(height);
31772 //            
31773 //            if(c == 0){
31774 //                item.el.setXY([x, y], isInstant ? false : true);
31775 //            } else {
31776 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31777 //            }
31778 //            
31779 //            y = y + height + this.alternativePadWidth;
31780 //            
31781 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31782 //            
31783 //        }, this);
31784 //        
31785 //        this.el.setHeight(maxHeight);
31786 //        
31787 //    },
31788     
31789     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31790     {
31791         var pos = this.el.getBox(true);
31792         
31793         var minX = pos.x;
31794         var minY = pos.y;
31795         
31796         var maxX = pos.right;
31797         
31798         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31799         
31800         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31801         
31802         Roo.each(queue, function(box, k){
31803             
31804             Roo.each(box, function(b, kk){
31805                 
31806                 b.el.position('absolute');
31807                 
31808                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31809                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31810                 
31811                 if(b.size == 'md-left' || b.size == 'md-right'){
31812                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31813                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31814                 }
31815                 
31816                 b.el.setWidth(width);
31817                 b.el.setHeight(height);
31818                 
31819             }, this);
31820             
31821             if(!box.length){
31822                 return;
31823             }
31824             
31825             var positions = [];
31826             
31827             switch (box.length){
31828                 case 1 :
31829                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31830                     break;
31831                 case 2 :
31832                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31833                     break;
31834                 case 3 :
31835                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31836                     break;
31837                 case 4 :
31838                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31839                     break;
31840                 default :
31841                     break;
31842             }
31843             
31844             Roo.each(box, function(b,kk){
31845                 
31846                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31847                 
31848                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31849                 
31850             }, this);
31851             
31852         }, this);
31853         
31854     },
31855     
31856     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31857     {
31858         Roo.each(eItems, function(b,k){
31859             
31860             b.size = (k == 0) ? 'sm' : 'xs';
31861             b.x = (k == 0) ? 2 : 1;
31862             b.y = (k == 0) ? 2 : 1;
31863             
31864             b.el.position('absolute');
31865             
31866             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31867                 
31868             b.el.setWidth(width);
31869             
31870             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31871             
31872             b.el.setHeight(height);
31873             
31874         }, this);
31875
31876         var positions = [];
31877         
31878         positions.push({
31879             x : maxX - this.unitWidth * 2 - this.gutter,
31880             y : minY
31881         });
31882         
31883         positions.push({
31884             x : maxX - this.unitWidth,
31885             y : minY + (this.unitWidth + this.gutter) * 2
31886         });
31887         
31888         positions.push({
31889             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31890             y : minY
31891         });
31892         
31893         Roo.each(eItems, function(b,k){
31894             
31895             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31896
31897         }, this);
31898         
31899     },
31900     
31901     getVerticalOneBoxColPositions : function(x, y, box)
31902     {
31903         var pos = [];
31904         
31905         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31906         
31907         if(box[0].size == 'md-left'){
31908             rand = 0;
31909         }
31910         
31911         if(box[0].size == 'md-right'){
31912             rand = 1;
31913         }
31914         
31915         pos.push({
31916             x : x + (this.unitWidth + this.gutter) * rand,
31917             y : y
31918         });
31919         
31920         return pos;
31921     },
31922     
31923     getVerticalTwoBoxColPositions : function(x, y, box)
31924     {
31925         var pos = [];
31926         
31927         if(box[0].size == 'xs'){
31928             
31929             pos.push({
31930                 x : x,
31931                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31932             });
31933
31934             pos.push({
31935                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31936                 y : y
31937             });
31938             
31939             return pos;
31940             
31941         }
31942         
31943         pos.push({
31944             x : x,
31945             y : y
31946         });
31947
31948         pos.push({
31949             x : x + (this.unitWidth + this.gutter) * 2,
31950             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31951         });
31952         
31953         return pos;
31954         
31955     },
31956     
31957     getVerticalThreeBoxColPositions : function(x, y, box)
31958     {
31959         var pos = [];
31960         
31961         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31962             
31963             pos.push({
31964                 x : x,
31965                 y : y
31966             });
31967
31968             pos.push({
31969                 x : x + (this.unitWidth + this.gutter) * 1,
31970                 y : y
31971             });
31972             
31973             pos.push({
31974                 x : x + (this.unitWidth + this.gutter) * 2,
31975                 y : y
31976             });
31977             
31978             return pos;
31979             
31980         }
31981         
31982         if(box[0].size == 'xs' && box[1].size == 'xs'){
31983             
31984             pos.push({
31985                 x : x,
31986                 y : y
31987             });
31988
31989             pos.push({
31990                 x : x,
31991                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31992             });
31993             
31994             pos.push({
31995                 x : x + (this.unitWidth + this.gutter) * 1,
31996                 y : y
31997             });
31998             
31999             return pos;
32000             
32001         }
32002         
32003         pos.push({
32004             x : x,
32005             y : y
32006         });
32007
32008         pos.push({
32009             x : x + (this.unitWidth + this.gutter) * 2,
32010             y : y
32011         });
32012
32013         pos.push({
32014             x : x + (this.unitWidth + this.gutter) * 2,
32015             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32016         });
32017             
32018         return pos;
32019         
32020     },
32021     
32022     getVerticalFourBoxColPositions : function(x, y, box)
32023     {
32024         var pos = [];
32025         
32026         if(box[0].size == 'xs'){
32027             
32028             pos.push({
32029                 x : x,
32030                 y : y
32031             });
32032
32033             pos.push({
32034                 x : x,
32035                 y : y + (this.unitHeight + this.gutter) * 1
32036             });
32037             
32038             pos.push({
32039                 x : x,
32040                 y : y + (this.unitHeight + this.gutter) * 2
32041             });
32042             
32043             pos.push({
32044                 x : x + (this.unitWidth + this.gutter) * 1,
32045                 y : y
32046             });
32047             
32048             return pos;
32049             
32050         }
32051         
32052         pos.push({
32053             x : x,
32054             y : y
32055         });
32056
32057         pos.push({
32058             x : x + (this.unitWidth + this.gutter) * 2,
32059             y : y
32060         });
32061
32062         pos.push({
32063             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32064             y : y + (this.unitHeight + this.gutter) * 1
32065         });
32066
32067         pos.push({
32068             x : x + (this.unitWidth + this.gutter) * 2,
32069             y : y + (this.unitWidth + this.gutter) * 2
32070         });
32071
32072         return pos;
32073         
32074     },
32075     
32076     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32077     {
32078         var pos = [];
32079         
32080         if(box[0].size == 'md-left'){
32081             pos.push({
32082                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32083                 y : minY
32084             });
32085             
32086             return pos;
32087         }
32088         
32089         if(box[0].size == 'md-right'){
32090             pos.push({
32091                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32092                 y : minY + (this.unitWidth + this.gutter) * 1
32093             });
32094             
32095             return pos;
32096         }
32097         
32098         var rand = Math.floor(Math.random() * (4 - box[0].y));
32099         
32100         pos.push({
32101             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32102             y : minY + (this.unitWidth + this.gutter) * rand
32103         });
32104         
32105         return pos;
32106         
32107     },
32108     
32109     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32110     {
32111         var pos = [];
32112         
32113         if(box[0].size == 'xs'){
32114             
32115             pos.push({
32116                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32117                 y : minY
32118             });
32119
32120             pos.push({
32121                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32122                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32123             });
32124             
32125             return pos;
32126             
32127         }
32128         
32129         pos.push({
32130             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32131             y : minY
32132         });
32133
32134         pos.push({
32135             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32136             y : minY + (this.unitWidth + this.gutter) * 2
32137         });
32138         
32139         return pos;
32140         
32141     },
32142     
32143     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32144     {
32145         var pos = [];
32146         
32147         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32148             
32149             pos.push({
32150                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32151                 y : minY
32152             });
32153
32154             pos.push({
32155                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32156                 y : minY + (this.unitWidth + this.gutter) * 1
32157             });
32158             
32159             pos.push({
32160                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32161                 y : minY + (this.unitWidth + this.gutter) * 2
32162             });
32163             
32164             return pos;
32165             
32166         }
32167         
32168         if(box[0].size == 'xs' && box[1].size == 'xs'){
32169             
32170             pos.push({
32171                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32172                 y : minY
32173             });
32174
32175             pos.push({
32176                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32177                 y : minY
32178             });
32179             
32180             pos.push({
32181                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32182                 y : minY + (this.unitWidth + this.gutter) * 1
32183             });
32184             
32185             return pos;
32186             
32187         }
32188         
32189         pos.push({
32190             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32191             y : minY
32192         });
32193
32194         pos.push({
32195             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32196             y : minY + (this.unitWidth + this.gutter) * 2
32197         });
32198
32199         pos.push({
32200             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32201             y : minY + (this.unitWidth + this.gutter) * 2
32202         });
32203             
32204         return pos;
32205         
32206     },
32207     
32208     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32209     {
32210         var pos = [];
32211         
32212         if(box[0].size == 'xs'){
32213             
32214             pos.push({
32215                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32216                 y : minY
32217             });
32218
32219             pos.push({
32220                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32221                 y : minY
32222             });
32223             
32224             pos.push({
32225                 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),
32226                 y : minY
32227             });
32228             
32229             pos.push({
32230                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32231                 y : minY + (this.unitWidth + this.gutter) * 1
32232             });
32233             
32234             return pos;
32235             
32236         }
32237         
32238         pos.push({
32239             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32240             y : minY
32241         });
32242         
32243         pos.push({
32244             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32245             y : minY + (this.unitWidth + this.gutter) * 2
32246         });
32247         
32248         pos.push({
32249             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32250             y : minY + (this.unitWidth + this.gutter) * 2
32251         });
32252         
32253         pos.push({
32254             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),
32255             y : minY + (this.unitWidth + this.gutter) * 2
32256         });
32257
32258         return pos;
32259         
32260     },
32261     
32262     /**
32263     * remove a Masonry Brick
32264     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32265     */
32266     removeBrick : function(brick_id)
32267     {
32268         if (!brick_id) {
32269             return;
32270         }
32271         
32272         for (var i = 0; i<this.bricks.length; i++) {
32273             if (this.bricks[i].id == brick_id) {
32274                 this.bricks.splice(i,1);
32275                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32276                 this.initial();
32277             }
32278         }
32279     },
32280     
32281     /**
32282     * adds a Masonry Brick
32283     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32284     */
32285     addBrick : function(cfg)
32286     {
32287         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32288         //this.register(cn);
32289         cn.parentId = this.id;
32290         cn.render(this.el);
32291         return cn;
32292     },
32293     
32294     /**
32295     * register a Masonry Brick
32296     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32297     */
32298     
32299     register : function(brick)
32300     {
32301         this.bricks.push(brick);
32302         brick.masonryId = this.id;
32303     },
32304     
32305     /**
32306     * clear all the Masonry Brick
32307     */
32308     clearAll : function()
32309     {
32310         this.bricks = [];
32311         //this.getChildContainer().dom.innerHTML = "";
32312         this.el.dom.innerHTML = '';
32313     },
32314     
32315     getSelected : function()
32316     {
32317         if (!this.selectedBrick) {
32318             return false;
32319         }
32320         
32321         return this.selectedBrick;
32322     }
32323 });
32324
32325 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32326     
32327     groups: {},
32328      /**
32329     * register a Masonry Layout
32330     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32331     */
32332     
32333     register : function(layout)
32334     {
32335         this.groups[layout.id] = layout;
32336     },
32337     /**
32338     * fetch a  Masonry Layout based on the masonry layout ID
32339     * @param {string} the masonry layout to add
32340     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32341     */
32342     
32343     get: function(layout_id) {
32344         if (typeof(this.groups[layout_id]) == 'undefined') {
32345             return false;
32346         }
32347         return this.groups[layout_id] ;
32348     }
32349     
32350     
32351     
32352 });
32353
32354  
32355
32356  /**
32357  *
32358  * This is based on 
32359  * http://masonry.desandro.com
32360  *
32361  * The idea is to render all the bricks based on vertical width...
32362  *
32363  * The original code extends 'outlayer' - we might need to use that....
32364  * 
32365  */
32366
32367
32368 /**
32369  * @class Roo.bootstrap.LayoutMasonryAuto
32370  * @extends Roo.bootstrap.Component
32371  * Bootstrap Layout Masonry class
32372  * 
32373  * @constructor
32374  * Create a new Element
32375  * @param {Object} config The config object
32376  */
32377
32378 Roo.bootstrap.LayoutMasonryAuto = function(config){
32379     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32380 };
32381
32382 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32383     
32384       /**
32385      * @cfg {Boolean} isFitWidth  - resize the width..
32386      */   
32387     isFitWidth : false,  // options..
32388     /**
32389      * @cfg {Boolean} isOriginLeft = left align?
32390      */   
32391     isOriginLeft : true,
32392     /**
32393      * @cfg {Boolean} isOriginTop = top align?
32394      */   
32395     isOriginTop : false,
32396     /**
32397      * @cfg {Boolean} isLayoutInstant = no animation?
32398      */   
32399     isLayoutInstant : false, // needed?
32400     /**
32401      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32402      */   
32403     isResizingContainer : true,
32404     /**
32405      * @cfg {Number} columnWidth  width of the columns 
32406      */   
32407     
32408     columnWidth : 0,
32409     
32410     /**
32411      * @cfg {Number} maxCols maximum number of columns
32412      */   
32413     
32414     maxCols: 0,
32415     /**
32416      * @cfg {Number} padHeight padding below box..
32417      */   
32418     
32419     padHeight : 10, 
32420     
32421     /**
32422      * @cfg {Boolean} isAutoInitial defalut true
32423      */   
32424     
32425     isAutoInitial : true, 
32426     
32427     // private?
32428     gutter : 0,
32429     
32430     containerWidth: 0,
32431     initialColumnWidth : 0,
32432     currentSize : null,
32433     
32434     colYs : null, // array.
32435     maxY : 0,
32436     padWidth: 10,
32437     
32438     
32439     tag: 'div',
32440     cls: '',
32441     bricks: null, //CompositeElement
32442     cols : 0, // array?
32443     // element : null, // wrapped now this.el
32444     _isLayoutInited : null, 
32445     
32446     
32447     getAutoCreate : function(){
32448         
32449         var cfg = {
32450             tag: this.tag,
32451             cls: 'blog-masonary-wrapper ' + this.cls,
32452             cn : {
32453                 cls : 'mas-boxes masonary'
32454             }
32455         };
32456         
32457         return cfg;
32458     },
32459     
32460     getChildContainer: function( )
32461     {
32462         if (this.boxesEl) {
32463             return this.boxesEl;
32464         }
32465         
32466         this.boxesEl = this.el.select('.mas-boxes').first();
32467         
32468         return this.boxesEl;
32469     },
32470     
32471     
32472     initEvents : function()
32473     {
32474         var _this = this;
32475         
32476         if(this.isAutoInitial){
32477             Roo.log('hook children rendered');
32478             this.on('childrenrendered', function() {
32479                 Roo.log('children rendered');
32480                 _this.initial();
32481             } ,this);
32482         }
32483         
32484     },
32485     
32486     initial : function()
32487     {
32488         this.reloadItems();
32489
32490         this.currentSize = this.el.getBox(true);
32491
32492         /// was window resize... - let's see if this works..
32493         Roo.EventManager.onWindowResize(this.resize, this); 
32494
32495         if(!this.isAutoInitial){
32496             this.layout();
32497             return;
32498         }
32499         
32500         this.layout.defer(500,this);
32501     },
32502     
32503     reloadItems: function()
32504     {
32505         this.bricks = this.el.select('.masonry-brick', true);
32506         
32507         this.bricks.each(function(b) {
32508             //Roo.log(b.getSize());
32509             if (!b.attr('originalwidth')) {
32510                 b.attr('originalwidth',  b.getSize().width);
32511             }
32512             
32513         });
32514         
32515         Roo.log(this.bricks.elements.length);
32516     },
32517     
32518     resize : function()
32519     {
32520         Roo.log('resize');
32521         var cs = this.el.getBox(true);
32522         
32523         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32524             Roo.log("no change in with or X");
32525             return;
32526         }
32527         this.currentSize = cs;
32528         this.layout();
32529     },
32530     
32531     layout : function()
32532     {
32533          Roo.log('layout');
32534         this._resetLayout();
32535         //this._manageStamps();
32536       
32537         // don't animate first layout
32538         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32539         this.layoutItems( isInstant );
32540       
32541         // flag for initalized
32542         this._isLayoutInited = true;
32543     },
32544     
32545     layoutItems : function( isInstant )
32546     {
32547         //var items = this._getItemsForLayout( this.items );
32548         // original code supports filtering layout items.. we just ignore it..
32549         
32550         this._layoutItems( this.bricks , isInstant );
32551       
32552         this._postLayout();
32553     },
32554     _layoutItems : function ( items , isInstant)
32555     {
32556        //this.fireEvent( 'layout', this, items );
32557     
32558
32559         if ( !items || !items.elements.length ) {
32560           // no items, emit event with empty array
32561             return;
32562         }
32563
32564         var queue = [];
32565         items.each(function(item) {
32566             Roo.log("layout item");
32567             Roo.log(item);
32568             // get x/y object from method
32569             var position = this._getItemLayoutPosition( item );
32570             // enqueue
32571             position.item = item;
32572             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32573             queue.push( position );
32574         }, this);
32575       
32576         this._processLayoutQueue( queue );
32577     },
32578     /** Sets position of item in DOM
32579     * @param {Element} item
32580     * @param {Number} x - horizontal position
32581     * @param {Number} y - vertical position
32582     * @param {Boolean} isInstant - disables transitions
32583     */
32584     _processLayoutQueue : function( queue )
32585     {
32586         for ( var i=0, len = queue.length; i < len; i++ ) {
32587             var obj = queue[i];
32588             obj.item.position('absolute');
32589             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32590         }
32591     },
32592       
32593     
32594     /**
32595     * Any logic you want to do after each layout,
32596     * i.e. size the container
32597     */
32598     _postLayout : function()
32599     {
32600         this.resizeContainer();
32601     },
32602     
32603     resizeContainer : function()
32604     {
32605         if ( !this.isResizingContainer ) {
32606             return;
32607         }
32608         var size = this._getContainerSize();
32609         if ( size ) {
32610             this.el.setSize(size.width,size.height);
32611             this.boxesEl.setSize(size.width,size.height);
32612         }
32613     },
32614     
32615     
32616     
32617     _resetLayout : function()
32618     {
32619         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32620         this.colWidth = this.el.getWidth();
32621         //this.gutter = this.el.getWidth(); 
32622         
32623         this.measureColumns();
32624
32625         // reset column Y
32626         var i = this.cols;
32627         this.colYs = [];
32628         while (i--) {
32629             this.colYs.push( 0 );
32630         }
32631     
32632         this.maxY = 0;
32633     },
32634
32635     measureColumns : function()
32636     {
32637         this.getContainerWidth();
32638       // if columnWidth is 0, default to outerWidth of first item
32639         if ( !this.columnWidth ) {
32640             var firstItem = this.bricks.first();
32641             Roo.log(firstItem);
32642             this.columnWidth  = this.containerWidth;
32643             if (firstItem && firstItem.attr('originalwidth') ) {
32644                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32645             }
32646             // columnWidth fall back to item of first element
32647             Roo.log("set column width?");
32648                         this.initialColumnWidth = this.columnWidth  ;
32649
32650             // if first elem has no width, default to size of container
32651             
32652         }
32653         
32654         
32655         if (this.initialColumnWidth) {
32656             this.columnWidth = this.initialColumnWidth;
32657         }
32658         
32659         
32660             
32661         // column width is fixed at the top - however if container width get's smaller we should
32662         // reduce it...
32663         
32664         // this bit calcs how man columns..
32665             
32666         var columnWidth = this.columnWidth += this.gutter;
32667       
32668         // calculate columns
32669         var containerWidth = this.containerWidth + this.gutter;
32670         
32671         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32672         // fix rounding errors, typically with gutters
32673         var excess = columnWidth - containerWidth % columnWidth;
32674         
32675         
32676         // if overshoot is less than a pixel, round up, otherwise floor it
32677         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32678         cols = Math[ mathMethod ]( cols );
32679         this.cols = Math.max( cols, 1 );
32680         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32681         
32682          // padding positioning..
32683         var totalColWidth = this.cols * this.columnWidth;
32684         var padavail = this.containerWidth - totalColWidth;
32685         // so for 2 columns - we need 3 'pads'
32686         
32687         var padNeeded = (1+this.cols) * this.padWidth;
32688         
32689         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32690         
32691         this.columnWidth += padExtra
32692         //this.padWidth = Math.floor(padavail /  ( this.cols));
32693         
32694         // adjust colum width so that padding is fixed??
32695         
32696         // we have 3 columns ... total = width * 3
32697         // we have X left over... that should be used by 
32698         
32699         //if (this.expandC) {
32700             
32701         //}
32702         
32703         
32704         
32705     },
32706     
32707     getContainerWidth : function()
32708     {
32709        /* // container is parent if fit width
32710         var container = this.isFitWidth ? this.element.parentNode : this.element;
32711         // check that this.size and size are there
32712         // IE8 triggers resize on body size change, so they might not be
32713         
32714         var size = getSize( container );  //FIXME
32715         this.containerWidth = size && size.innerWidth; //FIXME
32716         */
32717          
32718         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32719         
32720     },
32721     
32722     _getItemLayoutPosition : function( item )  // what is item?
32723     {
32724         // we resize the item to our columnWidth..
32725       
32726         item.setWidth(this.columnWidth);
32727         item.autoBoxAdjust  = false;
32728         
32729         var sz = item.getSize();
32730  
32731         // how many columns does this brick span
32732         var remainder = this.containerWidth % this.columnWidth;
32733         
32734         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32735         // round if off by 1 pixel, otherwise use ceil
32736         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32737         colSpan = Math.min( colSpan, this.cols );
32738         
32739         // normally this should be '1' as we dont' currently allow multi width columns..
32740         
32741         var colGroup = this._getColGroup( colSpan );
32742         // get the minimum Y value from the columns
32743         var minimumY = Math.min.apply( Math, colGroup );
32744         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32745         
32746         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32747          
32748         // position the brick
32749         var position = {
32750             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32751             y: this.currentSize.y + minimumY + this.padHeight
32752         };
32753         
32754         Roo.log(position);
32755         // apply setHeight to necessary columns
32756         var setHeight = minimumY + sz.height + this.padHeight;
32757         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32758         
32759         var setSpan = this.cols + 1 - colGroup.length;
32760         for ( var i = 0; i < setSpan; i++ ) {
32761           this.colYs[ shortColIndex + i ] = setHeight ;
32762         }
32763       
32764         return position;
32765     },
32766     
32767     /**
32768      * @param {Number} colSpan - number of columns the element spans
32769      * @returns {Array} colGroup
32770      */
32771     _getColGroup : function( colSpan )
32772     {
32773         if ( colSpan < 2 ) {
32774           // if brick spans only one column, use all the column Ys
32775           return this.colYs;
32776         }
32777       
32778         var colGroup = [];
32779         // how many different places could this brick fit horizontally
32780         var groupCount = this.cols + 1 - colSpan;
32781         // for each group potential horizontal position
32782         for ( var i = 0; i < groupCount; i++ ) {
32783           // make an array of colY values for that one group
32784           var groupColYs = this.colYs.slice( i, i + colSpan );
32785           // and get the max value of the array
32786           colGroup[i] = Math.max.apply( Math, groupColYs );
32787         }
32788         return colGroup;
32789     },
32790     /*
32791     _manageStamp : function( stamp )
32792     {
32793         var stampSize =  stamp.getSize();
32794         var offset = stamp.getBox();
32795         // get the columns that this stamp affects
32796         var firstX = this.isOriginLeft ? offset.x : offset.right;
32797         var lastX = firstX + stampSize.width;
32798         var firstCol = Math.floor( firstX / this.columnWidth );
32799         firstCol = Math.max( 0, firstCol );
32800         
32801         var lastCol = Math.floor( lastX / this.columnWidth );
32802         // lastCol should not go over if multiple of columnWidth #425
32803         lastCol -= lastX % this.columnWidth ? 0 : 1;
32804         lastCol = Math.min( this.cols - 1, lastCol );
32805         
32806         // set colYs to bottom of the stamp
32807         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32808             stampSize.height;
32809             
32810         for ( var i = firstCol; i <= lastCol; i++ ) {
32811           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32812         }
32813     },
32814     */
32815     
32816     _getContainerSize : function()
32817     {
32818         this.maxY = Math.max.apply( Math, this.colYs );
32819         var size = {
32820             height: this.maxY
32821         };
32822       
32823         if ( this.isFitWidth ) {
32824             size.width = this._getContainerFitWidth();
32825         }
32826       
32827         return size;
32828     },
32829     
32830     _getContainerFitWidth : function()
32831     {
32832         var unusedCols = 0;
32833         // count unused columns
32834         var i = this.cols;
32835         while ( --i ) {
32836           if ( this.colYs[i] !== 0 ) {
32837             break;
32838           }
32839           unusedCols++;
32840         }
32841         // fit container to columns that have been used
32842         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32843     },
32844     
32845     needsResizeLayout : function()
32846     {
32847         var previousWidth = this.containerWidth;
32848         this.getContainerWidth();
32849         return previousWidth !== this.containerWidth;
32850     }
32851  
32852 });
32853
32854  
32855
32856  /*
32857  * - LGPL
32858  *
32859  * element
32860  * 
32861  */
32862
32863 /**
32864  * @class Roo.bootstrap.MasonryBrick
32865  * @extends Roo.bootstrap.Component
32866  * Bootstrap MasonryBrick class
32867  * 
32868  * @constructor
32869  * Create a new MasonryBrick
32870  * @param {Object} config The config object
32871  */
32872
32873 Roo.bootstrap.MasonryBrick = function(config){
32874     
32875     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32876     
32877     Roo.bootstrap.MasonryBrick.register(this);
32878     
32879     this.addEvents({
32880         // raw events
32881         /**
32882          * @event click
32883          * When a MasonryBrick is clcik
32884          * @param {Roo.bootstrap.MasonryBrick} this
32885          * @param {Roo.EventObject} e
32886          */
32887         "click" : true
32888     });
32889 };
32890
32891 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32892     
32893     /**
32894      * @cfg {String} title
32895      */   
32896     title : '',
32897     /**
32898      * @cfg {String} html
32899      */   
32900     html : '',
32901     /**
32902      * @cfg {String} bgimage
32903      */   
32904     bgimage : '',
32905     /**
32906      * @cfg {String} videourl
32907      */   
32908     videourl : '',
32909     /**
32910      * @cfg {String} cls
32911      */   
32912     cls : '',
32913     /**
32914      * @cfg {String} href
32915      */   
32916     href : '',
32917     /**
32918      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32919      */   
32920     size : 'xs',
32921     
32922     /**
32923      * @cfg {String} placetitle (center|bottom)
32924      */   
32925     placetitle : '',
32926     
32927     /**
32928      * @cfg {Boolean} isFitContainer defalut true
32929      */   
32930     isFitContainer : true, 
32931     
32932     /**
32933      * @cfg {Boolean} preventDefault defalut false
32934      */   
32935     preventDefault : false, 
32936     
32937     /**
32938      * @cfg {Boolean} inverse defalut false
32939      */   
32940     maskInverse : false, 
32941     
32942     getAutoCreate : function()
32943     {
32944         if(!this.isFitContainer){
32945             return this.getSplitAutoCreate();
32946         }
32947         
32948         var cls = 'masonry-brick masonry-brick-full';
32949         
32950         if(this.href.length){
32951             cls += ' masonry-brick-link';
32952         }
32953         
32954         if(this.bgimage.length){
32955             cls += ' masonry-brick-image';
32956         }
32957         
32958         if(this.maskInverse){
32959             cls += ' mask-inverse';
32960         }
32961         
32962         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32963             cls += ' enable-mask';
32964         }
32965         
32966         if(this.size){
32967             cls += ' masonry-' + this.size + '-brick';
32968         }
32969         
32970         if(this.placetitle.length){
32971             
32972             switch (this.placetitle) {
32973                 case 'center' :
32974                     cls += ' masonry-center-title';
32975                     break;
32976                 case 'bottom' :
32977                     cls += ' masonry-bottom-title';
32978                     break;
32979                 default:
32980                     break;
32981             }
32982             
32983         } else {
32984             if(!this.html.length && !this.bgimage.length){
32985                 cls += ' masonry-center-title';
32986             }
32987
32988             if(!this.html.length && this.bgimage.length){
32989                 cls += ' masonry-bottom-title';
32990             }
32991         }
32992         
32993         if(this.cls){
32994             cls += ' ' + this.cls;
32995         }
32996         
32997         var cfg = {
32998             tag: (this.href.length) ? 'a' : 'div',
32999             cls: cls,
33000             cn: [
33001                 {
33002                     tag: 'div',
33003                     cls: 'masonry-brick-mask'
33004                 },
33005                 {
33006                     tag: 'div',
33007                     cls: 'masonry-brick-paragraph',
33008                     cn: []
33009                 }
33010             ]
33011         };
33012         
33013         if(this.href.length){
33014             cfg.href = this.href;
33015         }
33016         
33017         var cn = cfg.cn[1].cn;
33018         
33019         if(this.title.length){
33020             cn.push({
33021                 tag: 'h4',
33022                 cls: 'masonry-brick-title',
33023                 html: this.title
33024             });
33025         }
33026         
33027         if(this.html.length){
33028             cn.push({
33029                 tag: 'p',
33030                 cls: 'masonry-brick-text',
33031                 html: this.html
33032             });
33033         }
33034         
33035         if (!this.title.length && !this.html.length) {
33036             cfg.cn[1].cls += ' hide';
33037         }
33038         
33039         if(this.bgimage.length){
33040             cfg.cn.push({
33041                 tag: 'img',
33042                 cls: 'masonry-brick-image-view',
33043                 src: this.bgimage
33044             });
33045         }
33046         
33047         if(this.videourl.length){
33048             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33049             // youtube support only?
33050             cfg.cn.push({
33051                 tag: 'iframe',
33052                 cls: 'masonry-brick-image-view',
33053                 src: vurl,
33054                 frameborder : 0,
33055                 allowfullscreen : true
33056             });
33057         }
33058         
33059         return cfg;
33060         
33061     },
33062     
33063     getSplitAutoCreate : function()
33064     {
33065         var cls = 'masonry-brick masonry-brick-split';
33066         
33067         if(this.href.length){
33068             cls += ' masonry-brick-link';
33069         }
33070         
33071         if(this.bgimage.length){
33072             cls += ' masonry-brick-image';
33073         }
33074         
33075         if(this.size){
33076             cls += ' masonry-' + this.size + '-brick';
33077         }
33078         
33079         switch (this.placetitle) {
33080             case 'center' :
33081                 cls += ' masonry-center-title';
33082                 break;
33083             case 'bottom' :
33084                 cls += ' masonry-bottom-title';
33085                 break;
33086             default:
33087                 if(!this.bgimage.length){
33088                     cls += ' masonry-center-title';
33089                 }
33090
33091                 if(this.bgimage.length){
33092                     cls += ' masonry-bottom-title';
33093                 }
33094                 break;
33095         }
33096         
33097         if(this.cls){
33098             cls += ' ' + this.cls;
33099         }
33100         
33101         var cfg = {
33102             tag: (this.href.length) ? 'a' : 'div',
33103             cls: cls,
33104             cn: [
33105                 {
33106                     tag: 'div',
33107                     cls: 'masonry-brick-split-head',
33108                     cn: [
33109                         {
33110                             tag: 'div',
33111                             cls: 'masonry-brick-paragraph',
33112                             cn: []
33113                         }
33114                     ]
33115                 },
33116                 {
33117                     tag: 'div',
33118                     cls: 'masonry-brick-split-body',
33119                     cn: []
33120                 }
33121             ]
33122         };
33123         
33124         if(this.href.length){
33125             cfg.href = this.href;
33126         }
33127         
33128         if(this.title.length){
33129             cfg.cn[0].cn[0].cn.push({
33130                 tag: 'h4',
33131                 cls: 'masonry-brick-title',
33132                 html: this.title
33133             });
33134         }
33135         
33136         if(this.html.length){
33137             cfg.cn[1].cn.push({
33138                 tag: 'p',
33139                 cls: 'masonry-brick-text',
33140                 html: this.html
33141             });
33142         }
33143
33144         if(this.bgimage.length){
33145             cfg.cn[0].cn.push({
33146                 tag: 'img',
33147                 cls: 'masonry-brick-image-view',
33148                 src: this.bgimage
33149             });
33150         }
33151         
33152         if(this.videourl.length){
33153             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33154             // youtube support only?
33155             cfg.cn[0].cn.cn.push({
33156                 tag: 'iframe',
33157                 cls: 'masonry-brick-image-view',
33158                 src: vurl,
33159                 frameborder : 0,
33160                 allowfullscreen : true
33161             });
33162         }
33163         
33164         return cfg;
33165     },
33166     
33167     initEvents: function() 
33168     {
33169         switch (this.size) {
33170             case 'xs' :
33171                 this.x = 1;
33172                 this.y = 1;
33173                 break;
33174             case 'sm' :
33175                 this.x = 2;
33176                 this.y = 2;
33177                 break;
33178             case 'md' :
33179             case 'md-left' :
33180             case 'md-right' :
33181                 this.x = 3;
33182                 this.y = 3;
33183                 break;
33184             case 'tall' :
33185                 this.x = 2;
33186                 this.y = 3;
33187                 break;
33188             case 'wide' :
33189                 this.x = 3;
33190                 this.y = 2;
33191                 break;
33192             case 'wide-thin' :
33193                 this.x = 3;
33194                 this.y = 1;
33195                 break;
33196                         
33197             default :
33198                 break;
33199         }
33200         
33201         if(Roo.isTouch){
33202             this.el.on('touchstart', this.onTouchStart, this);
33203             this.el.on('touchmove', this.onTouchMove, this);
33204             this.el.on('touchend', this.onTouchEnd, this);
33205             this.el.on('contextmenu', this.onContextMenu, this);
33206         } else {
33207             this.el.on('mouseenter'  ,this.enter, this);
33208             this.el.on('mouseleave', this.leave, this);
33209             this.el.on('click', this.onClick, this);
33210         }
33211         
33212         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33213             this.parent().bricks.push(this);   
33214         }
33215         
33216     },
33217     
33218     onClick: function(e, el)
33219     {
33220         var time = this.endTimer - this.startTimer;
33221         // Roo.log(e.preventDefault());
33222         if(Roo.isTouch){
33223             if(time > 1000){
33224                 e.preventDefault();
33225                 return;
33226             }
33227         }
33228         
33229         if(!this.preventDefault){
33230             return;
33231         }
33232         
33233         e.preventDefault();
33234         
33235         if (this.activeClass != '') {
33236             this.selectBrick();
33237         }
33238         
33239         this.fireEvent('click', this, e);
33240     },
33241     
33242     enter: function(e, el)
33243     {
33244         e.preventDefault();
33245         
33246         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33247             return;
33248         }
33249         
33250         if(this.bgimage.length && this.html.length){
33251             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33252         }
33253     },
33254     
33255     leave: function(e, el)
33256     {
33257         e.preventDefault();
33258         
33259         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33260             return;
33261         }
33262         
33263         if(this.bgimage.length && this.html.length){
33264             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33265         }
33266     },
33267     
33268     onTouchStart: function(e, el)
33269     {
33270 //        e.preventDefault();
33271         
33272         this.touchmoved = false;
33273         
33274         if(!this.isFitContainer){
33275             return;
33276         }
33277         
33278         if(!this.bgimage.length || !this.html.length){
33279             return;
33280         }
33281         
33282         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33283         
33284         this.timer = new Date().getTime();
33285         
33286     },
33287     
33288     onTouchMove: function(e, el)
33289     {
33290         this.touchmoved = true;
33291     },
33292     
33293     onContextMenu : function(e,el)
33294     {
33295         e.preventDefault();
33296         e.stopPropagation();
33297         return false;
33298     },
33299     
33300     onTouchEnd: function(e, el)
33301     {
33302 //        e.preventDefault();
33303         
33304         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33305         
33306             this.leave(e,el);
33307             
33308             return;
33309         }
33310         
33311         if(!this.bgimage.length || !this.html.length){
33312             
33313             if(this.href.length){
33314                 window.location.href = this.href;
33315             }
33316             
33317             return;
33318         }
33319         
33320         if(!this.isFitContainer){
33321             return;
33322         }
33323         
33324         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33325         
33326         window.location.href = this.href;
33327     },
33328     
33329     //selection on single brick only
33330     selectBrick : function() {
33331         
33332         if (!this.parentId) {
33333             return;
33334         }
33335         
33336         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33337         var index = m.selectedBrick.indexOf(this.id);
33338         
33339         if ( index > -1) {
33340             m.selectedBrick.splice(index,1);
33341             this.el.removeClass(this.activeClass);
33342             return;
33343         }
33344         
33345         for(var i = 0; i < m.selectedBrick.length; i++) {
33346             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33347             b.el.removeClass(b.activeClass);
33348         }
33349         
33350         m.selectedBrick = [];
33351         
33352         m.selectedBrick.push(this.id);
33353         this.el.addClass(this.activeClass);
33354         return;
33355     },
33356     
33357     isSelected : function(){
33358         return this.el.hasClass(this.activeClass);
33359         
33360     }
33361 });
33362
33363 Roo.apply(Roo.bootstrap.MasonryBrick, {
33364     
33365     //groups: {},
33366     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33367      /**
33368     * register a Masonry Brick
33369     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33370     */
33371     
33372     register : function(brick)
33373     {
33374         //this.groups[brick.id] = brick;
33375         this.groups.add(brick.id, brick);
33376     },
33377     /**
33378     * fetch a  masonry brick based on the masonry brick ID
33379     * @param {string} the masonry brick to add
33380     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33381     */
33382     
33383     get: function(brick_id) 
33384     {
33385         // if (typeof(this.groups[brick_id]) == 'undefined') {
33386         //     return false;
33387         // }
33388         // return this.groups[brick_id] ;
33389         
33390         if(this.groups.key(brick_id)) {
33391             return this.groups.key(brick_id);
33392         }
33393         
33394         return false;
33395     }
33396     
33397     
33398     
33399 });
33400
33401  /*
33402  * - LGPL
33403  *
33404  * element
33405  * 
33406  */
33407
33408 /**
33409  * @class Roo.bootstrap.Brick
33410  * @extends Roo.bootstrap.Component
33411  * Bootstrap Brick class
33412  * 
33413  * @constructor
33414  * Create a new Brick
33415  * @param {Object} config The config object
33416  */
33417
33418 Roo.bootstrap.Brick = function(config){
33419     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33420     
33421     this.addEvents({
33422         // raw events
33423         /**
33424          * @event click
33425          * When a Brick is click
33426          * @param {Roo.bootstrap.Brick} this
33427          * @param {Roo.EventObject} e
33428          */
33429         "click" : true
33430     });
33431 };
33432
33433 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33434     
33435     /**
33436      * @cfg {String} title
33437      */   
33438     title : '',
33439     /**
33440      * @cfg {String} html
33441      */   
33442     html : '',
33443     /**
33444      * @cfg {String} bgimage
33445      */   
33446     bgimage : '',
33447     /**
33448      * @cfg {String} cls
33449      */   
33450     cls : '',
33451     /**
33452      * @cfg {String} href
33453      */   
33454     href : '',
33455     /**
33456      * @cfg {String} video
33457      */   
33458     video : '',
33459     /**
33460      * @cfg {Boolean} square
33461      */   
33462     square : true,
33463     
33464     getAutoCreate : function()
33465     {
33466         var cls = 'roo-brick';
33467         
33468         if(this.href.length){
33469             cls += ' roo-brick-link';
33470         }
33471         
33472         if(this.bgimage.length){
33473             cls += ' roo-brick-image';
33474         }
33475         
33476         if(!this.html.length && !this.bgimage.length){
33477             cls += ' roo-brick-center-title';
33478         }
33479         
33480         if(!this.html.length && this.bgimage.length){
33481             cls += ' roo-brick-bottom-title';
33482         }
33483         
33484         if(this.cls){
33485             cls += ' ' + this.cls;
33486         }
33487         
33488         var cfg = {
33489             tag: (this.href.length) ? 'a' : 'div',
33490             cls: cls,
33491             cn: [
33492                 {
33493                     tag: 'div',
33494                     cls: 'roo-brick-paragraph',
33495                     cn: []
33496                 }
33497             ]
33498         };
33499         
33500         if(this.href.length){
33501             cfg.href = this.href;
33502         }
33503         
33504         var cn = cfg.cn[0].cn;
33505         
33506         if(this.title.length){
33507             cn.push({
33508                 tag: 'h4',
33509                 cls: 'roo-brick-title',
33510                 html: this.title
33511             });
33512         }
33513         
33514         if(this.html.length){
33515             cn.push({
33516                 tag: 'p',
33517                 cls: 'roo-brick-text',
33518                 html: this.html
33519             });
33520         } else {
33521             cn.cls += ' hide';
33522         }
33523         
33524         if(this.bgimage.length){
33525             cfg.cn.push({
33526                 tag: 'img',
33527                 cls: 'roo-brick-image-view',
33528                 src: this.bgimage
33529             });
33530         }
33531         
33532         return cfg;
33533     },
33534     
33535     initEvents: function() 
33536     {
33537         if(this.title.length || this.html.length){
33538             this.el.on('mouseenter'  ,this.enter, this);
33539             this.el.on('mouseleave', this.leave, this);
33540         }
33541         
33542         Roo.EventManager.onWindowResize(this.resize, this); 
33543         
33544         if(this.bgimage.length){
33545             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33546             this.imageEl.on('load', this.onImageLoad, this);
33547             return;
33548         }
33549         
33550         this.resize();
33551     },
33552     
33553     onImageLoad : function()
33554     {
33555         this.resize();
33556     },
33557     
33558     resize : function()
33559     {
33560         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33561         
33562         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33563         
33564         if(this.bgimage.length){
33565             var image = this.el.select('.roo-brick-image-view', true).first();
33566             
33567             image.setWidth(paragraph.getWidth());
33568             
33569             if(this.square){
33570                 image.setHeight(paragraph.getWidth());
33571             }
33572             
33573             this.el.setHeight(image.getHeight());
33574             paragraph.setHeight(image.getHeight());
33575             
33576         }
33577         
33578     },
33579     
33580     enter: function(e, el)
33581     {
33582         e.preventDefault();
33583         
33584         if(this.bgimage.length){
33585             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33586             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33587         }
33588     },
33589     
33590     leave: function(e, el)
33591     {
33592         e.preventDefault();
33593         
33594         if(this.bgimage.length){
33595             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33596             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33597         }
33598     }
33599     
33600 });
33601
33602  
33603
33604  /*
33605  * - LGPL
33606  *
33607  * Number field 
33608  */
33609
33610 /**
33611  * @class Roo.bootstrap.NumberField
33612  * @extends Roo.bootstrap.Input
33613  * Bootstrap NumberField class
33614  * 
33615  * 
33616  * 
33617  * 
33618  * @constructor
33619  * Create a new NumberField
33620  * @param {Object} config The config object
33621  */
33622
33623 Roo.bootstrap.NumberField = function(config){
33624     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33625 };
33626
33627 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33628     
33629     /**
33630      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33631      */
33632     allowDecimals : true,
33633     /**
33634      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33635      */
33636     decimalSeparator : ".",
33637     /**
33638      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33639      */
33640     decimalPrecision : 2,
33641     /**
33642      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33643      */
33644     allowNegative : true,
33645     
33646     /**
33647      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33648      */
33649     allowZero: true,
33650     /**
33651      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33652      */
33653     minValue : Number.NEGATIVE_INFINITY,
33654     /**
33655      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33656      */
33657     maxValue : Number.MAX_VALUE,
33658     /**
33659      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33660      */
33661     minText : "The minimum value for this field is {0}",
33662     /**
33663      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33664      */
33665     maxText : "The maximum value for this field is {0}",
33666     /**
33667      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33668      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33669      */
33670     nanText : "{0} is not a valid number",
33671     /**
33672      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33673      */
33674     thousandsDelimiter : false,
33675     /**
33676      * @cfg {String} valueAlign alignment of value
33677      */
33678     valueAlign : "left",
33679
33680     getAutoCreate : function()
33681     {
33682         var hiddenInput = {
33683             tag: 'input',
33684             type: 'hidden',
33685             id: Roo.id(),
33686             cls: 'hidden-number-input'
33687         };
33688         
33689         if (this.name) {
33690             hiddenInput.name = this.name;
33691         }
33692         
33693         this.name = '';
33694         
33695         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33696         
33697         this.name = hiddenInput.name;
33698         
33699         if(cfg.cn.length > 0) {
33700             cfg.cn.push(hiddenInput);
33701         }
33702         
33703         return cfg;
33704     },
33705
33706     // private
33707     initEvents : function()
33708     {   
33709         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33710         
33711         var allowed = "0123456789";
33712         
33713         if(this.allowDecimals){
33714             allowed += this.decimalSeparator;
33715         }
33716         
33717         if(this.allowNegative){
33718             allowed += "-";
33719         }
33720         
33721         if(this.thousandsDelimiter) {
33722             allowed += ",";
33723         }
33724         
33725         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33726         
33727         var keyPress = function(e){
33728             
33729             var k = e.getKey();
33730             
33731             var c = e.getCharCode();
33732             
33733             if(
33734                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33735                     allowed.indexOf(String.fromCharCode(c)) === -1
33736             ){
33737                 e.stopEvent();
33738                 return;
33739             }
33740             
33741             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33742                 return;
33743             }
33744             
33745             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33746                 e.stopEvent();
33747             }
33748         };
33749         
33750         this.el.on("keypress", keyPress, this);
33751     },
33752     
33753     validateValue : function(value)
33754     {
33755         
33756         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33757             return false;
33758         }
33759         
33760         var num = this.parseValue(value);
33761         
33762         if(isNaN(num)){
33763             this.markInvalid(String.format(this.nanText, value));
33764             return false;
33765         }
33766         
33767         if(num < this.minValue){
33768             this.markInvalid(String.format(this.minText, this.minValue));
33769             return false;
33770         }
33771         
33772         if(num > this.maxValue){
33773             this.markInvalid(String.format(this.maxText, this.maxValue));
33774             return false;
33775         }
33776         
33777         return true;
33778     },
33779
33780     getValue : function()
33781     {
33782         var v = this.hiddenEl().getValue();
33783         
33784         return this.fixPrecision(this.parseValue(v));
33785     },
33786
33787     parseValue : function(value)
33788     {
33789         if(this.thousandsDelimiter) {
33790             value += "";
33791             r = new RegExp(",", "g");
33792             value = value.replace(r, "");
33793         }
33794         
33795         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33796         return isNaN(value) ? '' : value;
33797     },
33798
33799     fixPrecision : function(value)
33800     {
33801         if(this.thousandsDelimiter) {
33802             value += "";
33803             r = new RegExp(",", "g");
33804             value = value.replace(r, "");
33805         }
33806         
33807         var nan = isNaN(value);
33808         
33809         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33810             return nan ? '' : value;
33811         }
33812         return parseFloat(value).toFixed(this.decimalPrecision);
33813     },
33814
33815     setValue : function(v)
33816     {
33817         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33818         
33819         this.value = v;
33820         
33821         if(this.rendered){
33822             
33823             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33824             
33825             this.inputEl().dom.value = (v == '') ? '' :
33826                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33827             
33828             if(!this.allowZero && v === '0') {
33829                 this.hiddenEl().dom.value = '';
33830                 this.inputEl().dom.value = '';
33831             }
33832             
33833             this.validate();
33834         }
33835     },
33836
33837     decimalPrecisionFcn : function(v)
33838     {
33839         return Math.floor(v);
33840     },
33841
33842     beforeBlur : function()
33843     {
33844         var v = this.parseValue(this.getRawValue());
33845         
33846         if(v || v === 0 || v === ''){
33847             this.setValue(v);
33848         }
33849     },
33850     
33851     hiddenEl : function()
33852     {
33853         return this.el.select('input.hidden-number-input',true).first();
33854     }
33855     
33856 });
33857
33858  
33859
33860 /*
33861 * Licence: LGPL
33862 */
33863
33864 /**
33865  * @class Roo.bootstrap.DocumentSlider
33866  * @extends Roo.bootstrap.Component
33867  * Bootstrap DocumentSlider class
33868  * 
33869  * @constructor
33870  * Create a new DocumentViewer
33871  * @param {Object} config The config object
33872  */
33873
33874 Roo.bootstrap.DocumentSlider = function(config){
33875     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33876     
33877     this.files = [];
33878     
33879     this.addEvents({
33880         /**
33881          * @event initial
33882          * Fire after initEvent
33883          * @param {Roo.bootstrap.DocumentSlider} this
33884          */
33885         "initial" : true,
33886         /**
33887          * @event update
33888          * Fire after update
33889          * @param {Roo.bootstrap.DocumentSlider} this
33890          */
33891         "update" : true,
33892         /**
33893          * @event click
33894          * Fire after click
33895          * @param {Roo.bootstrap.DocumentSlider} this
33896          */
33897         "click" : true
33898     });
33899 };
33900
33901 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33902     
33903     files : false,
33904     
33905     indicator : 0,
33906     
33907     getAutoCreate : function()
33908     {
33909         var cfg = {
33910             tag : 'div',
33911             cls : 'roo-document-slider',
33912             cn : [
33913                 {
33914                     tag : 'div',
33915                     cls : 'roo-document-slider-header',
33916                     cn : [
33917                         {
33918                             tag : 'div',
33919                             cls : 'roo-document-slider-header-title'
33920                         }
33921                     ]
33922                 },
33923                 {
33924                     tag : 'div',
33925                     cls : 'roo-document-slider-body',
33926                     cn : [
33927                         {
33928                             tag : 'div',
33929                             cls : 'roo-document-slider-prev',
33930                             cn : [
33931                                 {
33932                                     tag : 'i',
33933                                     cls : 'fa fa-chevron-left'
33934                                 }
33935                             ]
33936                         },
33937                         {
33938                             tag : 'div',
33939                             cls : 'roo-document-slider-thumb',
33940                             cn : [
33941                                 {
33942                                     tag : 'img',
33943                                     cls : 'roo-document-slider-image'
33944                                 }
33945                             ]
33946                         },
33947                         {
33948                             tag : 'div',
33949                             cls : 'roo-document-slider-next',
33950                             cn : [
33951                                 {
33952                                     tag : 'i',
33953                                     cls : 'fa fa-chevron-right'
33954                                 }
33955                             ]
33956                         }
33957                     ]
33958                 }
33959             ]
33960         };
33961         
33962         return cfg;
33963     },
33964     
33965     initEvents : function()
33966     {
33967         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33968         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33969         
33970         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33971         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33972         
33973         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33974         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33975         
33976         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33977         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33978         
33979         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33980         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33981         
33982         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33983         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33984         
33985         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33986         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33987         
33988         this.thumbEl.on('click', this.onClick, this);
33989         
33990         this.prevIndicator.on('click', this.prev, this);
33991         
33992         this.nextIndicator.on('click', this.next, this);
33993         
33994     },
33995     
33996     initial : function()
33997     {
33998         if(this.files.length){
33999             this.indicator = 1;
34000             this.update()
34001         }
34002         
34003         this.fireEvent('initial', this);
34004     },
34005     
34006     update : function()
34007     {
34008         this.imageEl.attr('src', this.files[this.indicator - 1]);
34009         
34010         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34011         
34012         this.prevIndicator.show();
34013         
34014         if(this.indicator == 1){
34015             this.prevIndicator.hide();
34016         }
34017         
34018         this.nextIndicator.show();
34019         
34020         if(this.indicator == this.files.length){
34021             this.nextIndicator.hide();
34022         }
34023         
34024         this.thumbEl.scrollTo('top');
34025         
34026         this.fireEvent('update', this);
34027     },
34028     
34029     onClick : function(e)
34030     {
34031         e.preventDefault();
34032         
34033         this.fireEvent('click', this);
34034     },
34035     
34036     prev : function(e)
34037     {
34038         e.preventDefault();
34039         
34040         this.indicator = Math.max(1, this.indicator - 1);
34041         
34042         this.update();
34043     },
34044     
34045     next : function(e)
34046     {
34047         e.preventDefault();
34048         
34049         this.indicator = Math.min(this.files.length, this.indicator + 1);
34050         
34051         this.update();
34052     }
34053 });
34054 /*
34055  * - LGPL
34056  *
34057  * RadioSet
34058  *
34059  *
34060  */
34061
34062 /**
34063  * @class Roo.bootstrap.RadioSet
34064  * @extends Roo.bootstrap.Input
34065  * Bootstrap RadioSet class
34066  * @cfg {String} indicatorpos (left|right) default left
34067  * @cfg {Boolean} inline (true|false) inline the element (default true)
34068  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34069  * @constructor
34070  * Create a new RadioSet
34071  * @param {Object} config The config object
34072  */
34073
34074 Roo.bootstrap.RadioSet = function(config){
34075     
34076     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34077     
34078     this.radioes = [];
34079     
34080     Roo.bootstrap.RadioSet.register(this);
34081     
34082     this.addEvents({
34083         /**
34084         * @event check
34085         * Fires when the element is checked or unchecked.
34086         * @param {Roo.bootstrap.RadioSet} this This radio
34087         * @param {Roo.bootstrap.Radio} item The checked item
34088         */
34089        check : true,
34090        /**
34091         * @event click
34092         * Fires when the element is click.
34093         * @param {Roo.bootstrap.RadioSet} this This radio set
34094         * @param {Roo.bootstrap.Radio} item The checked item
34095         * @param {Roo.EventObject} e The event object
34096         */
34097        click : true
34098     });
34099     
34100 };
34101
34102 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34103
34104     radioes : false,
34105     
34106     inline : true,
34107     
34108     weight : '',
34109     
34110     indicatorpos : 'left',
34111     
34112     getAutoCreate : function()
34113     {
34114         var label = {
34115             tag : 'label',
34116             cls : 'roo-radio-set-label',
34117             cn : [
34118                 {
34119                     tag : 'span',
34120                     html : this.fieldLabel
34121                 }
34122             ]
34123         };
34124         if (Roo.bootstrap.version == 3) {
34125             
34126             
34127             if(this.indicatorpos == 'left'){
34128                 label.cn.unshift({
34129                     tag : 'i',
34130                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34131                     tooltip : 'This field is required'
34132                 });
34133             } else {
34134                 label.cn.push({
34135                     tag : 'i',
34136                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34137                     tooltip : 'This field is required'
34138                 });
34139             }
34140         }
34141         var items = {
34142             tag : 'div',
34143             cls : 'roo-radio-set-items'
34144         };
34145         
34146         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34147         
34148         if (align === 'left' && this.fieldLabel.length) {
34149             
34150             items = {
34151                 cls : "roo-radio-set-right", 
34152                 cn: [
34153                     items
34154                 ]
34155             };
34156             
34157             if(this.labelWidth > 12){
34158                 label.style = "width: " + this.labelWidth + 'px';
34159             }
34160             
34161             if(this.labelWidth < 13 && this.labelmd == 0){
34162                 this.labelmd = this.labelWidth;
34163             }
34164             
34165             if(this.labellg > 0){
34166                 label.cls += ' col-lg-' + this.labellg;
34167                 items.cls += ' col-lg-' + (12 - this.labellg);
34168             }
34169             
34170             if(this.labelmd > 0){
34171                 label.cls += ' col-md-' + this.labelmd;
34172                 items.cls += ' col-md-' + (12 - this.labelmd);
34173             }
34174             
34175             if(this.labelsm > 0){
34176                 label.cls += ' col-sm-' + this.labelsm;
34177                 items.cls += ' col-sm-' + (12 - this.labelsm);
34178             }
34179             
34180             if(this.labelxs > 0){
34181                 label.cls += ' col-xs-' + this.labelxs;
34182                 items.cls += ' col-xs-' + (12 - this.labelxs);
34183             }
34184         }
34185         
34186         var cfg = {
34187             tag : 'div',
34188             cls : 'roo-radio-set',
34189             cn : [
34190                 {
34191                     tag : 'input',
34192                     cls : 'roo-radio-set-input',
34193                     type : 'hidden',
34194                     name : this.name,
34195                     value : this.value ? this.value :  ''
34196                 },
34197                 label,
34198                 items
34199             ]
34200         };
34201         
34202         if(this.weight.length){
34203             cfg.cls += ' roo-radio-' + this.weight;
34204         }
34205         
34206         if(this.inline) {
34207             cfg.cls += ' roo-radio-set-inline';
34208         }
34209         
34210         var settings=this;
34211         ['xs','sm','md','lg'].map(function(size){
34212             if (settings[size]) {
34213                 cfg.cls += ' col-' + size + '-' + settings[size];
34214             }
34215         });
34216         
34217         return cfg;
34218         
34219     },
34220
34221     initEvents : function()
34222     {
34223         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34224         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34225         
34226         if(!this.fieldLabel.length){
34227             this.labelEl.hide();
34228         }
34229         
34230         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34231         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34232         
34233         this.indicator = this.indicatorEl();
34234         
34235         if(this.indicator){
34236             this.indicator.addClass('invisible');
34237         }
34238         
34239         this.originalValue = this.getValue();
34240         
34241     },
34242     
34243     inputEl: function ()
34244     {
34245         return this.el.select('.roo-radio-set-input', true).first();
34246     },
34247     
34248     getChildContainer : function()
34249     {
34250         return this.itemsEl;
34251     },
34252     
34253     register : function(item)
34254     {
34255         this.radioes.push(item);
34256         
34257     },
34258     
34259     validate : function()
34260     {   
34261         if(this.getVisibilityEl().hasClass('hidden')){
34262             return true;
34263         }
34264         
34265         var valid = false;
34266         
34267         Roo.each(this.radioes, function(i){
34268             if(!i.checked){
34269                 return;
34270             }
34271             
34272             valid = true;
34273             return false;
34274         });
34275         
34276         if(this.allowBlank) {
34277             return true;
34278         }
34279         
34280         if(this.disabled || valid){
34281             this.markValid();
34282             return true;
34283         }
34284         
34285         this.markInvalid();
34286         return false;
34287         
34288     },
34289     
34290     markValid : function()
34291     {
34292         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34293             this.indicatorEl().removeClass('visible');
34294             this.indicatorEl().addClass('invisible');
34295         }
34296         
34297         
34298         if (Roo.bootstrap.version == 3) {
34299             this.el.removeClass([this.invalidClass, this.validClass]);
34300             this.el.addClass(this.validClass);
34301         } else {
34302             this.el.removeClass(['is-invalid','is-valid']);
34303             this.el.addClass(['is-valid']);
34304         }
34305         this.fireEvent('valid', this);
34306     },
34307     
34308     markInvalid : function(msg)
34309     {
34310         if(this.allowBlank || this.disabled){
34311             return;
34312         }
34313         
34314         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34315             this.indicatorEl().removeClass('invisible');
34316             this.indicatorEl().addClass('visible');
34317         }
34318         if (Roo.bootstrap.version == 3) {
34319             this.el.removeClass([this.invalidClass, this.validClass]);
34320             this.el.addClass(this.invalidClass);
34321         } else {
34322             this.el.removeClass(['is-invalid','is-valid']);
34323             this.el.addClass(['is-invalid']);
34324         }
34325         
34326         this.fireEvent('invalid', this, msg);
34327         
34328     },
34329     
34330     setValue : function(v, suppressEvent)
34331     {   
34332         if(this.value === v){
34333             return;
34334         }
34335         
34336         this.value = v;
34337         
34338         if(this.rendered){
34339             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34340         }
34341         
34342         Roo.each(this.radioes, function(i){
34343             i.checked = false;
34344             i.el.removeClass('checked');
34345         });
34346         
34347         Roo.each(this.radioes, function(i){
34348             
34349             if(i.value === v || i.value.toString() === v.toString()){
34350                 i.checked = true;
34351                 i.el.addClass('checked');
34352                 
34353                 if(suppressEvent !== true){
34354                     this.fireEvent('check', this, i);
34355                 }
34356                 
34357                 return false;
34358             }
34359             
34360         }, this);
34361         
34362         this.validate();
34363     },
34364     
34365     clearInvalid : function(){
34366         
34367         if(!this.el || this.preventMark){
34368             return;
34369         }
34370         
34371         this.el.removeClass([this.invalidClass]);
34372         
34373         this.fireEvent('valid', this);
34374     }
34375     
34376 });
34377
34378 Roo.apply(Roo.bootstrap.RadioSet, {
34379     
34380     groups: {},
34381     
34382     register : function(set)
34383     {
34384         this.groups[set.name] = set;
34385     },
34386     
34387     get: function(name) 
34388     {
34389         if (typeof(this.groups[name]) == 'undefined') {
34390             return false;
34391         }
34392         
34393         return this.groups[name] ;
34394     }
34395     
34396 });
34397 /*
34398  * Based on:
34399  * Ext JS Library 1.1.1
34400  * Copyright(c) 2006-2007, Ext JS, LLC.
34401  *
34402  * Originally Released Under LGPL - original licence link has changed is not relivant.
34403  *
34404  * Fork - LGPL
34405  * <script type="text/javascript">
34406  */
34407
34408
34409 /**
34410  * @class Roo.bootstrap.SplitBar
34411  * @extends Roo.util.Observable
34412  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34413  * <br><br>
34414  * Usage:
34415  * <pre><code>
34416 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34417                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34418 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34419 split.minSize = 100;
34420 split.maxSize = 600;
34421 split.animate = true;
34422 split.on('moved', splitterMoved);
34423 </code></pre>
34424  * @constructor
34425  * Create a new SplitBar
34426  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34427  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34428  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34429  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34430                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34431                         position of the SplitBar).
34432  */
34433 Roo.bootstrap.SplitBar = function(cfg){
34434     
34435     /** @private */
34436     
34437     //{
34438     //  dragElement : elm
34439     //  resizingElement: el,
34440         // optional..
34441     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34442     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34443         // existingProxy ???
34444     //}
34445     
34446     this.el = Roo.get(cfg.dragElement, true);
34447     this.el.dom.unselectable = "on";
34448     /** @private */
34449     this.resizingEl = Roo.get(cfg.resizingElement, true);
34450
34451     /**
34452      * @private
34453      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34454      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34455      * @type Number
34456      */
34457     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34458     
34459     /**
34460      * The minimum size of the resizing element. (Defaults to 0)
34461      * @type Number
34462      */
34463     this.minSize = 0;
34464     
34465     /**
34466      * The maximum size of the resizing element. (Defaults to 2000)
34467      * @type Number
34468      */
34469     this.maxSize = 2000;
34470     
34471     /**
34472      * Whether to animate the transition to the new size
34473      * @type Boolean
34474      */
34475     this.animate = false;
34476     
34477     /**
34478      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34479      * @type Boolean
34480      */
34481     this.useShim = false;
34482     
34483     /** @private */
34484     this.shim = null;
34485     
34486     if(!cfg.existingProxy){
34487         /** @private */
34488         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34489     }else{
34490         this.proxy = Roo.get(cfg.existingProxy).dom;
34491     }
34492     /** @private */
34493     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34494     
34495     /** @private */
34496     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34497     
34498     /** @private */
34499     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34500     
34501     /** @private */
34502     this.dragSpecs = {};
34503     
34504     /**
34505      * @private The adapter to use to positon and resize elements
34506      */
34507     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34508     this.adapter.init(this);
34509     
34510     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34511         /** @private */
34512         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34513         this.el.addClass("roo-splitbar-h");
34514     }else{
34515         /** @private */
34516         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34517         this.el.addClass("roo-splitbar-v");
34518     }
34519     
34520     this.addEvents({
34521         /**
34522          * @event resize
34523          * Fires when the splitter is moved (alias for {@link #event-moved})
34524          * @param {Roo.bootstrap.SplitBar} this
34525          * @param {Number} newSize the new width or height
34526          */
34527         "resize" : true,
34528         /**
34529          * @event moved
34530          * Fires when the splitter is moved
34531          * @param {Roo.bootstrap.SplitBar} this
34532          * @param {Number} newSize the new width or height
34533          */
34534         "moved" : true,
34535         /**
34536          * @event beforeresize
34537          * Fires before the splitter is dragged
34538          * @param {Roo.bootstrap.SplitBar} this
34539          */
34540         "beforeresize" : true,
34541
34542         "beforeapply" : true
34543     });
34544
34545     Roo.util.Observable.call(this);
34546 };
34547
34548 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34549     onStartProxyDrag : function(x, y){
34550         this.fireEvent("beforeresize", this);
34551         if(!this.overlay){
34552             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34553             o.unselectable();
34554             o.enableDisplayMode("block");
34555             // all splitbars share the same overlay
34556             Roo.bootstrap.SplitBar.prototype.overlay = o;
34557         }
34558         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34559         this.overlay.show();
34560         Roo.get(this.proxy).setDisplayed("block");
34561         var size = this.adapter.getElementSize(this);
34562         this.activeMinSize = this.getMinimumSize();;
34563         this.activeMaxSize = this.getMaximumSize();;
34564         var c1 = size - this.activeMinSize;
34565         var c2 = Math.max(this.activeMaxSize - size, 0);
34566         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34567             this.dd.resetConstraints();
34568             this.dd.setXConstraint(
34569                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34570                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34571             );
34572             this.dd.setYConstraint(0, 0);
34573         }else{
34574             this.dd.resetConstraints();
34575             this.dd.setXConstraint(0, 0);
34576             this.dd.setYConstraint(
34577                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34578                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34579             );
34580          }
34581         this.dragSpecs.startSize = size;
34582         this.dragSpecs.startPoint = [x, y];
34583         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34584     },
34585     
34586     /** 
34587      * @private Called after the drag operation by the DDProxy
34588      */
34589     onEndProxyDrag : function(e){
34590         Roo.get(this.proxy).setDisplayed(false);
34591         var endPoint = Roo.lib.Event.getXY(e);
34592         if(this.overlay){
34593             this.overlay.hide();
34594         }
34595         var newSize;
34596         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34597             newSize = this.dragSpecs.startSize + 
34598                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34599                     endPoint[0] - this.dragSpecs.startPoint[0] :
34600                     this.dragSpecs.startPoint[0] - endPoint[0]
34601                 );
34602         }else{
34603             newSize = this.dragSpecs.startSize + 
34604                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34605                     endPoint[1] - this.dragSpecs.startPoint[1] :
34606                     this.dragSpecs.startPoint[1] - endPoint[1]
34607                 );
34608         }
34609         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34610         if(newSize != this.dragSpecs.startSize){
34611             if(this.fireEvent('beforeapply', this, newSize) !== false){
34612                 this.adapter.setElementSize(this, newSize);
34613                 this.fireEvent("moved", this, newSize);
34614                 this.fireEvent("resize", this, newSize);
34615             }
34616         }
34617     },
34618     
34619     /**
34620      * Get the adapter this SplitBar uses
34621      * @return The adapter object
34622      */
34623     getAdapter : function(){
34624         return this.adapter;
34625     },
34626     
34627     /**
34628      * Set the adapter this SplitBar uses
34629      * @param {Object} adapter A SplitBar adapter object
34630      */
34631     setAdapter : function(adapter){
34632         this.adapter = adapter;
34633         this.adapter.init(this);
34634     },
34635     
34636     /**
34637      * Gets the minimum size for the resizing element
34638      * @return {Number} The minimum size
34639      */
34640     getMinimumSize : function(){
34641         return this.minSize;
34642     },
34643     
34644     /**
34645      * Sets the minimum size for the resizing element
34646      * @param {Number} minSize The minimum size
34647      */
34648     setMinimumSize : function(minSize){
34649         this.minSize = minSize;
34650     },
34651     
34652     /**
34653      * Gets the maximum size for the resizing element
34654      * @return {Number} The maximum size
34655      */
34656     getMaximumSize : function(){
34657         return this.maxSize;
34658     },
34659     
34660     /**
34661      * Sets the maximum size for the resizing element
34662      * @param {Number} maxSize The maximum size
34663      */
34664     setMaximumSize : function(maxSize){
34665         this.maxSize = maxSize;
34666     },
34667     
34668     /**
34669      * Sets the initialize size for the resizing element
34670      * @param {Number} size The initial size
34671      */
34672     setCurrentSize : function(size){
34673         var oldAnimate = this.animate;
34674         this.animate = false;
34675         this.adapter.setElementSize(this, size);
34676         this.animate = oldAnimate;
34677     },
34678     
34679     /**
34680      * Destroy this splitbar. 
34681      * @param {Boolean} removeEl True to remove the element
34682      */
34683     destroy : function(removeEl){
34684         if(this.shim){
34685             this.shim.remove();
34686         }
34687         this.dd.unreg();
34688         this.proxy.parentNode.removeChild(this.proxy);
34689         if(removeEl){
34690             this.el.remove();
34691         }
34692     }
34693 });
34694
34695 /**
34696  * @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.
34697  */
34698 Roo.bootstrap.SplitBar.createProxy = function(dir){
34699     var proxy = new Roo.Element(document.createElement("div"));
34700     proxy.unselectable();
34701     var cls = 'roo-splitbar-proxy';
34702     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34703     document.body.appendChild(proxy.dom);
34704     return proxy.dom;
34705 };
34706
34707 /** 
34708  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34709  * Default Adapter. It assumes the splitter and resizing element are not positioned
34710  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34711  */
34712 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34713 };
34714
34715 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34716     // do nothing for now
34717     init : function(s){
34718     
34719     },
34720     /**
34721      * Called before drag operations to get the current size of the resizing element. 
34722      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34723      */
34724      getElementSize : function(s){
34725         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34726             return s.resizingEl.getWidth();
34727         }else{
34728             return s.resizingEl.getHeight();
34729         }
34730     },
34731     
34732     /**
34733      * Called after drag operations to set the size of the resizing element.
34734      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34735      * @param {Number} newSize The new size to set
34736      * @param {Function} onComplete A function to be invoked when resizing is complete
34737      */
34738     setElementSize : function(s, newSize, onComplete){
34739         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34740             if(!s.animate){
34741                 s.resizingEl.setWidth(newSize);
34742                 if(onComplete){
34743                     onComplete(s, newSize);
34744                 }
34745             }else{
34746                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34747             }
34748         }else{
34749             
34750             if(!s.animate){
34751                 s.resizingEl.setHeight(newSize);
34752                 if(onComplete){
34753                     onComplete(s, newSize);
34754                 }
34755             }else{
34756                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34757             }
34758         }
34759     }
34760 };
34761
34762 /** 
34763  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34764  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34765  * Adapter that  moves the splitter element to align with the resized sizing element. 
34766  * Used with an absolute positioned SplitBar.
34767  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34768  * document.body, make sure you assign an id to the body element.
34769  */
34770 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34771     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34772     this.container = Roo.get(container);
34773 };
34774
34775 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34776     init : function(s){
34777         this.basic.init(s);
34778     },
34779     
34780     getElementSize : function(s){
34781         return this.basic.getElementSize(s);
34782     },
34783     
34784     setElementSize : function(s, newSize, onComplete){
34785         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34786     },
34787     
34788     moveSplitter : function(s){
34789         var yes = Roo.bootstrap.SplitBar;
34790         switch(s.placement){
34791             case yes.LEFT:
34792                 s.el.setX(s.resizingEl.getRight());
34793                 break;
34794             case yes.RIGHT:
34795                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34796                 break;
34797             case yes.TOP:
34798                 s.el.setY(s.resizingEl.getBottom());
34799                 break;
34800             case yes.BOTTOM:
34801                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34802                 break;
34803         }
34804     }
34805 };
34806
34807 /**
34808  * Orientation constant - Create a vertical SplitBar
34809  * @static
34810  * @type Number
34811  */
34812 Roo.bootstrap.SplitBar.VERTICAL = 1;
34813
34814 /**
34815  * Orientation constant - Create a horizontal SplitBar
34816  * @static
34817  * @type Number
34818  */
34819 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34820
34821 /**
34822  * Placement constant - The resizing element is to the left of the splitter element
34823  * @static
34824  * @type Number
34825  */
34826 Roo.bootstrap.SplitBar.LEFT = 1;
34827
34828 /**
34829  * Placement constant - The resizing element is to the right of the splitter element
34830  * @static
34831  * @type Number
34832  */
34833 Roo.bootstrap.SplitBar.RIGHT = 2;
34834
34835 /**
34836  * Placement constant - The resizing element is positioned above the splitter element
34837  * @static
34838  * @type Number
34839  */
34840 Roo.bootstrap.SplitBar.TOP = 3;
34841
34842 /**
34843  * Placement constant - The resizing element is positioned under splitter element
34844  * @static
34845  * @type Number
34846  */
34847 Roo.bootstrap.SplitBar.BOTTOM = 4;
34848 Roo.namespace("Roo.bootstrap.layout");/*
34849  * Based on:
34850  * Ext JS Library 1.1.1
34851  * Copyright(c) 2006-2007, Ext JS, LLC.
34852  *
34853  * Originally Released Under LGPL - original licence link has changed is not relivant.
34854  *
34855  * Fork - LGPL
34856  * <script type="text/javascript">
34857  */
34858
34859 /**
34860  * @class Roo.bootstrap.layout.Manager
34861  * @extends Roo.bootstrap.Component
34862  * Base class for layout managers.
34863  */
34864 Roo.bootstrap.layout.Manager = function(config)
34865 {
34866     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34867
34868
34869
34870
34871
34872     /** false to disable window resize monitoring @type Boolean */
34873     this.monitorWindowResize = true;
34874     this.regions = {};
34875     this.addEvents({
34876         /**
34877          * @event layout
34878          * Fires when a layout is performed.
34879          * @param {Roo.LayoutManager} this
34880          */
34881         "layout" : true,
34882         /**
34883          * @event regionresized
34884          * Fires when the user resizes a region.
34885          * @param {Roo.LayoutRegion} region The resized region
34886          * @param {Number} newSize The new size (width for east/west, height for north/south)
34887          */
34888         "regionresized" : true,
34889         /**
34890          * @event regioncollapsed
34891          * Fires when a region is collapsed.
34892          * @param {Roo.LayoutRegion} region The collapsed region
34893          */
34894         "regioncollapsed" : true,
34895         /**
34896          * @event regionexpanded
34897          * Fires when a region is expanded.
34898          * @param {Roo.LayoutRegion} region The expanded region
34899          */
34900         "regionexpanded" : true
34901     });
34902     this.updating = false;
34903
34904     if (config.el) {
34905         this.el = Roo.get(config.el);
34906         this.initEvents();
34907     }
34908
34909 };
34910
34911 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34912
34913
34914     regions : null,
34915
34916     monitorWindowResize : true,
34917
34918
34919     updating : false,
34920
34921
34922     onRender : function(ct, position)
34923     {
34924         if(!this.el){
34925             this.el = Roo.get(ct);
34926             this.initEvents();
34927         }
34928         //this.fireEvent('render',this);
34929     },
34930
34931
34932     initEvents: function()
34933     {
34934
34935
34936         // ie scrollbar fix
34937         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34938             document.body.scroll = "no";
34939         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34940             this.el.position('relative');
34941         }
34942         this.id = this.el.id;
34943         this.el.addClass("roo-layout-container");
34944         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34945         if(this.el.dom != document.body ) {
34946             this.el.on('resize', this.layout,this);
34947             this.el.on('show', this.layout,this);
34948         }
34949
34950     },
34951
34952     /**
34953      * Returns true if this layout is currently being updated
34954      * @return {Boolean}
34955      */
34956     isUpdating : function(){
34957         return this.updating;
34958     },
34959
34960     /**
34961      * Suspend the LayoutManager from doing auto-layouts while
34962      * making multiple add or remove calls
34963      */
34964     beginUpdate : function(){
34965         this.updating = true;
34966     },
34967
34968     /**
34969      * Restore auto-layouts and optionally disable the manager from performing a layout
34970      * @param {Boolean} noLayout true to disable a layout update
34971      */
34972     endUpdate : function(noLayout){
34973         this.updating = false;
34974         if(!noLayout){
34975             this.layout();
34976         }
34977     },
34978
34979     layout: function(){
34980         // abstract...
34981     },
34982
34983     onRegionResized : function(region, newSize){
34984         this.fireEvent("regionresized", region, newSize);
34985         this.layout();
34986     },
34987
34988     onRegionCollapsed : function(region){
34989         this.fireEvent("regioncollapsed", region);
34990     },
34991
34992     onRegionExpanded : function(region){
34993         this.fireEvent("regionexpanded", region);
34994     },
34995
34996     /**
34997      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34998      * performs box-model adjustments.
34999      * @return {Object} The size as an object {width: (the width), height: (the height)}
35000      */
35001     getViewSize : function()
35002     {
35003         var size;
35004         if(this.el.dom != document.body){
35005             size = this.el.getSize();
35006         }else{
35007             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35008         }
35009         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35010         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35011         return size;
35012     },
35013
35014     /**
35015      * Returns the Element this layout is bound to.
35016      * @return {Roo.Element}
35017      */
35018     getEl : function(){
35019         return this.el;
35020     },
35021
35022     /**
35023      * Returns the specified region.
35024      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35025      * @return {Roo.LayoutRegion}
35026      */
35027     getRegion : function(target){
35028         return this.regions[target.toLowerCase()];
35029     },
35030
35031     onWindowResize : function(){
35032         if(this.monitorWindowResize){
35033             this.layout();
35034         }
35035     }
35036 });
35037 /*
35038  * Based on:
35039  * Ext JS Library 1.1.1
35040  * Copyright(c) 2006-2007, Ext JS, LLC.
35041  *
35042  * Originally Released Under LGPL - original licence link has changed is not relivant.
35043  *
35044  * Fork - LGPL
35045  * <script type="text/javascript">
35046  */
35047 /**
35048  * @class Roo.bootstrap.layout.Border
35049  * @extends Roo.bootstrap.layout.Manager
35050  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35051  * please see: examples/bootstrap/nested.html<br><br>
35052  
35053 <b>The container the layout is rendered into can be either the body element or any other element.
35054 If it is not the body element, the container needs to either be an absolute positioned element,
35055 or you will need to add "position:relative" to the css of the container.  You will also need to specify
35056 the container size if it is not the body element.</b>
35057
35058 * @constructor
35059 * Create a new Border
35060 * @param {Object} config Configuration options
35061  */
35062 Roo.bootstrap.layout.Border = function(config){
35063     config = config || {};
35064     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35065     
35066     
35067     
35068     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35069         if(config[region]){
35070             config[region].region = region;
35071             this.addRegion(config[region]);
35072         }
35073     },this);
35074     
35075 };
35076
35077 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35078
35079 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35080     /**
35081      * Creates and adds a new region if it doesn't already exist.
35082      * @param {String} target The target region key (north, south, east, west or center).
35083      * @param {Object} config The regions config object
35084      * @return {BorderLayoutRegion} The new region
35085      */
35086     addRegion : function(config)
35087     {
35088         if(!this.regions[config.region]){
35089             var r = this.factory(config);
35090             this.bindRegion(r);
35091         }
35092         return this.regions[config.region];
35093     },
35094
35095     // private (kinda)
35096     bindRegion : function(r){
35097         this.regions[r.config.region] = r;
35098         
35099         r.on("visibilitychange",    this.layout, this);
35100         r.on("paneladded",          this.layout, this);
35101         r.on("panelremoved",        this.layout, this);
35102         r.on("invalidated",         this.layout, this);
35103         r.on("resized",             this.onRegionResized, this);
35104         r.on("collapsed",           this.onRegionCollapsed, this);
35105         r.on("expanded",            this.onRegionExpanded, this);
35106     },
35107
35108     /**
35109      * Performs a layout update.
35110      */
35111     layout : function()
35112     {
35113         if(this.updating) {
35114             return;
35115         }
35116         
35117         // render all the rebions if they have not been done alreayd?
35118         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35119             if(this.regions[region] && !this.regions[region].bodyEl){
35120                 this.regions[region].onRender(this.el)
35121             }
35122         },this);
35123         
35124         var size = this.getViewSize();
35125         var w = size.width;
35126         var h = size.height;
35127         var centerW = w;
35128         var centerH = h;
35129         var centerY = 0;
35130         var centerX = 0;
35131         //var x = 0, y = 0;
35132
35133         var rs = this.regions;
35134         var north = rs["north"];
35135         var south = rs["south"]; 
35136         var west = rs["west"];
35137         var east = rs["east"];
35138         var center = rs["center"];
35139         //if(this.hideOnLayout){ // not supported anymore
35140             //c.el.setStyle("display", "none");
35141         //}
35142         if(north && north.isVisible()){
35143             var b = north.getBox();
35144             var m = north.getMargins();
35145             b.width = w - (m.left+m.right);
35146             b.x = m.left;
35147             b.y = m.top;
35148             centerY = b.height + b.y + m.bottom;
35149             centerH -= centerY;
35150             north.updateBox(this.safeBox(b));
35151         }
35152         if(south && south.isVisible()){
35153             var b = south.getBox();
35154             var m = south.getMargins();
35155             b.width = w - (m.left+m.right);
35156             b.x = m.left;
35157             var totalHeight = (b.height + m.top + m.bottom);
35158             b.y = h - totalHeight + m.top;
35159             centerH -= totalHeight;
35160             south.updateBox(this.safeBox(b));
35161         }
35162         if(west && west.isVisible()){
35163             var b = west.getBox();
35164             var m = west.getMargins();
35165             b.height = centerH - (m.top+m.bottom);
35166             b.x = m.left;
35167             b.y = centerY + m.top;
35168             var totalWidth = (b.width + m.left + m.right);
35169             centerX += totalWidth;
35170             centerW -= totalWidth;
35171             west.updateBox(this.safeBox(b));
35172         }
35173         if(east && east.isVisible()){
35174             var b = east.getBox();
35175             var m = east.getMargins();
35176             b.height = centerH - (m.top+m.bottom);
35177             var totalWidth = (b.width + m.left + m.right);
35178             b.x = w - totalWidth + m.left;
35179             b.y = centerY + m.top;
35180             centerW -= totalWidth;
35181             east.updateBox(this.safeBox(b));
35182         }
35183         if(center){
35184             var m = center.getMargins();
35185             var centerBox = {
35186                 x: centerX + m.left,
35187                 y: centerY + m.top,
35188                 width: centerW - (m.left+m.right),
35189                 height: centerH - (m.top+m.bottom)
35190             };
35191             //if(this.hideOnLayout){
35192                 //center.el.setStyle("display", "block");
35193             //}
35194             center.updateBox(this.safeBox(centerBox));
35195         }
35196         this.el.repaint();
35197         this.fireEvent("layout", this);
35198     },
35199
35200     // private
35201     safeBox : function(box){
35202         box.width = Math.max(0, box.width);
35203         box.height = Math.max(0, box.height);
35204         return box;
35205     },
35206
35207     /**
35208      * Adds a ContentPanel (or subclass) to this layout.
35209      * @param {String} target The target region key (north, south, east, west or center).
35210      * @param {Roo.ContentPanel} panel The panel to add
35211      * @return {Roo.ContentPanel} The added panel
35212      */
35213     add : function(target, panel){
35214          
35215         target = target.toLowerCase();
35216         return this.regions[target].add(panel);
35217     },
35218
35219     /**
35220      * Remove a ContentPanel (or subclass) to this layout.
35221      * @param {String} target The target region key (north, south, east, west or center).
35222      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35223      * @return {Roo.ContentPanel} The removed panel
35224      */
35225     remove : function(target, panel){
35226         target = target.toLowerCase();
35227         return this.regions[target].remove(panel);
35228     },
35229
35230     /**
35231      * Searches all regions for a panel with the specified id
35232      * @param {String} panelId
35233      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35234      */
35235     findPanel : function(panelId){
35236         var rs = this.regions;
35237         for(var target in rs){
35238             if(typeof rs[target] != "function"){
35239                 var p = rs[target].getPanel(panelId);
35240                 if(p){
35241                     return p;
35242                 }
35243             }
35244         }
35245         return null;
35246     },
35247
35248     /**
35249      * Searches all regions for a panel with the specified id and activates (shows) it.
35250      * @param {String/ContentPanel} panelId The panels id or the panel itself
35251      * @return {Roo.ContentPanel} The shown panel or null
35252      */
35253     showPanel : function(panelId) {
35254       var rs = this.regions;
35255       for(var target in rs){
35256          var r = rs[target];
35257          if(typeof r != "function"){
35258             if(r.hasPanel(panelId)){
35259                return r.showPanel(panelId);
35260             }
35261          }
35262       }
35263       return null;
35264    },
35265
35266    /**
35267      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35268      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35269      */
35270    /*
35271     restoreState : function(provider){
35272         if(!provider){
35273             provider = Roo.state.Manager;
35274         }
35275         var sm = new Roo.LayoutStateManager();
35276         sm.init(this, provider);
35277     },
35278 */
35279  
35280  
35281     /**
35282      * Adds a xtype elements to the layout.
35283      * <pre><code>
35284
35285 layout.addxtype({
35286        xtype : 'ContentPanel',
35287        region: 'west',
35288        items: [ .... ]
35289    }
35290 );
35291
35292 layout.addxtype({
35293         xtype : 'NestedLayoutPanel',
35294         region: 'west',
35295         layout: {
35296            center: { },
35297            west: { }   
35298         },
35299         items : [ ... list of content panels or nested layout panels.. ]
35300    }
35301 );
35302 </code></pre>
35303      * @param {Object} cfg Xtype definition of item to add.
35304      */
35305     addxtype : function(cfg)
35306     {
35307         // basically accepts a pannel...
35308         // can accept a layout region..!?!?
35309         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35310         
35311         
35312         // theory?  children can only be panels??
35313         
35314         //if (!cfg.xtype.match(/Panel$/)) {
35315         //    return false;
35316         //}
35317         var ret = false;
35318         
35319         if (typeof(cfg.region) == 'undefined') {
35320             Roo.log("Failed to add Panel, region was not set");
35321             Roo.log(cfg);
35322             return false;
35323         }
35324         var region = cfg.region;
35325         delete cfg.region;
35326         
35327           
35328         var xitems = [];
35329         if (cfg.items) {
35330             xitems = cfg.items;
35331             delete cfg.items;
35332         }
35333         var nb = false;
35334         
35335         switch(cfg.xtype) 
35336         {
35337             case 'Content':  // ContentPanel (el, cfg)
35338             case 'Scroll':  // ContentPanel (el, cfg)
35339             case 'View': 
35340                 cfg.autoCreate = true;
35341                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35342                 //} else {
35343                 //    var el = this.el.createChild();
35344                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35345                 //}
35346                 
35347                 this.add(region, ret);
35348                 break;
35349             
35350             /*
35351             case 'TreePanel': // our new panel!
35352                 cfg.el = this.el.createChild();
35353                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35354                 this.add(region, ret);
35355                 break;
35356             */
35357             
35358             case 'Nest': 
35359                 // create a new Layout (which is  a Border Layout...
35360                 
35361                 var clayout = cfg.layout;
35362                 clayout.el  = this.el.createChild();
35363                 clayout.items   = clayout.items  || [];
35364                 
35365                 delete cfg.layout;
35366                 
35367                 // replace this exitems with the clayout ones..
35368                 xitems = clayout.items;
35369                  
35370                 // force background off if it's in center...
35371                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35372                     cfg.background = false;
35373                 }
35374                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35375                 
35376                 
35377                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35378                 //console.log('adding nested layout panel '  + cfg.toSource());
35379                 this.add(region, ret);
35380                 nb = {}; /// find first...
35381                 break;
35382             
35383             case 'Grid':
35384                 
35385                 // needs grid and region
35386                 
35387                 //var el = this.getRegion(region).el.createChild();
35388                 /*
35389                  *var el = this.el.createChild();
35390                 // create the grid first...
35391                 cfg.grid.container = el;
35392                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35393                 */
35394                 
35395                 if (region == 'center' && this.active ) {
35396                     cfg.background = false;
35397                 }
35398                 
35399                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35400                 
35401                 this.add(region, ret);
35402                 /*
35403                 if (cfg.background) {
35404                     // render grid on panel activation (if panel background)
35405                     ret.on('activate', function(gp) {
35406                         if (!gp.grid.rendered) {
35407                     //        gp.grid.render(el);
35408                         }
35409                     });
35410                 } else {
35411                   //  cfg.grid.render(el);
35412                 }
35413                 */
35414                 break;
35415            
35416            
35417             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35418                 // it was the old xcomponent building that caused this before.
35419                 // espeically if border is the top element in the tree.
35420                 ret = this;
35421                 break; 
35422                 
35423                     
35424                 
35425                 
35426                 
35427             default:
35428                 /*
35429                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35430                     
35431                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35432                     this.add(region, ret);
35433                 } else {
35434                 */
35435                     Roo.log(cfg);
35436                     throw "Can not add '" + cfg.xtype + "' to Border";
35437                     return null;
35438              
35439                                 
35440              
35441         }
35442         this.beginUpdate();
35443         // add children..
35444         var region = '';
35445         var abn = {};
35446         Roo.each(xitems, function(i)  {
35447             region = nb && i.region ? i.region : false;
35448             
35449             var add = ret.addxtype(i);
35450            
35451             if (region) {
35452                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35453                 if (!i.background) {
35454                     abn[region] = nb[region] ;
35455                 }
35456             }
35457             
35458         });
35459         this.endUpdate();
35460
35461         // make the last non-background panel active..
35462         //if (nb) { Roo.log(abn); }
35463         if (nb) {
35464             
35465             for(var r in abn) {
35466                 region = this.getRegion(r);
35467                 if (region) {
35468                     // tried using nb[r], but it does not work..
35469                      
35470                     region.showPanel(abn[r]);
35471                    
35472                 }
35473             }
35474         }
35475         return ret;
35476         
35477     },
35478     
35479     
35480 // private
35481     factory : function(cfg)
35482     {
35483         
35484         var validRegions = Roo.bootstrap.layout.Border.regions;
35485
35486         var target = cfg.region;
35487         cfg.mgr = this;
35488         
35489         var r = Roo.bootstrap.layout;
35490         Roo.log(target);
35491         switch(target){
35492             case "north":
35493                 return new r.North(cfg);
35494             case "south":
35495                 return new r.South(cfg);
35496             case "east":
35497                 return new r.East(cfg);
35498             case "west":
35499                 return new r.West(cfg);
35500             case "center":
35501                 return new r.Center(cfg);
35502         }
35503         throw 'Layout region "'+target+'" not supported.';
35504     }
35505     
35506     
35507 });
35508  /*
35509  * Based on:
35510  * Ext JS Library 1.1.1
35511  * Copyright(c) 2006-2007, Ext JS, LLC.
35512  *
35513  * Originally Released Under LGPL - original licence link has changed is not relivant.
35514  *
35515  * Fork - LGPL
35516  * <script type="text/javascript">
35517  */
35518  
35519 /**
35520  * @class Roo.bootstrap.layout.Basic
35521  * @extends Roo.util.Observable
35522  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35523  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35524  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35525  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35526  * @cfg {string}   region  the region that it inhabits..
35527  * @cfg {bool}   skipConfig skip config?
35528  * 
35529
35530  */
35531 Roo.bootstrap.layout.Basic = function(config){
35532     
35533     this.mgr = config.mgr;
35534     
35535     this.position = config.region;
35536     
35537     var skipConfig = config.skipConfig;
35538     
35539     this.events = {
35540         /**
35541          * @scope Roo.BasicLayoutRegion
35542          */
35543         
35544         /**
35545          * @event beforeremove
35546          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35547          * @param {Roo.LayoutRegion} this
35548          * @param {Roo.ContentPanel} panel The panel
35549          * @param {Object} e The cancel event object
35550          */
35551         "beforeremove" : true,
35552         /**
35553          * @event invalidated
35554          * Fires when the layout for this region is changed.
35555          * @param {Roo.LayoutRegion} this
35556          */
35557         "invalidated" : true,
35558         /**
35559          * @event visibilitychange
35560          * Fires when this region is shown or hidden 
35561          * @param {Roo.LayoutRegion} this
35562          * @param {Boolean} visibility true or false
35563          */
35564         "visibilitychange" : true,
35565         /**
35566          * @event paneladded
35567          * Fires when a panel is added. 
35568          * @param {Roo.LayoutRegion} this
35569          * @param {Roo.ContentPanel} panel The panel
35570          */
35571         "paneladded" : true,
35572         /**
35573          * @event panelremoved
35574          * Fires when a panel is removed. 
35575          * @param {Roo.LayoutRegion} this
35576          * @param {Roo.ContentPanel} panel The panel
35577          */
35578         "panelremoved" : true,
35579         /**
35580          * @event beforecollapse
35581          * Fires when this region before collapse.
35582          * @param {Roo.LayoutRegion} this
35583          */
35584         "beforecollapse" : true,
35585         /**
35586          * @event collapsed
35587          * Fires when this region is collapsed.
35588          * @param {Roo.LayoutRegion} this
35589          */
35590         "collapsed" : true,
35591         /**
35592          * @event expanded
35593          * Fires when this region is expanded.
35594          * @param {Roo.LayoutRegion} this
35595          */
35596         "expanded" : true,
35597         /**
35598          * @event slideshow
35599          * Fires when this region is slid into view.
35600          * @param {Roo.LayoutRegion} this
35601          */
35602         "slideshow" : true,
35603         /**
35604          * @event slidehide
35605          * Fires when this region slides out of view. 
35606          * @param {Roo.LayoutRegion} this
35607          */
35608         "slidehide" : true,
35609         /**
35610          * @event panelactivated
35611          * Fires when a panel is activated. 
35612          * @param {Roo.LayoutRegion} this
35613          * @param {Roo.ContentPanel} panel The activated panel
35614          */
35615         "panelactivated" : true,
35616         /**
35617          * @event resized
35618          * Fires when the user resizes this region. 
35619          * @param {Roo.LayoutRegion} this
35620          * @param {Number} newSize The new size (width for east/west, height for north/south)
35621          */
35622         "resized" : true
35623     };
35624     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35625     this.panels = new Roo.util.MixedCollection();
35626     this.panels.getKey = this.getPanelId.createDelegate(this);
35627     this.box = null;
35628     this.activePanel = null;
35629     // ensure listeners are added...
35630     
35631     if (config.listeners || config.events) {
35632         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35633             listeners : config.listeners || {},
35634             events : config.events || {}
35635         });
35636     }
35637     
35638     if(skipConfig !== true){
35639         this.applyConfig(config);
35640     }
35641 };
35642
35643 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35644 {
35645     getPanelId : function(p){
35646         return p.getId();
35647     },
35648     
35649     applyConfig : function(config){
35650         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35651         this.config = config;
35652         
35653     },
35654     
35655     /**
35656      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35657      * the width, for horizontal (north, south) the height.
35658      * @param {Number} newSize The new width or height
35659      */
35660     resizeTo : function(newSize){
35661         var el = this.el ? this.el :
35662                  (this.activePanel ? this.activePanel.getEl() : null);
35663         if(el){
35664             switch(this.position){
35665                 case "east":
35666                 case "west":
35667                     el.setWidth(newSize);
35668                     this.fireEvent("resized", this, newSize);
35669                 break;
35670                 case "north":
35671                 case "south":
35672                     el.setHeight(newSize);
35673                     this.fireEvent("resized", this, newSize);
35674                 break;                
35675             }
35676         }
35677     },
35678     
35679     getBox : function(){
35680         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35681     },
35682     
35683     getMargins : function(){
35684         return this.margins;
35685     },
35686     
35687     updateBox : function(box){
35688         this.box = box;
35689         var el = this.activePanel.getEl();
35690         el.dom.style.left = box.x + "px";
35691         el.dom.style.top = box.y + "px";
35692         this.activePanel.setSize(box.width, box.height);
35693     },
35694     
35695     /**
35696      * Returns the container element for this region.
35697      * @return {Roo.Element}
35698      */
35699     getEl : function(){
35700         return this.activePanel;
35701     },
35702     
35703     /**
35704      * Returns true if this region is currently visible.
35705      * @return {Boolean}
35706      */
35707     isVisible : function(){
35708         return this.activePanel ? true : false;
35709     },
35710     
35711     setActivePanel : function(panel){
35712         panel = this.getPanel(panel);
35713         if(this.activePanel && this.activePanel != panel){
35714             this.activePanel.setActiveState(false);
35715             this.activePanel.getEl().setLeftTop(-10000,-10000);
35716         }
35717         this.activePanel = panel;
35718         panel.setActiveState(true);
35719         if(this.box){
35720             panel.setSize(this.box.width, this.box.height);
35721         }
35722         this.fireEvent("panelactivated", this, panel);
35723         this.fireEvent("invalidated");
35724     },
35725     
35726     /**
35727      * Show the specified panel.
35728      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35729      * @return {Roo.ContentPanel} The shown panel or null
35730      */
35731     showPanel : function(panel){
35732         panel = this.getPanel(panel);
35733         if(panel){
35734             this.setActivePanel(panel);
35735         }
35736         return panel;
35737     },
35738     
35739     /**
35740      * Get the active panel for this region.
35741      * @return {Roo.ContentPanel} The active panel or null
35742      */
35743     getActivePanel : function(){
35744         return this.activePanel;
35745     },
35746     
35747     /**
35748      * Add the passed ContentPanel(s)
35749      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35750      * @return {Roo.ContentPanel} The panel added (if only one was added)
35751      */
35752     add : function(panel){
35753         if(arguments.length > 1){
35754             for(var i = 0, len = arguments.length; i < len; i++) {
35755                 this.add(arguments[i]);
35756             }
35757             return null;
35758         }
35759         if(this.hasPanel(panel)){
35760             this.showPanel(panel);
35761             return panel;
35762         }
35763         var el = panel.getEl();
35764         if(el.dom.parentNode != this.mgr.el.dom){
35765             this.mgr.el.dom.appendChild(el.dom);
35766         }
35767         if(panel.setRegion){
35768             panel.setRegion(this);
35769         }
35770         this.panels.add(panel);
35771         el.setStyle("position", "absolute");
35772         if(!panel.background){
35773             this.setActivePanel(panel);
35774             if(this.config.initialSize && this.panels.getCount()==1){
35775                 this.resizeTo(this.config.initialSize);
35776             }
35777         }
35778         this.fireEvent("paneladded", this, panel);
35779         return panel;
35780     },
35781     
35782     /**
35783      * Returns true if the panel is in this region.
35784      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35785      * @return {Boolean}
35786      */
35787     hasPanel : function(panel){
35788         if(typeof panel == "object"){ // must be panel obj
35789             panel = panel.getId();
35790         }
35791         return this.getPanel(panel) ? true : false;
35792     },
35793     
35794     /**
35795      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35796      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35797      * @param {Boolean} preservePanel Overrides the config preservePanel option
35798      * @return {Roo.ContentPanel} The panel that was removed
35799      */
35800     remove : function(panel, preservePanel){
35801         panel = this.getPanel(panel);
35802         if(!panel){
35803             return null;
35804         }
35805         var e = {};
35806         this.fireEvent("beforeremove", this, panel, e);
35807         if(e.cancel === true){
35808             return null;
35809         }
35810         var panelId = panel.getId();
35811         this.panels.removeKey(panelId);
35812         return panel;
35813     },
35814     
35815     /**
35816      * Returns the panel specified or null if it's not in this region.
35817      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35818      * @return {Roo.ContentPanel}
35819      */
35820     getPanel : function(id){
35821         if(typeof id == "object"){ // must be panel obj
35822             return id;
35823         }
35824         return this.panels.get(id);
35825     },
35826     
35827     /**
35828      * Returns this regions position (north/south/east/west/center).
35829      * @return {String} 
35830      */
35831     getPosition: function(){
35832         return this.position;    
35833     }
35834 });/*
35835  * Based on:
35836  * Ext JS Library 1.1.1
35837  * Copyright(c) 2006-2007, Ext JS, LLC.
35838  *
35839  * Originally Released Under LGPL - original licence link has changed is not relivant.
35840  *
35841  * Fork - LGPL
35842  * <script type="text/javascript">
35843  */
35844  
35845 /**
35846  * @class Roo.bootstrap.layout.Region
35847  * @extends Roo.bootstrap.layout.Basic
35848  * This class represents a region in a layout manager.
35849  
35850  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35851  * @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})
35852  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35853  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35854  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35855  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35856  * @cfg {String}    title           The title for the region (overrides panel titles)
35857  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35858  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35859  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35860  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35861  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35862  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35863  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35864  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35865  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35866  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35867
35868  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35869  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35870  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35871  * @cfg {Number}    width           For East/West panels
35872  * @cfg {Number}    height          For North/South panels
35873  * @cfg {Boolean}   split           To show the splitter
35874  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35875  * 
35876  * @cfg {string}   cls             Extra CSS classes to add to region
35877  * 
35878  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35879  * @cfg {string}   region  the region that it inhabits..
35880  *
35881
35882  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35883  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35884
35885  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35886  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35887  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35888  */
35889 Roo.bootstrap.layout.Region = function(config)
35890 {
35891     this.applyConfig(config);
35892
35893     var mgr = config.mgr;
35894     var pos = config.region;
35895     config.skipConfig = true;
35896     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35897     
35898     if (mgr.el) {
35899         this.onRender(mgr.el);   
35900     }
35901      
35902     this.visible = true;
35903     this.collapsed = false;
35904     this.unrendered_panels = [];
35905 };
35906
35907 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35908
35909     position: '', // set by wrapper (eg. north/south etc..)
35910     unrendered_panels : null,  // unrendered panels.
35911     createBody : function(){
35912         /** This region's body element 
35913         * @type Roo.Element */
35914         this.bodyEl = this.el.createChild({
35915                 tag: "div",
35916                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35917         });
35918     },
35919
35920     onRender: function(ctr, pos)
35921     {
35922         var dh = Roo.DomHelper;
35923         /** This region's container element 
35924         * @type Roo.Element */
35925         this.el = dh.append(ctr.dom, {
35926                 tag: "div",
35927                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35928             }, true);
35929         /** This region's title element 
35930         * @type Roo.Element */
35931     
35932         this.titleEl = dh.append(this.el.dom,
35933             {
35934                     tag: "div",
35935                     unselectable: "on",
35936                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35937                     children:[
35938                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35939                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35940                     ]}, true);
35941         
35942         this.titleEl.enableDisplayMode();
35943         /** This region's title text element 
35944         * @type HTMLElement */
35945         this.titleTextEl = this.titleEl.dom.firstChild;
35946         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35947         /*
35948         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35949         this.closeBtn.enableDisplayMode();
35950         this.closeBtn.on("click", this.closeClicked, this);
35951         this.closeBtn.hide();
35952     */
35953         this.createBody(this.config);
35954         if(this.config.hideWhenEmpty){
35955             this.hide();
35956             this.on("paneladded", this.validateVisibility, this);
35957             this.on("panelremoved", this.validateVisibility, this);
35958         }
35959         if(this.autoScroll){
35960             this.bodyEl.setStyle("overflow", "auto");
35961         }else{
35962             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35963         }
35964         //if(c.titlebar !== false){
35965             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35966                 this.titleEl.hide();
35967             }else{
35968                 this.titleEl.show();
35969                 if(this.config.title){
35970                     this.titleTextEl.innerHTML = this.config.title;
35971                 }
35972             }
35973         //}
35974         if(this.config.collapsed){
35975             this.collapse(true);
35976         }
35977         if(this.config.hidden){
35978             this.hide();
35979         }
35980         
35981         if (this.unrendered_panels && this.unrendered_panels.length) {
35982             for (var i =0;i< this.unrendered_panels.length; i++) {
35983                 this.add(this.unrendered_panels[i]);
35984             }
35985             this.unrendered_panels = null;
35986             
35987         }
35988         
35989     },
35990     
35991     applyConfig : function(c)
35992     {
35993         /*
35994          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35995             var dh = Roo.DomHelper;
35996             if(c.titlebar !== false){
35997                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35998                 this.collapseBtn.on("click", this.collapse, this);
35999                 this.collapseBtn.enableDisplayMode();
36000                 /*
36001                 if(c.showPin === true || this.showPin){
36002                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36003                     this.stickBtn.enableDisplayMode();
36004                     this.stickBtn.on("click", this.expand, this);
36005                     this.stickBtn.hide();
36006                 }
36007                 
36008             }
36009             */
36010             /** This region's collapsed element
36011             * @type Roo.Element */
36012             /*
36013              *
36014             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36015                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36016             ]}, true);
36017             
36018             if(c.floatable !== false){
36019                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36020                this.collapsedEl.on("click", this.collapseClick, this);
36021             }
36022
36023             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36024                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36025                    id: "message", unselectable: "on", style:{"float":"left"}});
36026                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36027              }
36028             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36029             this.expandBtn.on("click", this.expand, this);
36030             
36031         }
36032         
36033         if(this.collapseBtn){
36034             this.collapseBtn.setVisible(c.collapsible == true);
36035         }
36036         
36037         this.cmargins = c.cmargins || this.cmargins ||
36038                          (this.position == "west" || this.position == "east" ?
36039                              {top: 0, left: 2, right:2, bottom: 0} :
36040                              {top: 2, left: 0, right:0, bottom: 2});
36041         */
36042         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36043         
36044         
36045         this.bottomTabs = c.tabPosition != "top";
36046         
36047         this.autoScroll = c.autoScroll || false;
36048         
36049         
36050        
36051         
36052         this.duration = c.duration || .30;
36053         this.slideDuration = c.slideDuration || .45;
36054         this.config = c;
36055        
36056     },
36057     /**
36058      * Returns true if this region is currently visible.
36059      * @return {Boolean}
36060      */
36061     isVisible : function(){
36062         return this.visible;
36063     },
36064
36065     /**
36066      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36067      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36068      */
36069     //setCollapsedTitle : function(title){
36070     //    title = title || "&#160;";
36071      //   if(this.collapsedTitleTextEl){
36072       //      this.collapsedTitleTextEl.innerHTML = title;
36073        // }
36074     //},
36075
36076     getBox : function(){
36077         var b;
36078       //  if(!this.collapsed){
36079             b = this.el.getBox(false, true);
36080        // }else{
36081           //  b = this.collapsedEl.getBox(false, true);
36082         //}
36083         return b;
36084     },
36085
36086     getMargins : function(){
36087         return this.margins;
36088         //return this.collapsed ? this.cmargins : this.margins;
36089     },
36090 /*
36091     highlight : function(){
36092         this.el.addClass("x-layout-panel-dragover");
36093     },
36094
36095     unhighlight : function(){
36096         this.el.removeClass("x-layout-panel-dragover");
36097     },
36098 */
36099     updateBox : function(box)
36100     {
36101         if (!this.bodyEl) {
36102             return; // not rendered yet..
36103         }
36104         
36105         this.box = box;
36106         if(!this.collapsed){
36107             this.el.dom.style.left = box.x + "px";
36108             this.el.dom.style.top = box.y + "px";
36109             this.updateBody(box.width, box.height);
36110         }else{
36111             this.collapsedEl.dom.style.left = box.x + "px";
36112             this.collapsedEl.dom.style.top = box.y + "px";
36113             this.collapsedEl.setSize(box.width, box.height);
36114         }
36115         if(this.tabs){
36116             this.tabs.autoSizeTabs();
36117         }
36118     },
36119
36120     updateBody : function(w, h)
36121     {
36122         if(w !== null){
36123             this.el.setWidth(w);
36124             w -= this.el.getBorderWidth("rl");
36125             if(this.config.adjustments){
36126                 w += this.config.adjustments[0];
36127             }
36128         }
36129         if(h !== null && h > 0){
36130             this.el.setHeight(h);
36131             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36132             h -= this.el.getBorderWidth("tb");
36133             if(this.config.adjustments){
36134                 h += this.config.adjustments[1];
36135             }
36136             this.bodyEl.setHeight(h);
36137             if(this.tabs){
36138                 h = this.tabs.syncHeight(h);
36139             }
36140         }
36141         if(this.panelSize){
36142             w = w !== null ? w : this.panelSize.width;
36143             h = h !== null ? h : this.panelSize.height;
36144         }
36145         if(this.activePanel){
36146             var el = this.activePanel.getEl();
36147             w = w !== null ? w : el.getWidth();
36148             h = h !== null ? h : el.getHeight();
36149             this.panelSize = {width: w, height: h};
36150             this.activePanel.setSize(w, h);
36151         }
36152         if(Roo.isIE && this.tabs){
36153             this.tabs.el.repaint();
36154         }
36155     },
36156
36157     /**
36158      * Returns the container element for this region.
36159      * @return {Roo.Element}
36160      */
36161     getEl : function(){
36162         return this.el;
36163     },
36164
36165     /**
36166      * Hides this region.
36167      */
36168     hide : function(){
36169         //if(!this.collapsed){
36170             this.el.dom.style.left = "-2000px";
36171             this.el.hide();
36172         //}else{
36173          //   this.collapsedEl.dom.style.left = "-2000px";
36174          //   this.collapsedEl.hide();
36175        // }
36176         this.visible = false;
36177         this.fireEvent("visibilitychange", this, false);
36178     },
36179
36180     /**
36181      * Shows this region if it was previously hidden.
36182      */
36183     show : function(){
36184         //if(!this.collapsed){
36185             this.el.show();
36186         //}else{
36187         //    this.collapsedEl.show();
36188        // }
36189         this.visible = true;
36190         this.fireEvent("visibilitychange", this, true);
36191     },
36192 /*
36193     closeClicked : function(){
36194         if(this.activePanel){
36195             this.remove(this.activePanel);
36196         }
36197     },
36198
36199     collapseClick : function(e){
36200         if(this.isSlid){
36201            e.stopPropagation();
36202            this.slideIn();
36203         }else{
36204            e.stopPropagation();
36205            this.slideOut();
36206         }
36207     },
36208 */
36209     /**
36210      * Collapses this region.
36211      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36212      */
36213     /*
36214     collapse : function(skipAnim, skipCheck = false){
36215         if(this.collapsed) {
36216             return;
36217         }
36218         
36219         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36220             
36221             this.collapsed = true;
36222             if(this.split){
36223                 this.split.el.hide();
36224             }
36225             if(this.config.animate && skipAnim !== true){
36226                 this.fireEvent("invalidated", this);
36227                 this.animateCollapse();
36228             }else{
36229                 this.el.setLocation(-20000,-20000);
36230                 this.el.hide();
36231                 this.collapsedEl.show();
36232                 this.fireEvent("collapsed", this);
36233                 this.fireEvent("invalidated", this);
36234             }
36235         }
36236         
36237     },
36238 */
36239     animateCollapse : function(){
36240         // overridden
36241     },
36242
36243     /**
36244      * Expands this region if it was previously collapsed.
36245      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36246      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36247      */
36248     /*
36249     expand : function(e, skipAnim){
36250         if(e) {
36251             e.stopPropagation();
36252         }
36253         if(!this.collapsed || this.el.hasActiveFx()) {
36254             return;
36255         }
36256         if(this.isSlid){
36257             this.afterSlideIn();
36258             skipAnim = true;
36259         }
36260         this.collapsed = false;
36261         if(this.config.animate && skipAnim !== true){
36262             this.animateExpand();
36263         }else{
36264             this.el.show();
36265             if(this.split){
36266                 this.split.el.show();
36267             }
36268             this.collapsedEl.setLocation(-2000,-2000);
36269             this.collapsedEl.hide();
36270             this.fireEvent("invalidated", this);
36271             this.fireEvent("expanded", this);
36272         }
36273     },
36274 */
36275     animateExpand : function(){
36276         // overridden
36277     },
36278
36279     initTabs : function()
36280     {
36281         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36282         
36283         var ts = new Roo.bootstrap.panel.Tabs({
36284                 el: this.bodyEl.dom,
36285                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36286                 disableTooltips: this.config.disableTabTips,
36287                 toolbar : this.config.toolbar
36288             });
36289         
36290         if(this.config.hideTabs){
36291             ts.stripWrap.setDisplayed(false);
36292         }
36293         this.tabs = ts;
36294         ts.resizeTabs = this.config.resizeTabs === true;
36295         ts.minTabWidth = this.config.minTabWidth || 40;
36296         ts.maxTabWidth = this.config.maxTabWidth || 250;
36297         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36298         ts.monitorResize = false;
36299         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36300         ts.bodyEl.addClass('roo-layout-tabs-body');
36301         this.panels.each(this.initPanelAsTab, this);
36302     },
36303
36304     initPanelAsTab : function(panel){
36305         var ti = this.tabs.addTab(
36306             panel.getEl().id,
36307             panel.getTitle(),
36308             null,
36309             this.config.closeOnTab && panel.isClosable(),
36310             panel.tpl
36311         );
36312         if(panel.tabTip !== undefined){
36313             ti.setTooltip(panel.tabTip);
36314         }
36315         ti.on("activate", function(){
36316               this.setActivePanel(panel);
36317         }, this);
36318         
36319         if(this.config.closeOnTab){
36320             ti.on("beforeclose", function(t, e){
36321                 e.cancel = true;
36322                 this.remove(panel);
36323             }, this);
36324         }
36325         
36326         panel.tabItem = ti;
36327         
36328         return ti;
36329     },
36330
36331     updatePanelTitle : function(panel, title)
36332     {
36333         if(this.activePanel == panel){
36334             this.updateTitle(title);
36335         }
36336         if(this.tabs){
36337             var ti = this.tabs.getTab(panel.getEl().id);
36338             ti.setText(title);
36339             if(panel.tabTip !== undefined){
36340                 ti.setTooltip(panel.tabTip);
36341             }
36342         }
36343     },
36344
36345     updateTitle : function(title){
36346         if(this.titleTextEl && !this.config.title){
36347             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36348         }
36349     },
36350
36351     setActivePanel : function(panel)
36352     {
36353         panel = this.getPanel(panel);
36354         if(this.activePanel && this.activePanel != panel){
36355             if(this.activePanel.setActiveState(false) === false){
36356                 return;
36357             }
36358         }
36359         this.activePanel = panel;
36360         panel.setActiveState(true);
36361         if(this.panelSize){
36362             panel.setSize(this.panelSize.width, this.panelSize.height);
36363         }
36364         if(this.closeBtn){
36365             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36366         }
36367         this.updateTitle(panel.getTitle());
36368         if(this.tabs){
36369             this.fireEvent("invalidated", this);
36370         }
36371         this.fireEvent("panelactivated", this, panel);
36372     },
36373
36374     /**
36375      * Shows the specified panel.
36376      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36377      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36378      */
36379     showPanel : function(panel)
36380     {
36381         panel = this.getPanel(panel);
36382         if(panel){
36383             if(this.tabs){
36384                 var tab = this.tabs.getTab(panel.getEl().id);
36385                 if(tab.isHidden()){
36386                     this.tabs.unhideTab(tab.id);
36387                 }
36388                 tab.activate();
36389             }else{
36390                 this.setActivePanel(panel);
36391             }
36392         }
36393         return panel;
36394     },
36395
36396     /**
36397      * Get the active panel for this region.
36398      * @return {Roo.ContentPanel} The active panel or null
36399      */
36400     getActivePanel : function(){
36401         return this.activePanel;
36402     },
36403
36404     validateVisibility : function(){
36405         if(this.panels.getCount() < 1){
36406             this.updateTitle("&#160;");
36407             this.closeBtn.hide();
36408             this.hide();
36409         }else{
36410             if(!this.isVisible()){
36411                 this.show();
36412             }
36413         }
36414     },
36415
36416     /**
36417      * Adds the passed ContentPanel(s) to this region.
36418      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36419      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36420      */
36421     add : function(panel)
36422     {
36423         if(arguments.length > 1){
36424             for(var i = 0, len = arguments.length; i < len; i++) {
36425                 this.add(arguments[i]);
36426             }
36427             return null;
36428         }
36429         
36430         // if we have not been rendered yet, then we can not really do much of this..
36431         if (!this.bodyEl) {
36432             this.unrendered_panels.push(panel);
36433             return panel;
36434         }
36435         
36436         
36437         
36438         
36439         if(this.hasPanel(panel)){
36440             this.showPanel(panel);
36441             return panel;
36442         }
36443         panel.setRegion(this);
36444         this.panels.add(panel);
36445        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36446             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36447             // and hide them... ???
36448             this.bodyEl.dom.appendChild(panel.getEl().dom);
36449             if(panel.background !== true){
36450                 this.setActivePanel(panel);
36451             }
36452             this.fireEvent("paneladded", this, panel);
36453             return panel;
36454         }
36455         */
36456         if(!this.tabs){
36457             this.initTabs();
36458         }else{
36459             this.initPanelAsTab(panel);
36460         }
36461         
36462         
36463         if(panel.background !== true){
36464             this.tabs.activate(panel.getEl().id);
36465         }
36466         this.fireEvent("paneladded", this, panel);
36467         return panel;
36468     },
36469
36470     /**
36471      * Hides the tab for the specified panel.
36472      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36473      */
36474     hidePanel : function(panel){
36475         if(this.tabs && (panel = this.getPanel(panel))){
36476             this.tabs.hideTab(panel.getEl().id);
36477         }
36478     },
36479
36480     /**
36481      * Unhides the tab for a previously hidden panel.
36482      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36483      */
36484     unhidePanel : function(panel){
36485         if(this.tabs && (panel = this.getPanel(panel))){
36486             this.tabs.unhideTab(panel.getEl().id);
36487         }
36488     },
36489
36490     clearPanels : function(){
36491         while(this.panels.getCount() > 0){
36492              this.remove(this.panels.first());
36493         }
36494     },
36495
36496     /**
36497      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36498      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36499      * @param {Boolean} preservePanel Overrides the config preservePanel option
36500      * @return {Roo.ContentPanel} The panel that was removed
36501      */
36502     remove : function(panel, preservePanel)
36503     {
36504         panel = this.getPanel(panel);
36505         if(!panel){
36506             return null;
36507         }
36508         var e = {};
36509         this.fireEvent("beforeremove", this, panel, e);
36510         if(e.cancel === true){
36511             return null;
36512         }
36513         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36514         var panelId = panel.getId();
36515         this.panels.removeKey(panelId);
36516         if(preservePanel){
36517             document.body.appendChild(panel.getEl().dom);
36518         }
36519         if(this.tabs){
36520             this.tabs.removeTab(panel.getEl().id);
36521         }else if (!preservePanel){
36522             this.bodyEl.dom.removeChild(panel.getEl().dom);
36523         }
36524         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36525             var p = this.panels.first();
36526             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36527             tempEl.appendChild(p.getEl().dom);
36528             this.bodyEl.update("");
36529             this.bodyEl.dom.appendChild(p.getEl().dom);
36530             tempEl = null;
36531             this.updateTitle(p.getTitle());
36532             this.tabs = null;
36533             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36534             this.setActivePanel(p);
36535         }
36536         panel.setRegion(null);
36537         if(this.activePanel == panel){
36538             this.activePanel = null;
36539         }
36540         if(this.config.autoDestroy !== false && preservePanel !== true){
36541             try{panel.destroy();}catch(e){}
36542         }
36543         this.fireEvent("panelremoved", this, panel);
36544         return panel;
36545     },
36546
36547     /**
36548      * Returns the TabPanel component used by this region
36549      * @return {Roo.TabPanel}
36550      */
36551     getTabs : function(){
36552         return this.tabs;
36553     },
36554
36555     createTool : function(parentEl, className){
36556         var btn = Roo.DomHelper.append(parentEl, {
36557             tag: "div",
36558             cls: "x-layout-tools-button",
36559             children: [ {
36560                 tag: "div",
36561                 cls: "roo-layout-tools-button-inner " + className,
36562                 html: "&#160;"
36563             }]
36564         }, true);
36565         btn.addClassOnOver("roo-layout-tools-button-over");
36566         return btn;
36567     }
36568 });/*
36569  * Based on:
36570  * Ext JS Library 1.1.1
36571  * Copyright(c) 2006-2007, Ext JS, LLC.
36572  *
36573  * Originally Released Under LGPL - original licence link has changed is not relivant.
36574  *
36575  * Fork - LGPL
36576  * <script type="text/javascript">
36577  */
36578  
36579
36580
36581 /**
36582  * @class Roo.SplitLayoutRegion
36583  * @extends Roo.LayoutRegion
36584  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36585  */
36586 Roo.bootstrap.layout.Split = function(config){
36587     this.cursor = config.cursor;
36588     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36589 };
36590
36591 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36592 {
36593     splitTip : "Drag to resize.",
36594     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36595     useSplitTips : false,
36596
36597     applyConfig : function(config){
36598         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36599     },
36600     
36601     onRender : function(ctr,pos) {
36602         
36603         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36604         if(!this.config.split){
36605             return;
36606         }
36607         if(!this.split){
36608             
36609             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36610                             tag: "div",
36611                             id: this.el.id + "-split",
36612                             cls: "roo-layout-split roo-layout-split-"+this.position,
36613                             html: "&#160;"
36614             });
36615             /** The SplitBar for this region 
36616             * @type Roo.SplitBar */
36617             // does not exist yet...
36618             Roo.log([this.position, this.orientation]);
36619             
36620             this.split = new Roo.bootstrap.SplitBar({
36621                 dragElement : splitEl,
36622                 resizingElement: this.el,
36623                 orientation : this.orientation
36624             });
36625             
36626             this.split.on("moved", this.onSplitMove, this);
36627             this.split.useShim = this.config.useShim === true;
36628             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36629             if(this.useSplitTips){
36630                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36631             }
36632             //if(config.collapsible){
36633             //    this.split.el.on("dblclick", this.collapse,  this);
36634             //}
36635         }
36636         if(typeof this.config.minSize != "undefined"){
36637             this.split.minSize = this.config.minSize;
36638         }
36639         if(typeof this.config.maxSize != "undefined"){
36640             this.split.maxSize = this.config.maxSize;
36641         }
36642         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36643             this.hideSplitter();
36644         }
36645         
36646     },
36647
36648     getHMaxSize : function(){
36649          var cmax = this.config.maxSize || 10000;
36650          var center = this.mgr.getRegion("center");
36651          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36652     },
36653
36654     getVMaxSize : function(){
36655          var cmax = this.config.maxSize || 10000;
36656          var center = this.mgr.getRegion("center");
36657          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36658     },
36659
36660     onSplitMove : function(split, newSize){
36661         this.fireEvent("resized", this, newSize);
36662     },
36663     
36664     /** 
36665      * Returns the {@link Roo.SplitBar} for this region.
36666      * @return {Roo.SplitBar}
36667      */
36668     getSplitBar : function(){
36669         return this.split;
36670     },
36671     
36672     hide : function(){
36673         this.hideSplitter();
36674         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36675     },
36676
36677     hideSplitter : function(){
36678         if(this.split){
36679             this.split.el.setLocation(-2000,-2000);
36680             this.split.el.hide();
36681         }
36682     },
36683
36684     show : function(){
36685         if(this.split){
36686             this.split.el.show();
36687         }
36688         Roo.bootstrap.layout.Split.superclass.show.call(this);
36689     },
36690     
36691     beforeSlide: function(){
36692         if(Roo.isGecko){// firefox overflow auto bug workaround
36693             this.bodyEl.clip();
36694             if(this.tabs) {
36695                 this.tabs.bodyEl.clip();
36696             }
36697             if(this.activePanel){
36698                 this.activePanel.getEl().clip();
36699                 
36700                 if(this.activePanel.beforeSlide){
36701                     this.activePanel.beforeSlide();
36702                 }
36703             }
36704         }
36705     },
36706     
36707     afterSlide : function(){
36708         if(Roo.isGecko){// firefox overflow auto bug workaround
36709             this.bodyEl.unclip();
36710             if(this.tabs) {
36711                 this.tabs.bodyEl.unclip();
36712             }
36713             if(this.activePanel){
36714                 this.activePanel.getEl().unclip();
36715                 if(this.activePanel.afterSlide){
36716                     this.activePanel.afterSlide();
36717                 }
36718             }
36719         }
36720     },
36721
36722     initAutoHide : function(){
36723         if(this.autoHide !== false){
36724             if(!this.autoHideHd){
36725                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36726                 this.autoHideHd = {
36727                     "mouseout": function(e){
36728                         if(!e.within(this.el, true)){
36729                             st.delay(500);
36730                         }
36731                     },
36732                     "mouseover" : function(e){
36733                         st.cancel();
36734                     },
36735                     scope : this
36736                 };
36737             }
36738             this.el.on(this.autoHideHd);
36739         }
36740     },
36741
36742     clearAutoHide : function(){
36743         if(this.autoHide !== false){
36744             this.el.un("mouseout", this.autoHideHd.mouseout);
36745             this.el.un("mouseover", this.autoHideHd.mouseover);
36746         }
36747     },
36748
36749     clearMonitor : function(){
36750         Roo.get(document).un("click", this.slideInIf, this);
36751     },
36752
36753     // these names are backwards but not changed for compat
36754     slideOut : function(){
36755         if(this.isSlid || this.el.hasActiveFx()){
36756             return;
36757         }
36758         this.isSlid = true;
36759         if(this.collapseBtn){
36760             this.collapseBtn.hide();
36761         }
36762         this.closeBtnState = this.closeBtn.getStyle('display');
36763         this.closeBtn.hide();
36764         if(this.stickBtn){
36765             this.stickBtn.show();
36766         }
36767         this.el.show();
36768         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36769         this.beforeSlide();
36770         this.el.setStyle("z-index", 10001);
36771         this.el.slideIn(this.getSlideAnchor(), {
36772             callback: function(){
36773                 this.afterSlide();
36774                 this.initAutoHide();
36775                 Roo.get(document).on("click", this.slideInIf, this);
36776                 this.fireEvent("slideshow", this);
36777             },
36778             scope: this,
36779             block: true
36780         });
36781     },
36782
36783     afterSlideIn : function(){
36784         this.clearAutoHide();
36785         this.isSlid = false;
36786         this.clearMonitor();
36787         this.el.setStyle("z-index", "");
36788         if(this.collapseBtn){
36789             this.collapseBtn.show();
36790         }
36791         this.closeBtn.setStyle('display', this.closeBtnState);
36792         if(this.stickBtn){
36793             this.stickBtn.hide();
36794         }
36795         this.fireEvent("slidehide", this);
36796     },
36797
36798     slideIn : function(cb){
36799         if(!this.isSlid || this.el.hasActiveFx()){
36800             Roo.callback(cb);
36801             return;
36802         }
36803         this.isSlid = false;
36804         this.beforeSlide();
36805         this.el.slideOut(this.getSlideAnchor(), {
36806             callback: function(){
36807                 this.el.setLeftTop(-10000, -10000);
36808                 this.afterSlide();
36809                 this.afterSlideIn();
36810                 Roo.callback(cb);
36811             },
36812             scope: this,
36813             block: true
36814         });
36815     },
36816     
36817     slideInIf : function(e){
36818         if(!e.within(this.el)){
36819             this.slideIn();
36820         }
36821     },
36822
36823     animateCollapse : function(){
36824         this.beforeSlide();
36825         this.el.setStyle("z-index", 20000);
36826         var anchor = this.getSlideAnchor();
36827         this.el.slideOut(anchor, {
36828             callback : function(){
36829                 this.el.setStyle("z-index", "");
36830                 this.collapsedEl.slideIn(anchor, {duration:.3});
36831                 this.afterSlide();
36832                 this.el.setLocation(-10000,-10000);
36833                 this.el.hide();
36834                 this.fireEvent("collapsed", this);
36835             },
36836             scope: this,
36837             block: true
36838         });
36839     },
36840
36841     animateExpand : function(){
36842         this.beforeSlide();
36843         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36844         this.el.setStyle("z-index", 20000);
36845         this.collapsedEl.hide({
36846             duration:.1
36847         });
36848         this.el.slideIn(this.getSlideAnchor(), {
36849             callback : function(){
36850                 this.el.setStyle("z-index", "");
36851                 this.afterSlide();
36852                 if(this.split){
36853                     this.split.el.show();
36854                 }
36855                 this.fireEvent("invalidated", this);
36856                 this.fireEvent("expanded", this);
36857             },
36858             scope: this,
36859             block: true
36860         });
36861     },
36862
36863     anchors : {
36864         "west" : "left",
36865         "east" : "right",
36866         "north" : "top",
36867         "south" : "bottom"
36868     },
36869
36870     sanchors : {
36871         "west" : "l",
36872         "east" : "r",
36873         "north" : "t",
36874         "south" : "b"
36875     },
36876
36877     canchors : {
36878         "west" : "tl-tr",
36879         "east" : "tr-tl",
36880         "north" : "tl-bl",
36881         "south" : "bl-tl"
36882     },
36883
36884     getAnchor : function(){
36885         return this.anchors[this.position];
36886     },
36887
36888     getCollapseAnchor : function(){
36889         return this.canchors[this.position];
36890     },
36891
36892     getSlideAnchor : function(){
36893         return this.sanchors[this.position];
36894     },
36895
36896     getAlignAdj : function(){
36897         var cm = this.cmargins;
36898         switch(this.position){
36899             case "west":
36900                 return [0, 0];
36901             break;
36902             case "east":
36903                 return [0, 0];
36904             break;
36905             case "north":
36906                 return [0, 0];
36907             break;
36908             case "south":
36909                 return [0, 0];
36910             break;
36911         }
36912     },
36913
36914     getExpandAdj : function(){
36915         var c = this.collapsedEl, cm = this.cmargins;
36916         switch(this.position){
36917             case "west":
36918                 return [-(cm.right+c.getWidth()+cm.left), 0];
36919             break;
36920             case "east":
36921                 return [cm.right+c.getWidth()+cm.left, 0];
36922             break;
36923             case "north":
36924                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36925             break;
36926             case "south":
36927                 return [0, cm.top+cm.bottom+c.getHeight()];
36928             break;
36929         }
36930     }
36931 });/*
36932  * Based on:
36933  * Ext JS Library 1.1.1
36934  * Copyright(c) 2006-2007, Ext JS, LLC.
36935  *
36936  * Originally Released Under LGPL - original licence link has changed is not relivant.
36937  *
36938  * Fork - LGPL
36939  * <script type="text/javascript">
36940  */
36941 /*
36942  * These classes are private internal classes
36943  */
36944 Roo.bootstrap.layout.Center = function(config){
36945     config.region = "center";
36946     Roo.bootstrap.layout.Region.call(this, config);
36947     this.visible = true;
36948     this.minWidth = config.minWidth || 20;
36949     this.minHeight = config.minHeight || 20;
36950 };
36951
36952 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36953     hide : function(){
36954         // center panel can't be hidden
36955     },
36956     
36957     show : function(){
36958         // center panel can't be hidden
36959     },
36960     
36961     getMinWidth: function(){
36962         return this.minWidth;
36963     },
36964     
36965     getMinHeight: function(){
36966         return this.minHeight;
36967     }
36968 });
36969
36970
36971
36972
36973  
36974
36975
36976
36977
36978
36979 Roo.bootstrap.layout.North = function(config)
36980 {
36981     config.region = 'north';
36982     config.cursor = 'n-resize';
36983     
36984     Roo.bootstrap.layout.Split.call(this, config);
36985     
36986     
36987     if(this.split){
36988         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36989         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36990         this.split.el.addClass("roo-layout-split-v");
36991     }
36992     var size = config.initialSize || config.height;
36993     if(typeof size != "undefined"){
36994         this.el.setHeight(size);
36995     }
36996 };
36997 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36998 {
36999     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37000     
37001     
37002     
37003     getBox : function(){
37004         if(this.collapsed){
37005             return this.collapsedEl.getBox();
37006         }
37007         var box = this.el.getBox();
37008         if(this.split){
37009             box.height += this.split.el.getHeight();
37010         }
37011         return box;
37012     },
37013     
37014     updateBox : function(box){
37015         if(this.split && !this.collapsed){
37016             box.height -= this.split.el.getHeight();
37017             this.split.el.setLeft(box.x);
37018             this.split.el.setTop(box.y+box.height);
37019             this.split.el.setWidth(box.width);
37020         }
37021         if(this.collapsed){
37022             this.updateBody(box.width, null);
37023         }
37024         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37025     }
37026 });
37027
37028
37029
37030
37031
37032 Roo.bootstrap.layout.South = function(config){
37033     config.region = 'south';
37034     config.cursor = 's-resize';
37035     Roo.bootstrap.layout.Split.call(this, config);
37036     if(this.split){
37037         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37038         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37039         this.split.el.addClass("roo-layout-split-v");
37040     }
37041     var size = config.initialSize || config.height;
37042     if(typeof size != "undefined"){
37043         this.el.setHeight(size);
37044     }
37045 };
37046
37047 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37048     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37049     getBox : function(){
37050         if(this.collapsed){
37051             return this.collapsedEl.getBox();
37052         }
37053         var box = this.el.getBox();
37054         if(this.split){
37055             var sh = this.split.el.getHeight();
37056             box.height += sh;
37057             box.y -= sh;
37058         }
37059         return box;
37060     },
37061     
37062     updateBox : function(box){
37063         if(this.split && !this.collapsed){
37064             var sh = this.split.el.getHeight();
37065             box.height -= sh;
37066             box.y += sh;
37067             this.split.el.setLeft(box.x);
37068             this.split.el.setTop(box.y-sh);
37069             this.split.el.setWidth(box.width);
37070         }
37071         if(this.collapsed){
37072             this.updateBody(box.width, null);
37073         }
37074         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37075     }
37076 });
37077
37078 Roo.bootstrap.layout.East = function(config){
37079     config.region = "east";
37080     config.cursor = "e-resize";
37081     Roo.bootstrap.layout.Split.call(this, config);
37082     if(this.split){
37083         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37084         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37085         this.split.el.addClass("roo-layout-split-h");
37086     }
37087     var size = config.initialSize || config.width;
37088     if(typeof size != "undefined"){
37089         this.el.setWidth(size);
37090     }
37091 };
37092 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37093     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37094     getBox : function(){
37095         if(this.collapsed){
37096             return this.collapsedEl.getBox();
37097         }
37098         var box = this.el.getBox();
37099         if(this.split){
37100             var sw = this.split.el.getWidth();
37101             box.width += sw;
37102             box.x -= sw;
37103         }
37104         return box;
37105     },
37106
37107     updateBox : function(box){
37108         if(this.split && !this.collapsed){
37109             var sw = this.split.el.getWidth();
37110             box.width -= sw;
37111             this.split.el.setLeft(box.x);
37112             this.split.el.setTop(box.y);
37113             this.split.el.setHeight(box.height);
37114             box.x += sw;
37115         }
37116         if(this.collapsed){
37117             this.updateBody(null, box.height);
37118         }
37119         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37120     }
37121 });
37122
37123 Roo.bootstrap.layout.West = function(config){
37124     config.region = "west";
37125     config.cursor = "w-resize";
37126     
37127     Roo.bootstrap.layout.Split.call(this, config);
37128     if(this.split){
37129         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37130         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37131         this.split.el.addClass("roo-layout-split-h");
37132     }
37133     
37134 };
37135 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37136     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37137     
37138     onRender: function(ctr, pos)
37139     {
37140         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37141         var size = this.config.initialSize || this.config.width;
37142         if(typeof size != "undefined"){
37143             this.el.setWidth(size);
37144         }
37145     },
37146     
37147     getBox : function(){
37148         if(this.collapsed){
37149             return this.collapsedEl.getBox();
37150         }
37151         var box = this.el.getBox();
37152         if(this.split){
37153             box.width += this.split.el.getWidth();
37154         }
37155         return box;
37156     },
37157     
37158     updateBox : function(box){
37159         if(this.split && !this.collapsed){
37160             var sw = this.split.el.getWidth();
37161             box.width -= sw;
37162             this.split.el.setLeft(box.x+box.width);
37163             this.split.el.setTop(box.y);
37164             this.split.el.setHeight(box.height);
37165         }
37166         if(this.collapsed){
37167             this.updateBody(null, box.height);
37168         }
37169         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37170     }
37171 });
37172 Roo.namespace("Roo.bootstrap.panel");/*
37173  * Based on:
37174  * Ext JS Library 1.1.1
37175  * Copyright(c) 2006-2007, Ext JS, LLC.
37176  *
37177  * Originally Released Under LGPL - original licence link has changed is not relivant.
37178  *
37179  * Fork - LGPL
37180  * <script type="text/javascript">
37181  */
37182 /**
37183  * @class Roo.ContentPanel
37184  * @extends Roo.util.Observable
37185  * A basic ContentPanel element.
37186  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37187  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37188  * @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
37189  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37190  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37191  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37192  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37193  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37194  * @cfg {String} title          The title for this panel
37195  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37196  * @cfg {String} url            Calls {@link #setUrl} with this value
37197  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37198  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37199  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37200  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37201  * @cfg {Boolean} badges render the badges
37202
37203  * @constructor
37204  * Create a new ContentPanel.
37205  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37206  * @param {String/Object} config A string to set only the title or a config object
37207  * @param {String} content (optional) Set the HTML content for this panel
37208  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37209  */
37210 Roo.bootstrap.panel.Content = function( config){
37211     
37212     this.tpl = config.tpl || false;
37213     
37214     var el = config.el;
37215     var content = config.content;
37216
37217     if(config.autoCreate){ // xtype is available if this is called from factory
37218         el = Roo.id();
37219     }
37220     this.el = Roo.get(el);
37221     if(!this.el && config && config.autoCreate){
37222         if(typeof config.autoCreate == "object"){
37223             if(!config.autoCreate.id){
37224                 config.autoCreate.id = config.id||el;
37225             }
37226             this.el = Roo.DomHelper.append(document.body,
37227                         config.autoCreate, true);
37228         }else{
37229             var elcfg =  {   tag: "div",
37230                             cls: "roo-layout-inactive-content",
37231                             id: config.id||el
37232                             };
37233             if (config.html) {
37234                 elcfg.html = config.html;
37235                 
37236             }
37237                         
37238             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37239         }
37240     } 
37241     this.closable = false;
37242     this.loaded = false;
37243     this.active = false;
37244    
37245       
37246     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37247         
37248         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37249         
37250         this.wrapEl = this.el; //this.el.wrap();
37251         var ti = [];
37252         if (config.toolbar.items) {
37253             ti = config.toolbar.items ;
37254             delete config.toolbar.items ;
37255         }
37256         
37257         var nitems = [];
37258         this.toolbar.render(this.wrapEl, 'before');
37259         for(var i =0;i < ti.length;i++) {
37260           //  Roo.log(['add child', items[i]]);
37261             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37262         }
37263         this.toolbar.items = nitems;
37264         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37265         delete config.toolbar;
37266         
37267     }
37268     /*
37269     // xtype created footer. - not sure if will work as we normally have to render first..
37270     if (this.footer && !this.footer.el && this.footer.xtype) {
37271         if (!this.wrapEl) {
37272             this.wrapEl = this.el.wrap();
37273         }
37274     
37275         this.footer.container = this.wrapEl.createChild();
37276          
37277         this.footer = Roo.factory(this.footer, Roo);
37278         
37279     }
37280     */
37281     
37282      if(typeof config == "string"){
37283         this.title = config;
37284     }else{
37285         Roo.apply(this, config);
37286     }
37287     
37288     if(this.resizeEl){
37289         this.resizeEl = Roo.get(this.resizeEl, true);
37290     }else{
37291         this.resizeEl = this.el;
37292     }
37293     // handle view.xtype
37294     
37295  
37296     
37297     
37298     this.addEvents({
37299         /**
37300          * @event activate
37301          * Fires when this panel is activated. 
37302          * @param {Roo.ContentPanel} this
37303          */
37304         "activate" : true,
37305         /**
37306          * @event deactivate
37307          * Fires when this panel is activated. 
37308          * @param {Roo.ContentPanel} this
37309          */
37310         "deactivate" : true,
37311
37312         /**
37313          * @event resize
37314          * Fires when this panel is resized if fitToFrame is true.
37315          * @param {Roo.ContentPanel} this
37316          * @param {Number} width The width after any component adjustments
37317          * @param {Number} height The height after any component adjustments
37318          */
37319         "resize" : true,
37320         
37321          /**
37322          * @event render
37323          * Fires when this tab is created
37324          * @param {Roo.ContentPanel} this
37325          */
37326         "render" : true
37327         
37328         
37329         
37330     });
37331     
37332
37333     
37334     
37335     if(this.autoScroll){
37336         this.resizeEl.setStyle("overflow", "auto");
37337     } else {
37338         // fix randome scrolling
37339         //this.el.on('scroll', function() {
37340         //    Roo.log('fix random scolling');
37341         //    this.scrollTo('top',0); 
37342         //});
37343     }
37344     content = content || this.content;
37345     if(content){
37346         this.setContent(content);
37347     }
37348     if(config && config.url){
37349         this.setUrl(this.url, this.params, this.loadOnce);
37350     }
37351     
37352     
37353     
37354     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37355     
37356     if (this.view && typeof(this.view.xtype) != 'undefined') {
37357         this.view.el = this.el.appendChild(document.createElement("div"));
37358         this.view = Roo.factory(this.view); 
37359         this.view.render  &&  this.view.render(false, '');  
37360     }
37361     
37362     
37363     this.fireEvent('render', this);
37364 };
37365
37366 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37367     
37368     tabTip : '',
37369     
37370     setRegion : function(region){
37371         this.region = region;
37372         this.setActiveClass(region && !this.background);
37373     },
37374     
37375     
37376     setActiveClass: function(state)
37377     {
37378         if(state){
37379            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37380            this.el.setStyle('position','relative');
37381         }else{
37382            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37383            this.el.setStyle('position', 'absolute');
37384         } 
37385     },
37386     
37387     /**
37388      * Returns the toolbar for this Panel if one was configured. 
37389      * @return {Roo.Toolbar} 
37390      */
37391     getToolbar : function(){
37392         return this.toolbar;
37393     },
37394     
37395     setActiveState : function(active)
37396     {
37397         this.active = active;
37398         this.setActiveClass(active);
37399         if(!active){
37400             if(this.fireEvent("deactivate", this) === false){
37401                 return false;
37402             }
37403             return true;
37404         }
37405         this.fireEvent("activate", this);
37406         return true;
37407     },
37408     /**
37409      * Updates this panel's element
37410      * @param {String} content The new content
37411      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37412     */
37413     setContent : function(content, loadScripts){
37414         this.el.update(content, loadScripts);
37415     },
37416
37417     ignoreResize : function(w, h){
37418         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37419             return true;
37420         }else{
37421             this.lastSize = {width: w, height: h};
37422             return false;
37423         }
37424     },
37425     /**
37426      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37427      * @return {Roo.UpdateManager} The UpdateManager
37428      */
37429     getUpdateManager : function(){
37430         return this.el.getUpdateManager();
37431     },
37432      /**
37433      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37434      * @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:
37435 <pre><code>
37436 panel.load({
37437     url: "your-url.php",
37438     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37439     callback: yourFunction,
37440     scope: yourObject, //(optional scope)
37441     discardUrl: false,
37442     nocache: false,
37443     text: "Loading...",
37444     timeout: 30,
37445     scripts: false
37446 });
37447 </code></pre>
37448      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37449      * 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.
37450      * @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}
37451      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37452      * @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.
37453      * @return {Roo.ContentPanel} this
37454      */
37455     load : function(){
37456         var um = this.el.getUpdateManager();
37457         um.update.apply(um, arguments);
37458         return this;
37459     },
37460
37461
37462     /**
37463      * 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.
37464      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37465      * @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)
37466      * @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)
37467      * @return {Roo.UpdateManager} The UpdateManager
37468      */
37469     setUrl : function(url, params, loadOnce){
37470         if(this.refreshDelegate){
37471             this.removeListener("activate", this.refreshDelegate);
37472         }
37473         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37474         this.on("activate", this.refreshDelegate);
37475         return this.el.getUpdateManager();
37476     },
37477     
37478     _handleRefresh : function(url, params, loadOnce){
37479         if(!loadOnce || !this.loaded){
37480             var updater = this.el.getUpdateManager();
37481             updater.update(url, params, this._setLoaded.createDelegate(this));
37482         }
37483     },
37484     
37485     _setLoaded : function(){
37486         this.loaded = true;
37487     }, 
37488     
37489     /**
37490      * Returns this panel's id
37491      * @return {String} 
37492      */
37493     getId : function(){
37494         return this.el.id;
37495     },
37496     
37497     /** 
37498      * Returns this panel's element - used by regiosn to add.
37499      * @return {Roo.Element} 
37500      */
37501     getEl : function(){
37502         return this.wrapEl || this.el;
37503     },
37504     
37505    
37506     
37507     adjustForComponents : function(width, height)
37508     {
37509         //Roo.log('adjustForComponents ');
37510         if(this.resizeEl != this.el){
37511             width -= this.el.getFrameWidth('lr');
37512             height -= this.el.getFrameWidth('tb');
37513         }
37514         if(this.toolbar){
37515             var te = this.toolbar.getEl();
37516             te.setWidth(width);
37517             height -= te.getHeight();
37518         }
37519         if(this.footer){
37520             var te = this.footer.getEl();
37521             te.setWidth(width);
37522             height -= te.getHeight();
37523         }
37524         
37525         
37526         if(this.adjustments){
37527             width += this.adjustments[0];
37528             height += this.adjustments[1];
37529         }
37530         return {"width": width, "height": height};
37531     },
37532     
37533     setSize : function(width, height){
37534         if(this.fitToFrame && !this.ignoreResize(width, height)){
37535             if(this.fitContainer && this.resizeEl != this.el){
37536                 this.el.setSize(width, height);
37537             }
37538             var size = this.adjustForComponents(width, height);
37539             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37540             this.fireEvent('resize', this, size.width, size.height);
37541         }
37542     },
37543     
37544     /**
37545      * Returns this panel's title
37546      * @return {String} 
37547      */
37548     getTitle : function(){
37549         
37550         if (typeof(this.title) != 'object') {
37551             return this.title;
37552         }
37553         
37554         var t = '';
37555         for (var k in this.title) {
37556             if (!this.title.hasOwnProperty(k)) {
37557                 continue;
37558             }
37559             
37560             if (k.indexOf('-') >= 0) {
37561                 var s = k.split('-');
37562                 for (var i = 0; i<s.length; i++) {
37563                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37564                 }
37565             } else {
37566                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37567             }
37568         }
37569         return t;
37570     },
37571     
37572     /**
37573      * Set this panel's title
37574      * @param {String} title
37575      */
37576     setTitle : function(title){
37577         this.title = title;
37578         if(this.region){
37579             this.region.updatePanelTitle(this, title);
37580         }
37581     },
37582     
37583     /**
37584      * Returns true is this panel was configured to be closable
37585      * @return {Boolean} 
37586      */
37587     isClosable : function(){
37588         return this.closable;
37589     },
37590     
37591     beforeSlide : function(){
37592         this.el.clip();
37593         this.resizeEl.clip();
37594     },
37595     
37596     afterSlide : function(){
37597         this.el.unclip();
37598         this.resizeEl.unclip();
37599     },
37600     
37601     /**
37602      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37603      *   Will fail silently if the {@link #setUrl} method has not been called.
37604      *   This does not activate the panel, just updates its content.
37605      */
37606     refresh : function(){
37607         if(this.refreshDelegate){
37608            this.loaded = false;
37609            this.refreshDelegate();
37610         }
37611     },
37612     
37613     /**
37614      * Destroys this panel
37615      */
37616     destroy : function(){
37617         this.el.removeAllListeners();
37618         var tempEl = document.createElement("span");
37619         tempEl.appendChild(this.el.dom);
37620         tempEl.innerHTML = "";
37621         this.el.remove();
37622         this.el = null;
37623     },
37624     
37625     /**
37626      * form - if the content panel contains a form - this is a reference to it.
37627      * @type {Roo.form.Form}
37628      */
37629     form : false,
37630     /**
37631      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37632      *    This contains a reference to it.
37633      * @type {Roo.View}
37634      */
37635     view : false,
37636     
37637       /**
37638      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37639      * <pre><code>
37640
37641 layout.addxtype({
37642        xtype : 'Form',
37643        items: [ .... ]
37644    }
37645 );
37646
37647 </code></pre>
37648      * @param {Object} cfg Xtype definition of item to add.
37649      */
37650     
37651     
37652     getChildContainer: function () {
37653         return this.getEl();
37654     }
37655     
37656     
37657     /*
37658         var  ret = new Roo.factory(cfg);
37659         return ret;
37660         
37661         
37662         // add form..
37663         if (cfg.xtype.match(/^Form$/)) {
37664             
37665             var el;
37666             //if (this.footer) {
37667             //    el = this.footer.container.insertSibling(false, 'before');
37668             //} else {
37669                 el = this.el.createChild();
37670             //}
37671
37672             this.form = new  Roo.form.Form(cfg);
37673             
37674             
37675             if ( this.form.allItems.length) {
37676                 this.form.render(el.dom);
37677             }
37678             return this.form;
37679         }
37680         // should only have one of theses..
37681         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37682             // views.. should not be just added - used named prop 'view''
37683             
37684             cfg.el = this.el.appendChild(document.createElement("div"));
37685             // factory?
37686             
37687             var ret = new Roo.factory(cfg);
37688              
37689              ret.render && ret.render(false, ''); // render blank..
37690             this.view = ret;
37691             return ret;
37692         }
37693         return false;
37694     }
37695     \*/
37696 });
37697  
37698 /**
37699  * @class Roo.bootstrap.panel.Grid
37700  * @extends Roo.bootstrap.panel.Content
37701  * @constructor
37702  * Create a new GridPanel.
37703  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37704  * @param {Object} config A the config object
37705   
37706  */
37707
37708
37709
37710 Roo.bootstrap.panel.Grid = function(config)
37711 {
37712     
37713       
37714     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37715         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37716
37717     config.el = this.wrapper;
37718     //this.el = this.wrapper;
37719     
37720       if (config.container) {
37721         // ctor'ed from a Border/panel.grid
37722         
37723         
37724         this.wrapper.setStyle("overflow", "hidden");
37725         this.wrapper.addClass('roo-grid-container');
37726
37727     }
37728     
37729     
37730     if(config.toolbar){
37731         var tool_el = this.wrapper.createChild();    
37732         this.toolbar = Roo.factory(config.toolbar);
37733         var ti = [];
37734         if (config.toolbar.items) {
37735             ti = config.toolbar.items ;
37736             delete config.toolbar.items ;
37737         }
37738         
37739         var nitems = [];
37740         this.toolbar.render(tool_el);
37741         for(var i =0;i < ti.length;i++) {
37742           //  Roo.log(['add child', items[i]]);
37743             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37744         }
37745         this.toolbar.items = nitems;
37746         
37747         delete config.toolbar;
37748     }
37749     
37750     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37751     config.grid.scrollBody = true;;
37752     config.grid.monitorWindowResize = false; // turn off autosizing
37753     config.grid.autoHeight = false;
37754     config.grid.autoWidth = false;
37755     
37756     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37757     
37758     if (config.background) {
37759         // render grid on panel activation (if panel background)
37760         this.on('activate', function(gp) {
37761             if (!gp.grid.rendered) {
37762                 gp.grid.render(this.wrapper);
37763                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37764             }
37765         });
37766             
37767     } else {
37768         this.grid.render(this.wrapper);
37769         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37770
37771     }
37772     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37773     // ??? needed ??? config.el = this.wrapper;
37774     
37775     
37776     
37777   
37778     // xtype created footer. - not sure if will work as we normally have to render first..
37779     if (this.footer && !this.footer.el && this.footer.xtype) {
37780         
37781         var ctr = this.grid.getView().getFooterPanel(true);
37782         this.footer.dataSource = this.grid.dataSource;
37783         this.footer = Roo.factory(this.footer, Roo);
37784         this.footer.render(ctr);
37785         
37786     }
37787     
37788     
37789     
37790     
37791      
37792 };
37793
37794 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37795     getId : function(){
37796         return this.grid.id;
37797     },
37798     
37799     /**
37800      * Returns the grid for this panel
37801      * @return {Roo.bootstrap.Table} 
37802      */
37803     getGrid : function(){
37804         return this.grid;    
37805     },
37806     
37807     setSize : function(width, height){
37808         if(!this.ignoreResize(width, height)){
37809             var grid = this.grid;
37810             var size = this.adjustForComponents(width, height);
37811             var gridel = grid.getGridEl();
37812             gridel.setSize(size.width, size.height);
37813             /*
37814             var thd = grid.getGridEl().select('thead',true).first();
37815             var tbd = grid.getGridEl().select('tbody', true).first();
37816             if (tbd) {
37817                 tbd.setSize(width, height - thd.getHeight());
37818             }
37819             */
37820             grid.autoSize();
37821         }
37822     },
37823      
37824     
37825     
37826     beforeSlide : function(){
37827         this.grid.getView().scroller.clip();
37828     },
37829     
37830     afterSlide : function(){
37831         this.grid.getView().scroller.unclip();
37832     },
37833     
37834     destroy : function(){
37835         this.grid.destroy();
37836         delete this.grid;
37837         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37838     }
37839 });
37840
37841 /**
37842  * @class Roo.bootstrap.panel.Nest
37843  * @extends Roo.bootstrap.panel.Content
37844  * @constructor
37845  * Create a new Panel, that can contain a layout.Border.
37846  * 
37847  * 
37848  * @param {Roo.BorderLayout} layout The layout for this panel
37849  * @param {String/Object} config A string to set only the title or a config object
37850  */
37851 Roo.bootstrap.panel.Nest = function(config)
37852 {
37853     // construct with only one argument..
37854     /* FIXME - implement nicer consturctors
37855     if (layout.layout) {
37856         config = layout;
37857         layout = config.layout;
37858         delete config.layout;
37859     }
37860     if (layout.xtype && !layout.getEl) {
37861         // then layout needs constructing..
37862         layout = Roo.factory(layout, Roo);
37863     }
37864     */
37865     
37866     config.el =  config.layout.getEl();
37867     
37868     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37869     
37870     config.layout.monitorWindowResize = false; // turn off autosizing
37871     this.layout = config.layout;
37872     this.layout.getEl().addClass("roo-layout-nested-layout");
37873     
37874     
37875     
37876     
37877 };
37878
37879 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37880
37881     setSize : function(width, height){
37882         if(!this.ignoreResize(width, height)){
37883             var size = this.adjustForComponents(width, height);
37884             var el = this.layout.getEl();
37885             if (size.height < 1) {
37886                 el.setWidth(size.width);   
37887             } else {
37888                 el.setSize(size.width, size.height);
37889             }
37890             var touch = el.dom.offsetWidth;
37891             this.layout.layout();
37892             // ie requires a double layout on the first pass
37893             if(Roo.isIE && !this.initialized){
37894                 this.initialized = true;
37895                 this.layout.layout();
37896             }
37897         }
37898     },
37899     
37900     // activate all subpanels if not currently active..
37901     
37902     setActiveState : function(active){
37903         this.active = active;
37904         this.setActiveClass(active);
37905         
37906         if(!active){
37907             this.fireEvent("deactivate", this);
37908             return;
37909         }
37910         
37911         this.fireEvent("activate", this);
37912         // not sure if this should happen before or after..
37913         if (!this.layout) {
37914             return; // should not happen..
37915         }
37916         var reg = false;
37917         for (var r in this.layout.regions) {
37918             reg = this.layout.getRegion(r);
37919             if (reg.getActivePanel()) {
37920                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37921                 reg.setActivePanel(reg.getActivePanel());
37922                 continue;
37923             }
37924             if (!reg.panels.length) {
37925                 continue;
37926             }
37927             reg.showPanel(reg.getPanel(0));
37928         }
37929         
37930         
37931         
37932         
37933     },
37934     
37935     /**
37936      * Returns the nested BorderLayout for this panel
37937      * @return {Roo.BorderLayout} 
37938      */
37939     getLayout : function(){
37940         return this.layout;
37941     },
37942     
37943      /**
37944      * Adds a xtype elements to the layout of the nested panel
37945      * <pre><code>
37946
37947 panel.addxtype({
37948        xtype : 'ContentPanel',
37949        region: 'west',
37950        items: [ .... ]
37951    }
37952 );
37953
37954 panel.addxtype({
37955         xtype : 'NestedLayoutPanel',
37956         region: 'west',
37957         layout: {
37958            center: { },
37959            west: { }   
37960         },
37961         items : [ ... list of content panels or nested layout panels.. ]
37962    }
37963 );
37964 </code></pre>
37965      * @param {Object} cfg Xtype definition of item to add.
37966      */
37967     addxtype : function(cfg) {
37968         return this.layout.addxtype(cfg);
37969     
37970     }
37971 });        /*
37972  * Based on:
37973  * Ext JS Library 1.1.1
37974  * Copyright(c) 2006-2007, Ext JS, LLC.
37975  *
37976  * Originally Released Under LGPL - original licence link has changed is not relivant.
37977  *
37978  * Fork - LGPL
37979  * <script type="text/javascript">
37980  */
37981 /**
37982  * @class Roo.TabPanel
37983  * @extends Roo.util.Observable
37984  * A lightweight tab container.
37985  * <br><br>
37986  * Usage:
37987  * <pre><code>
37988 // basic tabs 1, built from existing content
37989 var tabs = new Roo.TabPanel("tabs1");
37990 tabs.addTab("script", "View Script");
37991 tabs.addTab("markup", "View Markup");
37992 tabs.activate("script");
37993
37994 // more advanced tabs, built from javascript
37995 var jtabs = new Roo.TabPanel("jtabs");
37996 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37997
37998 // set up the UpdateManager
37999 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38000 var updater = tab2.getUpdateManager();
38001 updater.setDefaultUrl("ajax1.htm");
38002 tab2.on('activate', updater.refresh, updater, true);
38003
38004 // Use setUrl for Ajax loading
38005 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38006 tab3.setUrl("ajax2.htm", null, true);
38007
38008 // Disabled tab
38009 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38010 tab4.disable();
38011
38012 jtabs.activate("jtabs-1");
38013  * </code></pre>
38014  * @constructor
38015  * Create a new TabPanel.
38016  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38017  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38018  */
38019 Roo.bootstrap.panel.Tabs = function(config){
38020     /**
38021     * The container element for this TabPanel.
38022     * @type Roo.Element
38023     */
38024     this.el = Roo.get(config.el);
38025     delete config.el;
38026     if(config){
38027         if(typeof config == "boolean"){
38028             this.tabPosition = config ? "bottom" : "top";
38029         }else{
38030             Roo.apply(this, config);
38031         }
38032     }
38033     
38034     if(this.tabPosition == "bottom"){
38035         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38036         this.el.addClass("roo-tabs-bottom");
38037     }
38038     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38039     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38040     this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38041     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38042     if(Roo.isIE){
38043         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38044     }
38045     if(this.tabPosition != "bottom"){
38046         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38047          * @type Roo.Element
38048          */
38049         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38050         this.el.addClass("roo-tabs-top");
38051     }
38052     this.items = [];
38053
38054     this.bodyEl.setStyle("position", "relative");
38055
38056     this.active = null;
38057     this.activateDelegate = this.activate.createDelegate(this);
38058
38059     this.addEvents({
38060         /**
38061          * @event tabchange
38062          * Fires when the active tab changes
38063          * @param {Roo.TabPanel} this
38064          * @param {Roo.TabPanelItem} activePanel The new active tab
38065          */
38066         "tabchange": true,
38067         /**
38068          * @event beforetabchange
38069          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38070          * @param {Roo.TabPanel} this
38071          * @param {Object} e Set cancel to true on this object to cancel the tab change
38072          * @param {Roo.TabPanelItem} tab The tab being changed to
38073          */
38074         "beforetabchange" : true
38075     });
38076
38077     Roo.EventManager.onWindowResize(this.onResize, this);
38078     this.cpad = this.el.getPadding("lr");
38079     this.hiddenCount = 0;
38080
38081
38082     // toolbar on the tabbar support...
38083     if (this.toolbar) {
38084         alert("no toolbar support yet");
38085         this.toolbar  = false;
38086         /*
38087         var tcfg = this.toolbar;
38088         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38089         this.toolbar = new Roo.Toolbar(tcfg);
38090         if (Roo.isSafari) {
38091             var tbl = tcfg.container.child('table', true);
38092             tbl.setAttribute('width', '100%');
38093         }
38094         */
38095         
38096     }
38097    
38098
38099
38100     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38101 };
38102
38103 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38104     /*
38105      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38106      */
38107     tabPosition : "top",
38108     /*
38109      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38110      */
38111     currentTabWidth : 0,
38112     /*
38113      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38114      */
38115     minTabWidth : 40,
38116     /*
38117      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38118      */
38119     maxTabWidth : 250,
38120     /*
38121      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38122      */
38123     preferredTabWidth : 175,
38124     /*
38125      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38126      */
38127     resizeTabs : false,
38128     /*
38129      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38130      */
38131     monitorResize : true,
38132     /*
38133      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38134      */
38135     toolbar : false,
38136
38137     /**
38138      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38139      * @param {String} id The id of the div to use <b>or create</b>
38140      * @param {String} text The text for the tab
38141      * @param {String} content (optional) Content to put in the TabPanelItem body
38142      * @param {Boolean} closable (optional) True to create a close icon on the tab
38143      * @return {Roo.TabPanelItem} The created TabPanelItem
38144      */
38145     addTab : function(id, text, content, closable, tpl)
38146     {
38147         var item = new Roo.bootstrap.panel.TabItem({
38148             panel: this,
38149             id : id,
38150             text : text,
38151             closable : closable,
38152             tpl : tpl
38153         });
38154         this.addTabItem(item);
38155         if(content){
38156             item.setContent(content);
38157         }
38158         return item;
38159     },
38160
38161     /**
38162      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38163      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38164      * @return {Roo.TabPanelItem}
38165      */
38166     getTab : function(id){
38167         return this.items[id];
38168     },
38169
38170     /**
38171      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38172      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38173      */
38174     hideTab : function(id){
38175         var t = this.items[id];
38176         if(!t.isHidden()){
38177            t.setHidden(true);
38178            this.hiddenCount++;
38179            this.autoSizeTabs();
38180         }
38181     },
38182
38183     /**
38184      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38185      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38186      */
38187     unhideTab : function(id){
38188         var t = this.items[id];
38189         if(t.isHidden()){
38190            t.setHidden(false);
38191            this.hiddenCount--;
38192            this.autoSizeTabs();
38193         }
38194     },
38195
38196     /**
38197      * Adds an existing {@link Roo.TabPanelItem}.
38198      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38199      */
38200     addTabItem : function(item)
38201     {
38202         this.items[item.id] = item;
38203         this.items.push(item);
38204         this.autoSizeTabs();
38205       //  if(this.resizeTabs){
38206     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38207   //         this.autoSizeTabs();
38208 //        }else{
38209 //            item.autoSize();
38210        // }
38211     },
38212
38213     /**
38214      * Removes a {@link Roo.TabPanelItem}.
38215      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38216      */
38217     removeTab : function(id){
38218         var items = this.items;
38219         var tab = items[id];
38220         if(!tab) { return; }
38221         var index = items.indexOf(tab);
38222         if(this.active == tab && items.length > 1){
38223             var newTab = this.getNextAvailable(index);
38224             if(newTab) {
38225                 newTab.activate();
38226             }
38227         }
38228         this.stripEl.dom.removeChild(tab.pnode.dom);
38229         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38230             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38231         }
38232         items.splice(index, 1);
38233         delete this.items[tab.id];
38234         tab.fireEvent("close", tab);
38235         tab.purgeListeners();
38236         this.autoSizeTabs();
38237     },
38238
38239     getNextAvailable : function(start){
38240         var items = this.items;
38241         var index = start;
38242         // look for a next tab that will slide over to
38243         // replace the one being removed
38244         while(index < items.length){
38245             var item = items[++index];
38246             if(item && !item.isHidden()){
38247                 return item;
38248             }
38249         }
38250         // if one isn't found select the previous tab (on the left)
38251         index = start;
38252         while(index >= 0){
38253             var item = items[--index];
38254             if(item && !item.isHidden()){
38255                 return item;
38256             }
38257         }
38258         return null;
38259     },
38260
38261     /**
38262      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38263      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38264      */
38265     disableTab : function(id){
38266         var tab = this.items[id];
38267         if(tab && this.active != tab){
38268             tab.disable();
38269         }
38270     },
38271
38272     /**
38273      * Enables a {@link Roo.TabPanelItem} that is disabled.
38274      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38275      */
38276     enableTab : function(id){
38277         var tab = this.items[id];
38278         tab.enable();
38279     },
38280
38281     /**
38282      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38283      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38284      * @return {Roo.TabPanelItem} The TabPanelItem.
38285      */
38286     activate : function(id)
38287     {
38288         var tab = this.items[id];
38289         if(!tab){
38290             return null;
38291         }
38292         if(tab == this.active || tab.disabled){
38293             return tab;
38294         }
38295         var e = {};
38296         this.fireEvent("beforetabchange", this, e, tab);
38297         if(e.cancel !== true && !tab.disabled){
38298             if(this.active){
38299                 this.active.hide();
38300             }
38301             this.active = this.items[id];
38302             this.active.show();
38303             this.fireEvent("tabchange", this, this.active);
38304         }
38305         return tab;
38306     },
38307
38308     /**
38309      * Gets the active {@link Roo.TabPanelItem}.
38310      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38311      */
38312     getActiveTab : function(){
38313         return this.active;
38314     },
38315
38316     /**
38317      * Updates the tab body element to fit the height of the container element
38318      * for overflow scrolling
38319      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38320      */
38321     syncHeight : function(targetHeight){
38322         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38323         var bm = this.bodyEl.getMargins();
38324         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38325         this.bodyEl.setHeight(newHeight);
38326         return newHeight;
38327     },
38328
38329     onResize : function(){
38330         if(this.monitorResize){
38331             this.autoSizeTabs();
38332         }
38333     },
38334
38335     /**
38336      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38337      */
38338     beginUpdate : function(){
38339         this.updating = true;
38340     },
38341
38342     /**
38343      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38344      */
38345     endUpdate : function(){
38346         this.updating = false;
38347         this.autoSizeTabs();
38348     },
38349
38350     /**
38351      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38352      */
38353     autoSizeTabs : function()
38354     {
38355         var count = this.items.length;
38356         var vcount = count - this.hiddenCount;
38357         
38358         if (vcount < 2) {
38359             this.stripEl.hide();
38360         } else {
38361             this.stripEl.show();
38362         }
38363         
38364         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38365             return;
38366         }
38367         
38368         
38369         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38370         var availWidth = Math.floor(w / vcount);
38371         var b = this.stripBody;
38372         if(b.getWidth() > w){
38373             var tabs = this.items;
38374             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38375             if(availWidth < this.minTabWidth){
38376                 /*if(!this.sleft){    // incomplete scrolling code
38377                     this.createScrollButtons();
38378                 }
38379                 this.showScroll();
38380                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38381             }
38382         }else{
38383             if(this.currentTabWidth < this.preferredTabWidth){
38384                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38385             }
38386         }
38387     },
38388
38389     /**
38390      * Returns the number of tabs in this TabPanel.
38391      * @return {Number}
38392      */
38393      getCount : function(){
38394          return this.items.length;
38395      },
38396
38397     /**
38398      * Resizes all the tabs to the passed width
38399      * @param {Number} The new width
38400      */
38401     setTabWidth : function(width){
38402         this.currentTabWidth = width;
38403         for(var i = 0, len = this.items.length; i < len; i++) {
38404                 if(!this.items[i].isHidden()) {
38405                 this.items[i].setWidth(width);
38406             }
38407         }
38408     },
38409
38410     /**
38411      * Destroys this TabPanel
38412      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38413      */
38414     destroy : function(removeEl){
38415         Roo.EventManager.removeResizeListener(this.onResize, this);
38416         for(var i = 0, len = this.items.length; i < len; i++){
38417             this.items[i].purgeListeners();
38418         }
38419         if(removeEl === true){
38420             this.el.update("");
38421             this.el.remove();
38422         }
38423     },
38424     
38425     createStrip : function(container)
38426     {
38427         var strip = document.createElement("nav");
38428         strip.className = Roo.bootstrap.version == 4 ?
38429             "navbar-light bg-light" : 
38430             "navbar navbar-default"; //"x-tabs-wrap";
38431         container.appendChild(strip);
38432         return strip;
38433     },
38434     
38435     createStripList : function(strip)
38436     {
38437         // div wrapper for retard IE
38438         // returns the "tr" element.
38439         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38440         //'<div class="x-tabs-strip-wrap">'+
38441           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38442           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38443         return strip.firstChild; //.firstChild.firstChild.firstChild;
38444     },
38445     createBody : function(container)
38446     {
38447         var body = document.createElement("div");
38448         Roo.id(body, "tab-body");
38449         //Roo.fly(body).addClass("x-tabs-body");
38450         Roo.fly(body).addClass("tab-content");
38451         container.appendChild(body);
38452         return body;
38453     },
38454     createItemBody :function(bodyEl, id){
38455         var body = Roo.getDom(id);
38456         if(!body){
38457             body = document.createElement("div");
38458             body.id = id;
38459         }
38460         //Roo.fly(body).addClass("x-tabs-item-body");
38461         Roo.fly(body).addClass("tab-pane");
38462          bodyEl.insertBefore(body, bodyEl.firstChild);
38463         return body;
38464     },
38465     /** @private */
38466     createStripElements :  function(stripEl, text, closable, tpl)
38467     {
38468         var td = document.createElement("li"); // was td..
38469         td.className = 'nav-item';
38470         
38471         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38472         
38473         
38474         stripEl.appendChild(td);
38475         /*if(closable){
38476             td.className = "x-tabs-closable";
38477             if(!this.closeTpl){
38478                 this.closeTpl = new Roo.Template(
38479                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38480                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38481                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38482                 );
38483             }
38484             var el = this.closeTpl.overwrite(td, {"text": text});
38485             var close = el.getElementsByTagName("div")[0];
38486             var inner = el.getElementsByTagName("em")[0];
38487             return {"el": el, "close": close, "inner": inner};
38488         } else {
38489         */
38490         // not sure what this is..
38491 //            if(!this.tabTpl){
38492                 //this.tabTpl = new Roo.Template(
38493                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38494                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38495                 //);
38496 //                this.tabTpl = new Roo.Template(
38497 //                   '<a href="#">' +
38498 //                   '<span unselectable="on"' +
38499 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38500 //                            ' >{text}</span></a>'
38501 //                );
38502 //                
38503 //            }
38504
38505
38506             var template = tpl || this.tabTpl || false;
38507             
38508             if(!template){
38509                 template =  new Roo.Template(
38510                         Roo.bootstrap.version == 4 ? 
38511                             (
38512                                 '<a class="nav-link" href="#" unselectable="on"' +
38513                                      (this.disableTooltips ? '' : ' title="{text}"') +
38514                                      ' >{text}</a>'
38515                             ) : (
38516                                 '<a class="nav-link" href="#">' +
38517                                 '<span unselectable="on"' +
38518                                          (this.disableTooltips ? '' : ' title="{text}"') +
38519                                     ' >{text}</span></a>'
38520                             )
38521                 );
38522             }
38523             
38524             switch (typeof(template)) {
38525                 case 'object' :
38526                     break;
38527                 case 'string' :
38528                     template = new Roo.Template(template);
38529                     break;
38530                 default :
38531                     break;
38532             }
38533             
38534             var el = template.overwrite(td, {"text": text});
38535             
38536             var inner = el.getElementsByTagName("span")[0];
38537             
38538             return {"el": el, "inner": inner};
38539             
38540     }
38541         
38542     
38543 });
38544
38545 /**
38546  * @class Roo.TabPanelItem
38547  * @extends Roo.util.Observable
38548  * Represents an individual item (tab plus body) in a TabPanel.
38549  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38550  * @param {String} id The id of this TabPanelItem
38551  * @param {String} text The text for the tab of this TabPanelItem
38552  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38553  */
38554 Roo.bootstrap.panel.TabItem = function(config){
38555     /**
38556      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38557      * @type Roo.TabPanel
38558      */
38559     this.tabPanel = config.panel;
38560     /**
38561      * The id for this TabPanelItem
38562      * @type String
38563      */
38564     this.id = config.id;
38565     /** @private */
38566     this.disabled = false;
38567     /** @private */
38568     this.text = config.text;
38569     /** @private */
38570     this.loaded = false;
38571     this.closable = config.closable;
38572
38573     /**
38574      * The body element for this TabPanelItem.
38575      * @type Roo.Element
38576      */
38577     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38578     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38579     this.bodyEl.setStyle("display", "block");
38580     this.bodyEl.setStyle("zoom", "1");
38581     //this.hideAction();
38582
38583     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38584     /** @private */
38585     this.el = Roo.get(els.el);
38586     this.inner = Roo.get(els.inner, true);
38587      this.textEl = Roo.bootstrap.version == 4 ?
38588         this.el : Roo.get(this.el.dom.firstChild, true);
38589
38590     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38591     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38592
38593     
38594 //    this.el.on("mousedown", this.onTabMouseDown, this);
38595     this.el.on("click", this.onTabClick, this);
38596     /** @private */
38597     if(config.closable){
38598         var c = Roo.get(els.close, true);
38599         c.dom.title = this.closeText;
38600         c.addClassOnOver("close-over");
38601         c.on("click", this.closeClick, this);
38602      }
38603
38604     this.addEvents({
38605          /**
38606          * @event activate
38607          * Fires when this tab becomes the active tab.
38608          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38609          * @param {Roo.TabPanelItem} this
38610          */
38611         "activate": true,
38612         /**
38613          * @event beforeclose
38614          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38615          * @param {Roo.TabPanelItem} this
38616          * @param {Object} e Set cancel to true on this object to cancel the close.
38617          */
38618         "beforeclose": true,
38619         /**
38620          * @event close
38621          * Fires when this tab is closed.
38622          * @param {Roo.TabPanelItem} this
38623          */
38624          "close": true,
38625         /**
38626          * @event deactivate
38627          * Fires when this tab is no longer the active tab.
38628          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38629          * @param {Roo.TabPanelItem} this
38630          */
38631          "deactivate" : true
38632     });
38633     this.hidden = false;
38634
38635     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38636 };
38637
38638 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38639            {
38640     purgeListeners : function(){
38641        Roo.util.Observable.prototype.purgeListeners.call(this);
38642        this.el.removeAllListeners();
38643     },
38644     /**
38645      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38646      */
38647     show : function(){
38648         this.status_node.addClass("active");
38649         this.showAction();
38650         if(Roo.isOpera){
38651             this.tabPanel.stripWrap.repaint();
38652         }
38653         this.fireEvent("activate", this.tabPanel, this);
38654     },
38655
38656     /**
38657      * Returns true if this tab is the active tab.
38658      * @return {Boolean}
38659      */
38660     isActive : function(){
38661         return this.tabPanel.getActiveTab() == this;
38662     },
38663
38664     /**
38665      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38666      */
38667     hide : function(){
38668         this.status_node.removeClass("active");
38669         this.hideAction();
38670         this.fireEvent("deactivate", this.tabPanel, this);
38671     },
38672
38673     hideAction : function(){
38674         this.bodyEl.hide();
38675         this.bodyEl.setStyle("position", "absolute");
38676         this.bodyEl.setLeft("-20000px");
38677         this.bodyEl.setTop("-20000px");
38678     },
38679
38680     showAction : function(){
38681         this.bodyEl.setStyle("position", "relative");
38682         this.bodyEl.setTop("");
38683         this.bodyEl.setLeft("");
38684         this.bodyEl.show();
38685     },
38686
38687     /**
38688      * Set the tooltip for the tab.
38689      * @param {String} tooltip The tab's tooltip
38690      */
38691     setTooltip : function(text){
38692         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38693             this.textEl.dom.qtip = text;
38694             this.textEl.dom.removeAttribute('title');
38695         }else{
38696             this.textEl.dom.title = text;
38697         }
38698     },
38699
38700     onTabClick : function(e){
38701         e.preventDefault();
38702         this.tabPanel.activate(this.id);
38703     },
38704
38705     onTabMouseDown : function(e){
38706         e.preventDefault();
38707         this.tabPanel.activate(this.id);
38708     },
38709 /*
38710     getWidth : function(){
38711         return this.inner.getWidth();
38712     },
38713
38714     setWidth : function(width){
38715         var iwidth = width - this.linode.getPadding("lr");
38716         this.inner.setWidth(iwidth);
38717         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38718         this.linode.setWidth(width);
38719     },
38720 */
38721     /**
38722      * Show or hide the tab
38723      * @param {Boolean} hidden True to hide or false to show.
38724      */
38725     setHidden : function(hidden){
38726         this.hidden = hidden;
38727         this.linode.setStyle("display", hidden ? "none" : "");
38728     },
38729
38730     /**
38731      * Returns true if this tab is "hidden"
38732      * @return {Boolean}
38733      */
38734     isHidden : function(){
38735         return this.hidden;
38736     },
38737
38738     /**
38739      * Returns the text for this tab
38740      * @return {String}
38741      */
38742     getText : function(){
38743         return this.text;
38744     },
38745     /*
38746     autoSize : function(){
38747         //this.el.beginMeasure();
38748         this.textEl.setWidth(1);
38749         /*
38750          *  #2804 [new] Tabs in Roojs
38751          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38752          */
38753         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38754         //this.el.endMeasure();
38755     //},
38756
38757     /**
38758      * Sets the text for the tab (Note: this also sets the tooltip text)
38759      * @param {String} text The tab's text and tooltip
38760      */
38761     setText : function(text){
38762         this.text = text;
38763         this.textEl.update(text);
38764         this.setTooltip(text);
38765         //if(!this.tabPanel.resizeTabs){
38766         //    this.autoSize();
38767         //}
38768     },
38769     /**
38770      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38771      */
38772     activate : function(){
38773         this.tabPanel.activate(this.id);
38774     },
38775
38776     /**
38777      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38778      */
38779     disable : function(){
38780         if(this.tabPanel.active != this){
38781             this.disabled = true;
38782             this.status_node.addClass("disabled");
38783         }
38784     },
38785
38786     /**
38787      * Enables this TabPanelItem if it was previously disabled.
38788      */
38789     enable : function(){
38790         this.disabled = false;
38791         this.status_node.removeClass("disabled");
38792     },
38793
38794     /**
38795      * Sets the content for this TabPanelItem.
38796      * @param {String} content The content
38797      * @param {Boolean} loadScripts true to look for and load scripts
38798      */
38799     setContent : function(content, loadScripts){
38800         this.bodyEl.update(content, loadScripts);
38801     },
38802
38803     /**
38804      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38805      * @return {Roo.UpdateManager} The UpdateManager
38806      */
38807     getUpdateManager : function(){
38808         return this.bodyEl.getUpdateManager();
38809     },
38810
38811     /**
38812      * Set a URL to be used to load the content for this TabPanelItem.
38813      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38814      * @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)
38815      * @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)
38816      * @return {Roo.UpdateManager} The UpdateManager
38817      */
38818     setUrl : function(url, params, loadOnce){
38819         if(this.refreshDelegate){
38820             this.un('activate', this.refreshDelegate);
38821         }
38822         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38823         this.on("activate", this.refreshDelegate);
38824         return this.bodyEl.getUpdateManager();
38825     },
38826
38827     /** @private */
38828     _handleRefresh : function(url, params, loadOnce){
38829         if(!loadOnce || !this.loaded){
38830             var updater = this.bodyEl.getUpdateManager();
38831             updater.update(url, params, this._setLoaded.createDelegate(this));
38832         }
38833     },
38834
38835     /**
38836      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38837      *   Will fail silently if the setUrl method has not been called.
38838      *   This does not activate the panel, just updates its content.
38839      */
38840     refresh : function(){
38841         if(this.refreshDelegate){
38842            this.loaded = false;
38843            this.refreshDelegate();
38844         }
38845     },
38846
38847     /** @private */
38848     _setLoaded : function(){
38849         this.loaded = true;
38850     },
38851
38852     /** @private */
38853     closeClick : function(e){
38854         var o = {};
38855         e.stopEvent();
38856         this.fireEvent("beforeclose", this, o);
38857         if(o.cancel !== true){
38858             this.tabPanel.removeTab(this.id);
38859         }
38860     },
38861     /**
38862      * The text displayed in the tooltip for the close icon.
38863      * @type String
38864      */
38865     closeText : "Close this tab"
38866 });
38867 /**
38868 *    This script refer to:
38869 *    Title: International Telephone Input
38870 *    Author: Jack O'Connor
38871 *    Code version:  v12.1.12
38872 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38873 **/
38874
38875 Roo.bootstrap.PhoneInputData = function() {
38876     var d = [
38877       [
38878         "Afghanistan (‫افغانستان‬‎)",
38879         "af",
38880         "93"
38881       ],
38882       [
38883         "Albania (Shqipëri)",
38884         "al",
38885         "355"
38886       ],
38887       [
38888         "Algeria (‫الجزائر‬‎)",
38889         "dz",
38890         "213"
38891       ],
38892       [
38893         "American Samoa",
38894         "as",
38895         "1684"
38896       ],
38897       [
38898         "Andorra",
38899         "ad",
38900         "376"
38901       ],
38902       [
38903         "Angola",
38904         "ao",
38905         "244"
38906       ],
38907       [
38908         "Anguilla",
38909         "ai",
38910         "1264"
38911       ],
38912       [
38913         "Antigua and Barbuda",
38914         "ag",
38915         "1268"
38916       ],
38917       [
38918         "Argentina",
38919         "ar",
38920         "54"
38921       ],
38922       [
38923         "Armenia (Հայաստան)",
38924         "am",
38925         "374"
38926       ],
38927       [
38928         "Aruba",
38929         "aw",
38930         "297"
38931       ],
38932       [
38933         "Australia",
38934         "au",
38935         "61",
38936         0
38937       ],
38938       [
38939         "Austria (Österreich)",
38940         "at",
38941         "43"
38942       ],
38943       [
38944         "Azerbaijan (Azərbaycan)",
38945         "az",
38946         "994"
38947       ],
38948       [
38949         "Bahamas",
38950         "bs",
38951         "1242"
38952       ],
38953       [
38954         "Bahrain (‫البحرين‬‎)",
38955         "bh",
38956         "973"
38957       ],
38958       [
38959         "Bangladesh (বাংলাদেশ)",
38960         "bd",
38961         "880"
38962       ],
38963       [
38964         "Barbados",
38965         "bb",
38966         "1246"
38967       ],
38968       [
38969         "Belarus (Беларусь)",
38970         "by",
38971         "375"
38972       ],
38973       [
38974         "Belgium (België)",
38975         "be",
38976         "32"
38977       ],
38978       [
38979         "Belize",
38980         "bz",
38981         "501"
38982       ],
38983       [
38984         "Benin (Bénin)",
38985         "bj",
38986         "229"
38987       ],
38988       [
38989         "Bermuda",
38990         "bm",
38991         "1441"
38992       ],
38993       [
38994         "Bhutan (འབྲུག)",
38995         "bt",
38996         "975"
38997       ],
38998       [
38999         "Bolivia",
39000         "bo",
39001         "591"
39002       ],
39003       [
39004         "Bosnia and Herzegovina (Босна и Херцеговина)",
39005         "ba",
39006         "387"
39007       ],
39008       [
39009         "Botswana",
39010         "bw",
39011         "267"
39012       ],
39013       [
39014         "Brazil (Brasil)",
39015         "br",
39016         "55"
39017       ],
39018       [
39019         "British Indian Ocean Territory",
39020         "io",
39021         "246"
39022       ],
39023       [
39024         "British Virgin Islands",
39025         "vg",
39026         "1284"
39027       ],
39028       [
39029         "Brunei",
39030         "bn",
39031         "673"
39032       ],
39033       [
39034         "Bulgaria (България)",
39035         "bg",
39036         "359"
39037       ],
39038       [
39039         "Burkina Faso",
39040         "bf",
39041         "226"
39042       ],
39043       [
39044         "Burundi (Uburundi)",
39045         "bi",
39046         "257"
39047       ],
39048       [
39049         "Cambodia (កម្ពុជា)",
39050         "kh",
39051         "855"
39052       ],
39053       [
39054         "Cameroon (Cameroun)",
39055         "cm",
39056         "237"
39057       ],
39058       [
39059         "Canada",
39060         "ca",
39061         "1",
39062         1,
39063         ["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"]
39064       ],
39065       [
39066         "Cape Verde (Kabu Verdi)",
39067         "cv",
39068         "238"
39069       ],
39070       [
39071         "Caribbean Netherlands",
39072         "bq",
39073         "599",
39074         1
39075       ],
39076       [
39077         "Cayman Islands",
39078         "ky",
39079         "1345"
39080       ],
39081       [
39082         "Central African Republic (République centrafricaine)",
39083         "cf",
39084         "236"
39085       ],
39086       [
39087         "Chad (Tchad)",
39088         "td",
39089         "235"
39090       ],
39091       [
39092         "Chile",
39093         "cl",
39094         "56"
39095       ],
39096       [
39097         "China (中国)",
39098         "cn",
39099         "86"
39100       ],
39101       [
39102         "Christmas Island",
39103         "cx",
39104         "61",
39105         2
39106       ],
39107       [
39108         "Cocos (Keeling) Islands",
39109         "cc",
39110         "61",
39111         1
39112       ],
39113       [
39114         "Colombia",
39115         "co",
39116         "57"
39117       ],
39118       [
39119         "Comoros (‫جزر القمر‬‎)",
39120         "km",
39121         "269"
39122       ],
39123       [
39124         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39125         "cd",
39126         "243"
39127       ],
39128       [
39129         "Congo (Republic) (Congo-Brazzaville)",
39130         "cg",
39131         "242"
39132       ],
39133       [
39134         "Cook Islands",
39135         "ck",
39136         "682"
39137       ],
39138       [
39139         "Costa Rica",
39140         "cr",
39141         "506"
39142       ],
39143       [
39144         "Côte d’Ivoire",
39145         "ci",
39146         "225"
39147       ],
39148       [
39149         "Croatia (Hrvatska)",
39150         "hr",
39151         "385"
39152       ],
39153       [
39154         "Cuba",
39155         "cu",
39156         "53"
39157       ],
39158       [
39159         "Curaçao",
39160         "cw",
39161         "599",
39162         0
39163       ],
39164       [
39165         "Cyprus (Κύπρος)",
39166         "cy",
39167         "357"
39168       ],
39169       [
39170         "Czech Republic (Česká republika)",
39171         "cz",
39172         "420"
39173       ],
39174       [
39175         "Denmark (Danmark)",
39176         "dk",
39177         "45"
39178       ],
39179       [
39180         "Djibouti",
39181         "dj",
39182         "253"
39183       ],
39184       [
39185         "Dominica",
39186         "dm",
39187         "1767"
39188       ],
39189       [
39190         "Dominican Republic (República Dominicana)",
39191         "do",
39192         "1",
39193         2,
39194         ["809", "829", "849"]
39195       ],
39196       [
39197         "Ecuador",
39198         "ec",
39199         "593"
39200       ],
39201       [
39202         "Egypt (‫مصر‬‎)",
39203         "eg",
39204         "20"
39205       ],
39206       [
39207         "El Salvador",
39208         "sv",
39209         "503"
39210       ],
39211       [
39212         "Equatorial Guinea (Guinea Ecuatorial)",
39213         "gq",
39214         "240"
39215       ],
39216       [
39217         "Eritrea",
39218         "er",
39219         "291"
39220       ],
39221       [
39222         "Estonia (Eesti)",
39223         "ee",
39224         "372"
39225       ],
39226       [
39227         "Ethiopia",
39228         "et",
39229         "251"
39230       ],
39231       [
39232         "Falkland Islands (Islas Malvinas)",
39233         "fk",
39234         "500"
39235       ],
39236       [
39237         "Faroe Islands (Føroyar)",
39238         "fo",
39239         "298"
39240       ],
39241       [
39242         "Fiji",
39243         "fj",
39244         "679"
39245       ],
39246       [
39247         "Finland (Suomi)",
39248         "fi",
39249         "358",
39250         0
39251       ],
39252       [
39253         "France",
39254         "fr",
39255         "33"
39256       ],
39257       [
39258         "French Guiana (Guyane française)",
39259         "gf",
39260         "594"
39261       ],
39262       [
39263         "French Polynesia (Polynésie française)",
39264         "pf",
39265         "689"
39266       ],
39267       [
39268         "Gabon",
39269         "ga",
39270         "241"
39271       ],
39272       [
39273         "Gambia",
39274         "gm",
39275         "220"
39276       ],
39277       [
39278         "Georgia (საქართველო)",
39279         "ge",
39280         "995"
39281       ],
39282       [
39283         "Germany (Deutschland)",
39284         "de",
39285         "49"
39286       ],
39287       [
39288         "Ghana (Gaana)",
39289         "gh",
39290         "233"
39291       ],
39292       [
39293         "Gibraltar",
39294         "gi",
39295         "350"
39296       ],
39297       [
39298         "Greece (Ελλάδα)",
39299         "gr",
39300         "30"
39301       ],
39302       [
39303         "Greenland (Kalaallit Nunaat)",
39304         "gl",
39305         "299"
39306       ],
39307       [
39308         "Grenada",
39309         "gd",
39310         "1473"
39311       ],
39312       [
39313         "Guadeloupe",
39314         "gp",
39315         "590",
39316         0
39317       ],
39318       [
39319         "Guam",
39320         "gu",
39321         "1671"
39322       ],
39323       [
39324         "Guatemala",
39325         "gt",
39326         "502"
39327       ],
39328       [
39329         "Guernsey",
39330         "gg",
39331         "44",
39332         1
39333       ],
39334       [
39335         "Guinea (Guinée)",
39336         "gn",
39337         "224"
39338       ],
39339       [
39340         "Guinea-Bissau (Guiné Bissau)",
39341         "gw",
39342         "245"
39343       ],
39344       [
39345         "Guyana",
39346         "gy",
39347         "592"
39348       ],
39349       [
39350         "Haiti",
39351         "ht",
39352         "509"
39353       ],
39354       [
39355         "Honduras",
39356         "hn",
39357         "504"
39358       ],
39359       [
39360         "Hong Kong (香港)",
39361         "hk",
39362         "852"
39363       ],
39364       [
39365         "Hungary (Magyarország)",
39366         "hu",
39367         "36"
39368       ],
39369       [
39370         "Iceland (Ísland)",
39371         "is",
39372         "354"
39373       ],
39374       [
39375         "India (भारत)",
39376         "in",
39377         "91"
39378       ],
39379       [
39380         "Indonesia",
39381         "id",
39382         "62"
39383       ],
39384       [
39385         "Iran (‫ایران‬‎)",
39386         "ir",
39387         "98"
39388       ],
39389       [
39390         "Iraq (‫العراق‬‎)",
39391         "iq",
39392         "964"
39393       ],
39394       [
39395         "Ireland",
39396         "ie",
39397         "353"
39398       ],
39399       [
39400         "Isle of Man",
39401         "im",
39402         "44",
39403         2
39404       ],
39405       [
39406         "Israel (‫ישראל‬‎)",
39407         "il",
39408         "972"
39409       ],
39410       [
39411         "Italy (Italia)",
39412         "it",
39413         "39",
39414         0
39415       ],
39416       [
39417         "Jamaica",
39418         "jm",
39419         "1876"
39420       ],
39421       [
39422         "Japan (日本)",
39423         "jp",
39424         "81"
39425       ],
39426       [
39427         "Jersey",
39428         "je",
39429         "44",
39430         3
39431       ],
39432       [
39433         "Jordan (‫الأردن‬‎)",
39434         "jo",
39435         "962"
39436       ],
39437       [
39438         "Kazakhstan (Казахстан)",
39439         "kz",
39440         "7",
39441         1
39442       ],
39443       [
39444         "Kenya",
39445         "ke",
39446         "254"
39447       ],
39448       [
39449         "Kiribati",
39450         "ki",
39451         "686"
39452       ],
39453       [
39454         "Kosovo",
39455         "xk",
39456         "383"
39457       ],
39458       [
39459         "Kuwait (‫الكويت‬‎)",
39460         "kw",
39461         "965"
39462       ],
39463       [
39464         "Kyrgyzstan (Кыргызстан)",
39465         "kg",
39466         "996"
39467       ],
39468       [
39469         "Laos (ລາວ)",
39470         "la",
39471         "856"
39472       ],
39473       [
39474         "Latvia (Latvija)",
39475         "lv",
39476         "371"
39477       ],
39478       [
39479         "Lebanon (‫لبنان‬‎)",
39480         "lb",
39481         "961"
39482       ],
39483       [
39484         "Lesotho",
39485         "ls",
39486         "266"
39487       ],
39488       [
39489         "Liberia",
39490         "lr",
39491         "231"
39492       ],
39493       [
39494         "Libya (‫ليبيا‬‎)",
39495         "ly",
39496         "218"
39497       ],
39498       [
39499         "Liechtenstein",
39500         "li",
39501         "423"
39502       ],
39503       [
39504         "Lithuania (Lietuva)",
39505         "lt",
39506         "370"
39507       ],
39508       [
39509         "Luxembourg",
39510         "lu",
39511         "352"
39512       ],
39513       [
39514         "Macau (澳門)",
39515         "mo",
39516         "853"
39517       ],
39518       [
39519         "Macedonia (FYROM) (Македонија)",
39520         "mk",
39521         "389"
39522       ],
39523       [
39524         "Madagascar (Madagasikara)",
39525         "mg",
39526         "261"
39527       ],
39528       [
39529         "Malawi",
39530         "mw",
39531         "265"
39532       ],
39533       [
39534         "Malaysia",
39535         "my",
39536         "60"
39537       ],
39538       [
39539         "Maldives",
39540         "mv",
39541         "960"
39542       ],
39543       [
39544         "Mali",
39545         "ml",
39546         "223"
39547       ],
39548       [
39549         "Malta",
39550         "mt",
39551         "356"
39552       ],
39553       [
39554         "Marshall Islands",
39555         "mh",
39556         "692"
39557       ],
39558       [
39559         "Martinique",
39560         "mq",
39561         "596"
39562       ],
39563       [
39564         "Mauritania (‫موريتانيا‬‎)",
39565         "mr",
39566         "222"
39567       ],
39568       [
39569         "Mauritius (Moris)",
39570         "mu",
39571         "230"
39572       ],
39573       [
39574         "Mayotte",
39575         "yt",
39576         "262",
39577         1
39578       ],
39579       [
39580         "Mexico (México)",
39581         "mx",
39582         "52"
39583       ],
39584       [
39585         "Micronesia",
39586         "fm",
39587         "691"
39588       ],
39589       [
39590         "Moldova (Republica Moldova)",
39591         "md",
39592         "373"
39593       ],
39594       [
39595         "Monaco",
39596         "mc",
39597         "377"
39598       ],
39599       [
39600         "Mongolia (Монгол)",
39601         "mn",
39602         "976"
39603       ],
39604       [
39605         "Montenegro (Crna Gora)",
39606         "me",
39607         "382"
39608       ],
39609       [
39610         "Montserrat",
39611         "ms",
39612         "1664"
39613       ],
39614       [
39615         "Morocco (‫المغرب‬‎)",
39616         "ma",
39617         "212",
39618         0
39619       ],
39620       [
39621         "Mozambique (Moçambique)",
39622         "mz",
39623         "258"
39624       ],
39625       [
39626         "Myanmar (Burma) (မြန်မာ)",
39627         "mm",
39628         "95"
39629       ],
39630       [
39631         "Namibia (Namibië)",
39632         "na",
39633         "264"
39634       ],
39635       [
39636         "Nauru",
39637         "nr",
39638         "674"
39639       ],
39640       [
39641         "Nepal (नेपाल)",
39642         "np",
39643         "977"
39644       ],
39645       [
39646         "Netherlands (Nederland)",
39647         "nl",
39648         "31"
39649       ],
39650       [
39651         "New Caledonia (Nouvelle-Calédonie)",
39652         "nc",
39653         "687"
39654       ],
39655       [
39656         "New Zealand",
39657         "nz",
39658         "64"
39659       ],
39660       [
39661         "Nicaragua",
39662         "ni",
39663         "505"
39664       ],
39665       [
39666         "Niger (Nijar)",
39667         "ne",
39668         "227"
39669       ],
39670       [
39671         "Nigeria",
39672         "ng",
39673         "234"
39674       ],
39675       [
39676         "Niue",
39677         "nu",
39678         "683"
39679       ],
39680       [
39681         "Norfolk Island",
39682         "nf",
39683         "672"
39684       ],
39685       [
39686         "North Korea (조선 민주주의 인민 공화국)",
39687         "kp",
39688         "850"
39689       ],
39690       [
39691         "Northern Mariana Islands",
39692         "mp",
39693         "1670"
39694       ],
39695       [
39696         "Norway (Norge)",
39697         "no",
39698         "47",
39699         0
39700       ],
39701       [
39702         "Oman (‫عُمان‬‎)",
39703         "om",
39704         "968"
39705       ],
39706       [
39707         "Pakistan (‫پاکستان‬‎)",
39708         "pk",
39709         "92"
39710       ],
39711       [
39712         "Palau",
39713         "pw",
39714         "680"
39715       ],
39716       [
39717         "Palestine (‫فلسطين‬‎)",
39718         "ps",
39719         "970"
39720       ],
39721       [
39722         "Panama (Panamá)",
39723         "pa",
39724         "507"
39725       ],
39726       [
39727         "Papua New Guinea",
39728         "pg",
39729         "675"
39730       ],
39731       [
39732         "Paraguay",
39733         "py",
39734         "595"
39735       ],
39736       [
39737         "Peru (Perú)",
39738         "pe",
39739         "51"
39740       ],
39741       [
39742         "Philippines",
39743         "ph",
39744         "63"
39745       ],
39746       [
39747         "Poland (Polska)",
39748         "pl",
39749         "48"
39750       ],
39751       [
39752         "Portugal",
39753         "pt",
39754         "351"
39755       ],
39756       [
39757         "Puerto Rico",
39758         "pr",
39759         "1",
39760         3,
39761         ["787", "939"]
39762       ],
39763       [
39764         "Qatar (‫قطر‬‎)",
39765         "qa",
39766         "974"
39767       ],
39768       [
39769         "Réunion (La Réunion)",
39770         "re",
39771         "262",
39772         0
39773       ],
39774       [
39775         "Romania (România)",
39776         "ro",
39777         "40"
39778       ],
39779       [
39780         "Russia (Россия)",
39781         "ru",
39782         "7",
39783         0
39784       ],
39785       [
39786         "Rwanda",
39787         "rw",
39788         "250"
39789       ],
39790       [
39791         "Saint Barthélemy",
39792         "bl",
39793         "590",
39794         1
39795       ],
39796       [
39797         "Saint Helena",
39798         "sh",
39799         "290"
39800       ],
39801       [
39802         "Saint Kitts and Nevis",
39803         "kn",
39804         "1869"
39805       ],
39806       [
39807         "Saint Lucia",
39808         "lc",
39809         "1758"
39810       ],
39811       [
39812         "Saint Martin (Saint-Martin (partie française))",
39813         "mf",
39814         "590",
39815         2
39816       ],
39817       [
39818         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39819         "pm",
39820         "508"
39821       ],
39822       [
39823         "Saint Vincent and the Grenadines",
39824         "vc",
39825         "1784"
39826       ],
39827       [
39828         "Samoa",
39829         "ws",
39830         "685"
39831       ],
39832       [
39833         "San Marino",
39834         "sm",
39835         "378"
39836       ],
39837       [
39838         "São Tomé and Príncipe (São Tomé e Príncipe)",
39839         "st",
39840         "239"
39841       ],
39842       [
39843         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39844         "sa",
39845         "966"
39846       ],
39847       [
39848         "Senegal (Sénégal)",
39849         "sn",
39850         "221"
39851       ],
39852       [
39853         "Serbia (Србија)",
39854         "rs",
39855         "381"
39856       ],
39857       [
39858         "Seychelles",
39859         "sc",
39860         "248"
39861       ],
39862       [
39863         "Sierra Leone",
39864         "sl",
39865         "232"
39866       ],
39867       [
39868         "Singapore",
39869         "sg",
39870         "65"
39871       ],
39872       [
39873         "Sint Maarten",
39874         "sx",
39875         "1721"
39876       ],
39877       [
39878         "Slovakia (Slovensko)",
39879         "sk",
39880         "421"
39881       ],
39882       [
39883         "Slovenia (Slovenija)",
39884         "si",
39885         "386"
39886       ],
39887       [
39888         "Solomon Islands",
39889         "sb",
39890         "677"
39891       ],
39892       [
39893         "Somalia (Soomaaliya)",
39894         "so",
39895         "252"
39896       ],
39897       [
39898         "South Africa",
39899         "za",
39900         "27"
39901       ],
39902       [
39903         "South Korea (대한민국)",
39904         "kr",
39905         "82"
39906       ],
39907       [
39908         "South Sudan (‫جنوب السودان‬‎)",
39909         "ss",
39910         "211"
39911       ],
39912       [
39913         "Spain (España)",
39914         "es",
39915         "34"
39916       ],
39917       [
39918         "Sri Lanka (ශ්‍රී ලංකාව)",
39919         "lk",
39920         "94"
39921       ],
39922       [
39923         "Sudan (‫السودان‬‎)",
39924         "sd",
39925         "249"
39926       ],
39927       [
39928         "Suriname",
39929         "sr",
39930         "597"
39931       ],
39932       [
39933         "Svalbard and Jan Mayen",
39934         "sj",
39935         "47",
39936         1
39937       ],
39938       [
39939         "Swaziland",
39940         "sz",
39941         "268"
39942       ],
39943       [
39944         "Sweden (Sverige)",
39945         "se",
39946         "46"
39947       ],
39948       [
39949         "Switzerland (Schweiz)",
39950         "ch",
39951         "41"
39952       ],
39953       [
39954         "Syria (‫سوريا‬‎)",
39955         "sy",
39956         "963"
39957       ],
39958       [
39959         "Taiwan (台灣)",
39960         "tw",
39961         "886"
39962       ],
39963       [
39964         "Tajikistan",
39965         "tj",
39966         "992"
39967       ],
39968       [
39969         "Tanzania",
39970         "tz",
39971         "255"
39972       ],
39973       [
39974         "Thailand (ไทย)",
39975         "th",
39976         "66"
39977       ],
39978       [
39979         "Timor-Leste",
39980         "tl",
39981         "670"
39982       ],
39983       [
39984         "Togo",
39985         "tg",
39986         "228"
39987       ],
39988       [
39989         "Tokelau",
39990         "tk",
39991         "690"
39992       ],
39993       [
39994         "Tonga",
39995         "to",
39996         "676"
39997       ],
39998       [
39999         "Trinidad and Tobago",
40000         "tt",
40001         "1868"
40002       ],
40003       [
40004         "Tunisia (‫تونس‬‎)",
40005         "tn",
40006         "216"
40007       ],
40008       [
40009         "Turkey (Türkiye)",
40010         "tr",
40011         "90"
40012       ],
40013       [
40014         "Turkmenistan",
40015         "tm",
40016         "993"
40017       ],
40018       [
40019         "Turks and Caicos Islands",
40020         "tc",
40021         "1649"
40022       ],
40023       [
40024         "Tuvalu",
40025         "tv",
40026         "688"
40027       ],
40028       [
40029         "U.S. Virgin Islands",
40030         "vi",
40031         "1340"
40032       ],
40033       [
40034         "Uganda",
40035         "ug",
40036         "256"
40037       ],
40038       [
40039         "Ukraine (Україна)",
40040         "ua",
40041         "380"
40042       ],
40043       [
40044         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
40045         "ae",
40046         "971"
40047       ],
40048       [
40049         "United Kingdom",
40050         "gb",
40051         "44",
40052         0
40053       ],
40054       [
40055         "United States",
40056         "us",
40057         "1",
40058         0
40059       ],
40060       [
40061         "Uruguay",
40062         "uy",
40063         "598"
40064       ],
40065       [
40066         "Uzbekistan (Oʻzbekiston)",
40067         "uz",
40068         "998"
40069       ],
40070       [
40071         "Vanuatu",
40072         "vu",
40073         "678"
40074       ],
40075       [
40076         "Vatican City (Città del Vaticano)",
40077         "va",
40078         "39",
40079         1
40080       ],
40081       [
40082         "Venezuela",
40083         "ve",
40084         "58"
40085       ],
40086       [
40087         "Vietnam (Việt Nam)",
40088         "vn",
40089         "84"
40090       ],
40091       [
40092         "Wallis and Futuna (Wallis-et-Futuna)",
40093         "wf",
40094         "681"
40095       ],
40096       [
40097         "Western Sahara (‫الصحراء الغربية‬‎)",
40098         "eh",
40099         "212",
40100         1
40101       ],
40102       [
40103         "Yemen (‫اليمن‬‎)",
40104         "ye",
40105         "967"
40106       ],
40107       [
40108         "Zambia",
40109         "zm",
40110         "260"
40111       ],
40112       [
40113         "Zimbabwe",
40114         "zw",
40115         "263"
40116       ],
40117       [
40118         "Åland Islands",
40119         "ax",
40120         "358",
40121         1
40122       ]
40123   ];
40124   
40125   return d;
40126 }/**
40127 *    This script refer to:
40128 *    Title: International Telephone Input
40129 *    Author: Jack O'Connor
40130 *    Code version:  v12.1.12
40131 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40132 **/
40133
40134 /**
40135  * @class Roo.bootstrap.PhoneInput
40136  * @extends Roo.bootstrap.TriggerField
40137  * An input with International dial-code selection
40138  
40139  * @cfg {String} defaultDialCode default '+852'
40140  * @cfg {Array} preferedCountries default []
40141   
40142  * @constructor
40143  * Create a new PhoneInput.
40144  * @param {Object} config Configuration options
40145  */
40146
40147 Roo.bootstrap.PhoneInput = function(config) {
40148     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40149 };
40150
40151 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40152         
40153         listWidth: undefined,
40154         
40155         selectedClass: 'active',
40156         
40157         invalidClass : "has-warning",
40158         
40159         validClass: 'has-success',
40160         
40161         allowed: '0123456789',
40162         
40163         max_length: 15,
40164         
40165         /**
40166          * @cfg {String} defaultDialCode The default dial code when initializing the input
40167          */
40168         defaultDialCode: '+852',
40169         
40170         /**
40171          * @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
40172          */
40173         preferedCountries: false,
40174         
40175         getAutoCreate : function()
40176         {
40177             var data = Roo.bootstrap.PhoneInputData();
40178             var align = this.labelAlign || this.parentLabelAlign();
40179             var id = Roo.id();
40180             
40181             this.allCountries = [];
40182             this.dialCodeMapping = [];
40183             
40184             for (var i = 0; i < data.length; i++) {
40185               var c = data[i];
40186               this.allCountries[i] = {
40187                 name: c[0],
40188                 iso2: c[1],
40189                 dialCode: c[2],
40190                 priority: c[3] || 0,
40191                 areaCodes: c[4] || null
40192               };
40193               this.dialCodeMapping[c[2]] = {
40194                   name: c[0],
40195                   iso2: c[1],
40196                   priority: c[3] || 0,
40197                   areaCodes: c[4] || null
40198               };
40199             }
40200             
40201             var cfg = {
40202                 cls: 'form-group',
40203                 cn: []
40204             };
40205             
40206             var input =  {
40207                 tag: 'input',
40208                 id : id,
40209                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40210                 maxlength: this.max_length,
40211                 cls : 'form-control tel-input',
40212                 autocomplete: 'new-password'
40213             };
40214             
40215             var hiddenInput = {
40216                 tag: 'input',
40217                 type: 'hidden',
40218                 cls: 'hidden-tel-input'
40219             };
40220             
40221             if (this.name) {
40222                 hiddenInput.name = this.name;
40223             }
40224             
40225             if (this.disabled) {
40226                 input.disabled = true;
40227             }
40228             
40229             var flag_container = {
40230                 tag: 'div',
40231                 cls: 'flag-box',
40232                 cn: [
40233                     {
40234                         tag: 'div',
40235                         cls: 'flag'
40236                     },
40237                     {
40238                         tag: 'div',
40239                         cls: 'caret'
40240                     }
40241                 ]
40242             };
40243             
40244             var box = {
40245                 tag: 'div',
40246                 cls: this.hasFeedback ? 'has-feedback' : '',
40247                 cn: [
40248                     hiddenInput,
40249                     input,
40250                     {
40251                         tag: 'input',
40252                         cls: 'dial-code-holder',
40253                         disabled: true
40254                     }
40255                 ]
40256             };
40257             
40258             var container = {
40259                 cls: 'roo-select2-container input-group',
40260                 cn: [
40261                     flag_container,
40262                     box
40263                 ]
40264             };
40265             
40266             if (this.fieldLabel.length) {
40267                 var indicator = {
40268                     tag: 'i',
40269                     tooltip: 'This field is required'
40270                 };
40271                 
40272                 var label = {
40273                     tag: 'label',
40274                     'for':  id,
40275                     cls: 'control-label',
40276                     cn: []
40277                 };
40278                 
40279                 var label_text = {
40280                     tag: 'span',
40281                     html: this.fieldLabel
40282                 };
40283                 
40284                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40285                 label.cn = [
40286                     indicator,
40287                     label_text
40288                 ];
40289                 
40290                 if(this.indicatorpos == 'right') {
40291                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40292                     label.cn = [
40293                         label_text,
40294                         indicator
40295                     ];
40296                 }
40297                 
40298                 if(align == 'left') {
40299                     container = {
40300                         tag: 'div',
40301                         cn: [
40302                             container
40303                         ]
40304                     };
40305                     
40306                     if(this.labelWidth > 12){
40307                         label.style = "width: " + this.labelWidth + 'px';
40308                     }
40309                     if(this.labelWidth < 13 && this.labelmd == 0){
40310                         this.labelmd = this.labelWidth;
40311                     }
40312                     if(this.labellg > 0){
40313                         label.cls += ' col-lg-' + this.labellg;
40314                         input.cls += ' col-lg-' + (12 - this.labellg);
40315                     }
40316                     if(this.labelmd > 0){
40317                         label.cls += ' col-md-' + this.labelmd;
40318                         container.cls += ' col-md-' + (12 - this.labelmd);
40319                     }
40320                     if(this.labelsm > 0){
40321                         label.cls += ' col-sm-' + this.labelsm;
40322                         container.cls += ' col-sm-' + (12 - this.labelsm);
40323                     }
40324                     if(this.labelxs > 0){
40325                         label.cls += ' col-xs-' + this.labelxs;
40326                         container.cls += ' col-xs-' + (12 - this.labelxs);
40327                     }
40328                 }
40329             }
40330             
40331             cfg.cn = [
40332                 label,
40333                 container
40334             ];
40335             
40336             var settings = this;
40337             
40338             ['xs','sm','md','lg'].map(function(size){
40339                 if (settings[size]) {
40340                     cfg.cls += ' col-' + size + '-' + settings[size];
40341                 }
40342             });
40343             
40344             this.store = new Roo.data.Store({
40345                 proxy : new Roo.data.MemoryProxy({}),
40346                 reader : new Roo.data.JsonReader({
40347                     fields : [
40348                         {
40349                             'name' : 'name',
40350                             'type' : 'string'
40351                         },
40352                         {
40353                             'name' : 'iso2',
40354                             'type' : 'string'
40355                         },
40356                         {
40357                             'name' : 'dialCode',
40358                             'type' : 'string'
40359                         },
40360                         {
40361                             'name' : 'priority',
40362                             'type' : 'string'
40363                         },
40364                         {
40365                             'name' : 'areaCodes',
40366                             'type' : 'string'
40367                         }
40368                     ]
40369                 })
40370             });
40371             
40372             if(!this.preferedCountries) {
40373                 this.preferedCountries = [
40374                     'hk',
40375                     'gb',
40376                     'us'
40377                 ];
40378             }
40379             
40380             var p = this.preferedCountries.reverse();
40381             
40382             if(p) {
40383                 for (var i = 0; i < p.length; i++) {
40384                     for (var j = 0; j < this.allCountries.length; j++) {
40385                         if(this.allCountries[j].iso2 == p[i]) {
40386                             var t = this.allCountries[j];
40387                             this.allCountries.splice(j,1);
40388                             this.allCountries.unshift(t);
40389                         }
40390                     } 
40391                 }
40392             }
40393             
40394             this.store.proxy.data = {
40395                 success: true,
40396                 data: this.allCountries
40397             };
40398             
40399             return cfg;
40400         },
40401         
40402         initEvents : function()
40403         {
40404             this.createList();
40405             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40406             
40407             this.indicator = this.indicatorEl();
40408             this.flag = this.flagEl();
40409             this.dialCodeHolder = this.dialCodeHolderEl();
40410             
40411             this.trigger = this.el.select('div.flag-box',true).first();
40412             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40413             
40414             var _this = this;
40415             
40416             (function(){
40417                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40418                 _this.list.setWidth(lw);
40419             }).defer(100);
40420             
40421             this.list.on('mouseover', this.onViewOver, this);
40422             this.list.on('mousemove', this.onViewMove, this);
40423             this.inputEl().on("keyup", this.onKeyUp, this);
40424             this.inputEl().on("keypress", this.onKeyPress, this);
40425             
40426             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40427
40428             this.view = new Roo.View(this.list, this.tpl, {
40429                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40430             });
40431             
40432             this.view.on('click', this.onViewClick, this);
40433             this.setValue(this.defaultDialCode);
40434         },
40435         
40436         onTriggerClick : function(e)
40437         {
40438             Roo.log('trigger click');
40439             if(this.disabled){
40440                 return;
40441             }
40442             
40443             if(this.isExpanded()){
40444                 this.collapse();
40445                 this.hasFocus = false;
40446             }else {
40447                 this.store.load({});
40448                 this.hasFocus = true;
40449                 this.expand();
40450             }
40451         },
40452         
40453         isExpanded : function()
40454         {
40455             return this.list.isVisible();
40456         },
40457         
40458         collapse : function()
40459         {
40460             if(!this.isExpanded()){
40461                 return;
40462             }
40463             this.list.hide();
40464             Roo.get(document).un('mousedown', this.collapseIf, this);
40465             Roo.get(document).un('mousewheel', this.collapseIf, this);
40466             this.fireEvent('collapse', this);
40467             this.validate();
40468         },
40469         
40470         expand : function()
40471         {
40472             Roo.log('expand');
40473
40474             if(this.isExpanded() || !this.hasFocus){
40475                 return;
40476             }
40477             
40478             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40479             this.list.setWidth(lw);
40480             
40481             this.list.show();
40482             this.restrictHeight();
40483             
40484             Roo.get(document).on('mousedown', this.collapseIf, this);
40485             Roo.get(document).on('mousewheel', this.collapseIf, this);
40486             
40487             this.fireEvent('expand', this);
40488         },
40489         
40490         restrictHeight : function()
40491         {
40492             this.list.alignTo(this.inputEl(), this.listAlign);
40493             this.list.alignTo(this.inputEl(), this.listAlign);
40494         },
40495         
40496         onViewOver : function(e, t)
40497         {
40498             if(this.inKeyMode){
40499                 return;
40500             }
40501             var item = this.view.findItemFromChild(t);
40502             
40503             if(item){
40504                 var index = this.view.indexOf(item);
40505                 this.select(index, false);
40506             }
40507         },
40508
40509         // private
40510         onViewClick : function(view, doFocus, el, e)
40511         {
40512             var index = this.view.getSelectedIndexes()[0];
40513             
40514             var r = this.store.getAt(index);
40515             
40516             if(r){
40517                 this.onSelect(r, index);
40518             }
40519             if(doFocus !== false && !this.blockFocus){
40520                 this.inputEl().focus();
40521             }
40522         },
40523         
40524         onViewMove : function(e, t)
40525         {
40526             this.inKeyMode = false;
40527         },
40528         
40529         select : function(index, scrollIntoView)
40530         {
40531             this.selectedIndex = index;
40532             this.view.select(index);
40533             if(scrollIntoView !== false){
40534                 var el = this.view.getNode(index);
40535                 if(el){
40536                     this.list.scrollChildIntoView(el, false);
40537                 }
40538             }
40539         },
40540         
40541         createList : function()
40542         {
40543             this.list = Roo.get(document.body).createChild({
40544                 tag: 'ul',
40545                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40546                 style: 'display:none'
40547             });
40548             
40549             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40550         },
40551         
40552         collapseIf : function(e)
40553         {
40554             var in_combo  = e.within(this.el);
40555             var in_list =  e.within(this.list);
40556             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40557             
40558             if (in_combo || in_list || is_list) {
40559                 return;
40560             }
40561             this.collapse();
40562         },
40563         
40564         onSelect : function(record, index)
40565         {
40566             if(this.fireEvent('beforeselect', this, record, index) !== false){
40567                 
40568                 this.setFlagClass(record.data.iso2);
40569                 this.setDialCode(record.data.dialCode);
40570                 this.hasFocus = false;
40571                 this.collapse();
40572                 this.fireEvent('select', this, record, index);
40573             }
40574         },
40575         
40576         flagEl : function()
40577         {
40578             var flag = this.el.select('div.flag',true).first();
40579             if(!flag){
40580                 return false;
40581             }
40582             return flag;
40583         },
40584         
40585         dialCodeHolderEl : function()
40586         {
40587             var d = this.el.select('input.dial-code-holder',true).first();
40588             if(!d){
40589                 return false;
40590             }
40591             return d;
40592         },
40593         
40594         setDialCode : function(v)
40595         {
40596             this.dialCodeHolder.dom.value = '+'+v;
40597         },
40598         
40599         setFlagClass : function(n)
40600         {
40601             this.flag.dom.className = 'flag '+n;
40602         },
40603         
40604         getValue : function()
40605         {
40606             var v = this.inputEl().getValue();
40607             if(this.dialCodeHolder) {
40608                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40609             }
40610             return v;
40611         },
40612         
40613         setValue : function(v)
40614         {
40615             var d = this.getDialCode(v);
40616             
40617             //invalid dial code
40618             if(v.length == 0 || !d || d.length == 0) {
40619                 if(this.rendered){
40620                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40621                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40622                 }
40623                 return;
40624             }
40625             
40626             //valid dial code
40627             this.setFlagClass(this.dialCodeMapping[d].iso2);
40628             this.setDialCode(d);
40629             this.inputEl().dom.value = v.replace('+'+d,'');
40630             this.hiddenEl().dom.value = this.getValue();
40631             
40632             this.validate();
40633         },
40634         
40635         getDialCode : function(v)
40636         {
40637             v = v ||  '';
40638             
40639             if (v.length == 0) {
40640                 return this.dialCodeHolder.dom.value;
40641             }
40642             
40643             var dialCode = "";
40644             if (v.charAt(0) != "+") {
40645                 return false;
40646             }
40647             var numericChars = "";
40648             for (var i = 1; i < v.length; i++) {
40649               var c = v.charAt(i);
40650               if (!isNaN(c)) {
40651                 numericChars += c;
40652                 if (this.dialCodeMapping[numericChars]) {
40653                   dialCode = v.substr(1, i);
40654                 }
40655                 if (numericChars.length == 4) {
40656                   break;
40657                 }
40658               }
40659             }
40660             return dialCode;
40661         },
40662         
40663         reset : function()
40664         {
40665             this.setValue(this.defaultDialCode);
40666             this.validate();
40667         },
40668         
40669         hiddenEl : function()
40670         {
40671             return this.el.select('input.hidden-tel-input',true).first();
40672         },
40673         
40674         // after setting val
40675         onKeyUp : function(e){
40676             this.setValue(this.getValue());
40677         },
40678         
40679         onKeyPress : function(e){
40680             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40681                 e.stopEvent();
40682             }
40683         }
40684         
40685 });
40686 /**
40687  * @class Roo.bootstrap.MoneyField
40688  * @extends Roo.bootstrap.ComboBox
40689  * Bootstrap MoneyField class
40690  * 
40691  * @constructor
40692  * Create a new MoneyField.
40693  * @param {Object} config Configuration options
40694  */
40695
40696 Roo.bootstrap.MoneyField = function(config) {
40697     
40698     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40699     
40700 };
40701
40702 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40703     
40704     /**
40705      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40706      */
40707     allowDecimals : true,
40708     /**
40709      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40710      */
40711     decimalSeparator : ".",
40712     /**
40713      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40714      */
40715     decimalPrecision : 0,
40716     /**
40717      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40718      */
40719     allowNegative : true,
40720     /**
40721      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40722      */
40723     allowZero: true,
40724     /**
40725      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40726      */
40727     minValue : Number.NEGATIVE_INFINITY,
40728     /**
40729      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40730      */
40731     maxValue : Number.MAX_VALUE,
40732     /**
40733      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40734      */
40735     minText : "The minimum value for this field is {0}",
40736     /**
40737      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40738      */
40739     maxText : "The maximum value for this field is {0}",
40740     /**
40741      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40742      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40743      */
40744     nanText : "{0} is not a valid number",
40745     /**
40746      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40747      */
40748     castInt : true,
40749     /**
40750      * @cfg {String} defaults currency of the MoneyField
40751      * value should be in lkey
40752      */
40753     defaultCurrency : false,
40754     /**
40755      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40756      */
40757     thousandsDelimiter : false,
40758     /**
40759      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40760      */
40761     max_length: false,
40762     
40763     inputlg : 9,
40764     inputmd : 9,
40765     inputsm : 9,
40766     inputxs : 6,
40767     
40768     store : false,
40769     
40770     getAutoCreate : function()
40771     {
40772         var align = this.labelAlign || this.parentLabelAlign();
40773         
40774         var id = Roo.id();
40775
40776         var cfg = {
40777             cls: 'form-group',
40778             cn: []
40779         };
40780
40781         var input =  {
40782             tag: 'input',
40783             id : id,
40784             cls : 'form-control roo-money-amount-input',
40785             autocomplete: 'new-password'
40786         };
40787         
40788         var hiddenInput = {
40789             tag: 'input',
40790             type: 'hidden',
40791             id: Roo.id(),
40792             cls: 'hidden-number-input'
40793         };
40794         
40795         if(this.max_length) {
40796             input.maxlength = this.max_length; 
40797         }
40798         
40799         if (this.name) {
40800             hiddenInput.name = this.name;
40801         }
40802
40803         if (this.disabled) {
40804             input.disabled = true;
40805         }
40806
40807         var clg = 12 - this.inputlg;
40808         var cmd = 12 - this.inputmd;
40809         var csm = 12 - this.inputsm;
40810         var cxs = 12 - this.inputxs;
40811         
40812         var container = {
40813             tag : 'div',
40814             cls : 'row roo-money-field',
40815             cn : [
40816                 {
40817                     tag : 'div',
40818                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40819                     cn : [
40820                         {
40821                             tag : 'div',
40822                             cls: 'roo-select2-container input-group',
40823                             cn: [
40824                                 {
40825                                     tag : 'input',
40826                                     cls : 'form-control roo-money-currency-input',
40827                                     autocomplete: 'new-password',
40828                                     readOnly : 1,
40829                                     name : this.currencyName
40830                                 },
40831                                 {
40832                                     tag :'span',
40833                                     cls : 'input-group-addon',
40834                                     cn : [
40835                                         {
40836                                             tag: 'span',
40837                                             cls: 'caret'
40838                                         }
40839                                     ]
40840                                 }
40841                             ]
40842                         }
40843                     ]
40844                 },
40845                 {
40846                     tag : 'div',
40847                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40848                     cn : [
40849                         {
40850                             tag: 'div',
40851                             cls: this.hasFeedback ? 'has-feedback' : '',
40852                             cn: [
40853                                 input
40854                             ]
40855                         }
40856                     ]
40857                 }
40858             ]
40859             
40860         };
40861         
40862         if (this.fieldLabel.length) {
40863             var indicator = {
40864                 tag: 'i',
40865                 tooltip: 'This field is required'
40866             };
40867
40868             var label = {
40869                 tag: 'label',
40870                 'for':  id,
40871                 cls: 'control-label',
40872                 cn: []
40873             };
40874
40875             var label_text = {
40876                 tag: 'span',
40877                 html: this.fieldLabel
40878             };
40879
40880             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40881             label.cn = [
40882                 indicator,
40883                 label_text
40884             ];
40885
40886             if(this.indicatorpos == 'right') {
40887                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40888                 label.cn = [
40889                     label_text,
40890                     indicator
40891                 ];
40892             }
40893
40894             if(align == 'left') {
40895                 container = {
40896                     tag: 'div',
40897                     cn: [
40898                         container
40899                     ]
40900                 };
40901
40902                 if(this.labelWidth > 12){
40903                     label.style = "width: " + this.labelWidth + 'px';
40904                 }
40905                 if(this.labelWidth < 13 && this.labelmd == 0){
40906                     this.labelmd = this.labelWidth;
40907                 }
40908                 if(this.labellg > 0){
40909                     label.cls += ' col-lg-' + this.labellg;
40910                     input.cls += ' col-lg-' + (12 - this.labellg);
40911                 }
40912                 if(this.labelmd > 0){
40913                     label.cls += ' col-md-' + this.labelmd;
40914                     container.cls += ' col-md-' + (12 - this.labelmd);
40915                 }
40916                 if(this.labelsm > 0){
40917                     label.cls += ' col-sm-' + this.labelsm;
40918                     container.cls += ' col-sm-' + (12 - this.labelsm);
40919                 }
40920                 if(this.labelxs > 0){
40921                     label.cls += ' col-xs-' + this.labelxs;
40922                     container.cls += ' col-xs-' + (12 - this.labelxs);
40923                 }
40924             }
40925         }
40926
40927         cfg.cn = [
40928             label,
40929             container,
40930             hiddenInput
40931         ];
40932         
40933         var settings = this;
40934
40935         ['xs','sm','md','lg'].map(function(size){
40936             if (settings[size]) {
40937                 cfg.cls += ' col-' + size + '-' + settings[size];
40938             }
40939         });
40940         
40941         return cfg;
40942     },
40943     
40944     initEvents : function()
40945     {
40946         this.indicator = this.indicatorEl();
40947         
40948         this.initCurrencyEvent();
40949         
40950         this.initNumberEvent();
40951     },
40952     
40953     initCurrencyEvent : function()
40954     {
40955         if (!this.store) {
40956             throw "can not find store for combo";
40957         }
40958         
40959         this.store = Roo.factory(this.store, Roo.data);
40960         this.store.parent = this;
40961         
40962         this.createList();
40963         
40964         this.triggerEl = this.el.select('.input-group-addon', true).first();
40965         
40966         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40967         
40968         var _this = this;
40969         
40970         (function(){
40971             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40972             _this.list.setWidth(lw);
40973         }).defer(100);
40974         
40975         this.list.on('mouseover', this.onViewOver, this);
40976         this.list.on('mousemove', this.onViewMove, this);
40977         this.list.on('scroll', this.onViewScroll, this);
40978         
40979         if(!this.tpl){
40980             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40981         }
40982         
40983         this.view = new Roo.View(this.list, this.tpl, {
40984             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40985         });
40986         
40987         this.view.on('click', this.onViewClick, this);
40988         
40989         this.store.on('beforeload', this.onBeforeLoad, this);
40990         this.store.on('load', this.onLoad, this);
40991         this.store.on('loadexception', this.onLoadException, this);
40992         
40993         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40994             "up" : function(e){
40995                 this.inKeyMode = true;
40996                 this.selectPrev();
40997             },
40998
40999             "down" : function(e){
41000                 if(!this.isExpanded()){
41001                     this.onTriggerClick();
41002                 }else{
41003                     this.inKeyMode = true;
41004                     this.selectNext();
41005                 }
41006             },
41007
41008             "enter" : function(e){
41009                 this.collapse();
41010                 
41011                 if(this.fireEvent("specialkey", this, e)){
41012                     this.onViewClick(false);
41013                 }
41014                 
41015                 return true;
41016             },
41017
41018             "esc" : function(e){
41019                 this.collapse();
41020             },
41021
41022             "tab" : function(e){
41023                 this.collapse();
41024                 
41025                 if(this.fireEvent("specialkey", this, e)){
41026                     this.onViewClick(false);
41027                 }
41028                 
41029                 return true;
41030             },
41031
41032             scope : this,
41033
41034             doRelay : function(foo, bar, hname){
41035                 if(hname == 'down' || this.scope.isExpanded()){
41036                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41037                 }
41038                 return true;
41039             },
41040
41041             forceKeyDown: true
41042         });
41043         
41044         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41045         
41046     },
41047     
41048     initNumberEvent : function(e)
41049     {
41050         this.inputEl().on("keydown" , this.fireKey,  this);
41051         this.inputEl().on("focus", this.onFocus,  this);
41052         this.inputEl().on("blur", this.onBlur,  this);
41053         
41054         this.inputEl().relayEvent('keyup', this);
41055         
41056         if(this.indicator){
41057             this.indicator.addClass('invisible');
41058         }
41059  
41060         this.originalValue = this.getValue();
41061         
41062         if(this.validationEvent == 'keyup'){
41063             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41064             this.inputEl().on('keyup', this.filterValidation, this);
41065         }
41066         else if(this.validationEvent !== false){
41067             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41068         }
41069         
41070         if(this.selectOnFocus){
41071             this.on("focus", this.preFocus, this);
41072             
41073         }
41074         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41075             this.inputEl().on("keypress", this.filterKeys, this);
41076         } else {
41077             this.inputEl().relayEvent('keypress', this);
41078         }
41079         
41080         var allowed = "0123456789";
41081         
41082         if(this.allowDecimals){
41083             allowed += this.decimalSeparator;
41084         }
41085         
41086         if(this.allowNegative){
41087             allowed += "-";
41088         }
41089         
41090         if(this.thousandsDelimiter) {
41091             allowed += ",";
41092         }
41093         
41094         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41095         
41096         var keyPress = function(e){
41097             
41098             var k = e.getKey();
41099             
41100             var c = e.getCharCode();
41101             
41102             if(
41103                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41104                     allowed.indexOf(String.fromCharCode(c)) === -1
41105             ){
41106                 e.stopEvent();
41107                 return;
41108             }
41109             
41110             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41111                 return;
41112             }
41113             
41114             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41115                 e.stopEvent();
41116             }
41117         };
41118         
41119         this.inputEl().on("keypress", keyPress, this);
41120         
41121     },
41122     
41123     onTriggerClick : function(e)
41124     {   
41125         if(this.disabled){
41126             return;
41127         }
41128         
41129         this.page = 0;
41130         this.loadNext = false;
41131         
41132         if(this.isExpanded()){
41133             this.collapse();
41134             return;
41135         }
41136         
41137         this.hasFocus = true;
41138         
41139         if(this.triggerAction == 'all') {
41140             this.doQuery(this.allQuery, true);
41141             return;
41142         }
41143         
41144         this.doQuery(this.getRawValue());
41145     },
41146     
41147     getCurrency : function()
41148     {   
41149         var v = this.currencyEl().getValue();
41150         
41151         return v;
41152     },
41153     
41154     restrictHeight : function()
41155     {
41156         this.list.alignTo(this.currencyEl(), this.listAlign);
41157         this.list.alignTo(this.currencyEl(), this.listAlign);
41158     },
41159     
41160     onViewClick : function(view, doFocus, el, e)
41161     {
41162         var index = this.view.getSelectedIndexes()[0];
41163         
41164         var r = this.store.getAt(index);
41165         
41166         if(r){
41167             this.onSelect(r, index);
41168         }
41169     },
41170     
41171     onSelect : function(record, index){
41172         
41173         if(this.fireEvent('beforeselect', this, record, index) !== false){
41174         
41175             this.setFromCurrencyData(index > -1 ? record.data : false);
41176             
41177             this.collapse();
41178             
41179             this.fireEvent('select', this, record, index);
41180         }
41181     },
41182     
41183     setFromCurrencyData : function(o)
41184     {
41185         var currency = '';
41186         
41187         this.lastCurrency = o;
41188         
41189         if (this.currencyField) {
41190             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41191         } else {
41192             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41193         }
41194         
41195         this.lastSelectionText = currency;
41196         
41197         //setting default currency
41198         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41199             this.setCurrency(this.defaultCurrency);
41200             return;
41201         }
41202         
41203         this.setCurrency(currency);
41204     },
41205     
41206     setFromData : function(o)
41207     {
41208         var c = {};
41209         
41210         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41211         
41212         this.setFromCurrencyData(c);
41213         
41214         var value = '';
41215         
41216         if (this.name) {
41217             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41218         } else {
41219             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41220         }
41221         
41222         this.setValue(value);
41223         
41224     },
41225     
41226     setCurrency : function(v)
41227     {   
41228         this.currencyValue = v;
41229         
41230         if(this.rendered){
41231             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41232             this.validate();
41233         }
41234     },
41235     
41236     setValue : function(v)
41237     {
41238         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41239         
41240         this.value = v;
41241         
41242         if(this.rendered){
41243             
41244             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41245             
41246             this.inputEl().dom.value = (v == '') ? '' :
41247                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41248             
41249             if(!this.allowZero && v === '0') {
41250                 this.hiddenEl().dom.value = '';
41251                 this.inputEl().dom.value = '';
41252             }
41253             
41254             this.validate();
41255         }
41256     },
41257     
41258     getRawValue : function()
41259     {
41260         var v = this.inputEl().getValue();
41261         
41262         return v;
41263     },
41264     
41265     getValue : function()
41266     {
41267         return this.fixPrecision(this.parseValue(this.getRawValue()));
41268     },
41269     
41270     parseValue : function(value)
41271     {
41272         if(this.thousandsDelimiter) {
41273             value += "";
41274             r = new RegExp(",", "g");
41275             value = value.replace(r, "");
41276         }
41277         
41278         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41279         return isNaN(value) ? '' : value;
41280         
41281     },
41282     
41283     fixPrecision : function(value)
41284     {
41285         if(this.thousandsDelimiter) {
41286             value += "";
41287             r = new RegExp(",", "g");
41288             value = value.replace(r, "");
41289         }
41290         
41291         var nan = isNaN(value);
41292         
41293         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41294             return nan ? '' : value;
41295         }
41296         return parseFloat(value).toFixed(this.decimalPrecision);
41297     },
41298     
41299     decimalPrecisionFcn : function(v)
41300     {
41301         return Math.floor(v);
41302     },
41303     
41304     validateValue : function(value)
41305     {
41306         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41307             return false;
41308         }
41309         
41310         var num = this.parseValue(value);
41311         
41312         if(isNaN(num)){
41313             this.markInvalid(String.format(this.nanText, value));
41314             return false;
41315         }
41316         
41317         if(num < this.minValue){
41318             this.markInvalid(String.format(this.minText, this.minValue));
41319             return false;
41320         }
41321         
41322         if(num > this.maxValue){
41323             this.markInvalid(String.format(this.maxText, this.maxValue));
41324             return false;
41325         }
41326         
41327         return true;
41328     },
41329     
41330     validate : function()
41331     {
41332         if(this.disabled || this.allowBlank){
41333             this.markValid();
41334             return true;
41335         }
41336         
41337         var currency = this.getCurrency();
41338         
41339         if(this.validateValue(this.getRawValue()) && currency.length){
41340             this.markValid();
41341             return true;
41342         }
41343         
41344         this.markInvalid();
41345         return false;
41346     },
41347     
41348     getName: function()
41349     {
41350         return this.name;
41351     },
41352     
41353     beforeBlur : function()
41354     {
41355         if(!this.castInt){
41356             return;
41357         }
41358         
41359         var v = this.parseValue(this.getRawValue());
41360         
41361         if(v || v == 0){
41362             this.setValue(v);
41363         }
41364     },
41365     
41366     onBlur : function()
41367     {
41368         this.beforeBlur();
41369         
41370         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41371             //this.el.removeClass(this.focusClass);
41372         }
41373         
41374         this.hasFocus = false;
41375         
41376         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41377             this.validate();
41378         }
41379         
41380         var v = this.getValue();
41381         
41382         if(String(v) !== String(this.startValue)){
41383             this.fireEvent('change', this, v, this.startValue);
41384         }
41385         
41386         this.fireEvent("blur", this);
41387     },
41388     
41389     inputEl : function()
41390     {
41391         return this.el.select('.roo-money-amount-input', true).first();
41392     },
41393     
41394     currencyEl : function()
41395     {
41396         return this.el.select('.roo-money-currency-input', true).first();
41397     },
41398     
41399     hiddenEl : function()
41400     {
41401         return this.el.select('input.hidden-number-input',true).first();
41402     }
41403     
41404 });