Roo/bootstrap/NavSimplebar.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');
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');
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, 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.footer.dom.style.display = 'none';
3328             return width;
3329         }
3330         dlg.footerEl.dom.style.display = '';
3331         for(var k in buttons){
3332             if(typeof buttons[k] != "function"){
3333                 if(b[k]){
3334                     buttons[k].show();
3335                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3336                     width += buttons[k].el.getWidth()+15;
3337                 }else{
3338                     buttons[k].hide();
3339                 }
3340             }
3341         }
3342         return width;
3343     };
3344
3345     // private
3346     var handleEsc = function(d, k, e){
3347         if(opt && opt.closable !== false){
3348             dlg.hide();
3349         }
3350         if(e){
3351             e.stopEvent();
3352         }
3353     };
3354
3355     return {
3356         /**
3357          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3358          * @return {Roo.BasicDialog} The BasicDialog element
3359          */
3360         getDialog : function(){
3361            if(!dlg){
3362                 dlg = new Roo.bootstrap.Modal( {
3363                     //draggable: true,
3364                     //resizable:false,
3365                     //constraintoviewport:false,
3366                     //fixedcenter:true,
3367                     //collapsible : false,
3368                     //shim:true,
3369                     //modal: true,
3370                 //    width: 'auto',
3371                   //  height:100,
3372                     //buttonAlign:"center",
3373                     closeClick : function(){
3374                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3375                             handleButton("no");
3376                         }else{
3377                             handleButton("cancel");
3378                         }
3379                     }
3380                 });
3381                 dlg.render();
3382                 dlg.on("hide", handleHide);
3383                 mask = dlg.mask;
3384                 //dlg.addKeyListener(27, handleEsc);
3385                 buttons = {};
3386                 this.buttons = buttons;
3387                 var bt = this.buttonText;
3388                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3389                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3390                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3391                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3392                 //Roo.log(buttons);
3393                 bodyEl = dlg.bodyEl.createChild({
3394
3395                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3396                         '<textarea class="roo-mb-textarea"></textarea>' +
3397                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3398                 });
3399                 msgEl = bodyEl.dom.firstChild;
3400                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3401                 textboxEl.enableDisplayMode();
3402                 textboxEl.addKeyListener([10,13], function(){
3403                     if(dlg.isVisible() && opt && opt.buttons){
3404                         if(opt.buttons.ok){
3405                             handleButton("ok");
3406                         }else if(opt.buttons.yes){
3407                             handleButton("yes");
3408                         }
3409                     }
3410                 });
3411                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3412                 textareaEl.enableDisplayMode();
3413                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3414                 progressEl.enableDisplayMode();
3415                 
3416                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3417                 var pf = progressEl.dom.firstChild;
3418                 if (pf) {
3419                     pp = Roo.get(pf.firstChild);
3420                     pp.setHeight(pf.offsetHeight);
3421                 }
3422                 
3423             }
3424             return dlg;
3425         },
3426
3427         /**
3428          * Updates the message box body text
3429          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3430          * the XHTML-compliant non-breaking space character '&amp;#160;')
3431          * @return {Roo.MessageBox} This message box
3432          */
3433         updateText : function(text)
3434         {
3435             if(!dlg.isVisible() && !opt.width){
3436                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3437                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3438             }
3439             msgEl.innerHTML = text || '&#160;';
3440       
3441             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3442             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3443             var w = Math.max(
3444                     Math.min(opt.width || cw , this.maxWidth), 
3445                     Math.max(opt.minWidth || this.minWidth, bwidth)
3446             );
3447             if(opt.prompt){
3448                 activeTextEl.setWidth(w);
3449             }
3450             if(dlg.isVisible()){
3451                 dlg.fixedcenter = false;
3452             }
3453             // to big, make it scroll. = But as usual stupid IE does not support
3454             // !important..
3455             
3456             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3457                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3458                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3459             } else {
3460                 bodyEl.dom.style.height = '';
3461                 bodyEl.dom.style.overflowY = '';
3462             }
3463             if (cw > w) {
3464                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3465             } else {
3466                 bodyEl.dom.style.overflowX = '';
3467             }
3468             
3469             dlg.setContentSize(w, bodyEl.getHeight());
3470             if(dlg.isVisible()){
3471                 dlg.fixedcenter = true;
3472             }
3473             return this;
3474         },
3475
3476         /**
3477          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3478          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3479          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3480          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3481          * @return {Roo.MessageBox} This message box
3482          */
3483         updateProgress : function(value, text){
3484             if(text){
3485                 this.updateText(text);
3486             }
3487             
3488             if (pp) { // weird bug on my firefox - for some reason this is not defined
3489                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3490                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3491             }
3492             return this;
3493         },        
3494
3495         /**
3496          * Returns true if the message box is currently displayed
3497          * @return {Boolean} True if the message box is visible, else false
3498          */
3499         isVisible : function(){
3500             return dlg && dlg.isVisible();  
3501         },
3502
3503         /**
3504          * Hides the message box if it is displayed
3505          */
3506         hide : function(){
3507             if(this.isVisible()){
3508                 dlg.hide();
3509             }  
3510         },
3511
3512         /**
3513          * Displays a new message box, or reinitializes an existing message box, based on the config options
3514          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3515          * The following config object properties are supported:
3516          * <pre>
3517 Property    Type             Description
3518 ----------  ---------------  ------------------------------------------------------------------------------------
3519 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3520                                    closes (defaults to undefined)
3521 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3522                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3523 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3524                                    progress and wait dialogs will ignore this property and always hide the
3525                                    close button as they can only be closed programmatically.
3526 cls               String           A custom CSS class to apply to the message box element
3527 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3528                                    displayed (defaults to 75)
3529 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3530                                    function will be btn (the name of the button that was clicked, if applicable,
3531                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3532                                    Progress and wait dialogs will ignore this option since they do not respond to
3533                                    user actions and can only be closed programmatically, so any required function
3534                                    should be called by the same code after it closes the dialog.
3535 icon              String           A CSS class that provides a background image to be used as an icon for
3536                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3537 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3538 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3539 modal             Boolean          False to allow user interaction with the page while the message box is
3540                                    displayed (defaults to true)
3541 msg               String           A string that will replace the existing message box body text (defaults
3542                                    to the XHTML-compliant non-breaking space character '&#160;')
3543 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3544 progress          Boolean          True to display a progress bar (defaults to false)
3545 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3546 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3547 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3548 title             String           The title text
3549 value             String           The string value to set into the active textbox element if displayed
3550 wait              Boolean          True to display a progress bar (defaults to false)
3551 width             Number           The width of the dialog in pixels
3552 </pre>
3553          *
3554          * Example usage:
3555          * <pre><code>
3556 Roo.Msg.show({
3557    title: 'Address',
3558    msg: 'Please enter your address:',
3559    width: 300,
3560    buttons: Roo.MessageBox.OKCANCEL,
3561    multiline: true,
3562    fn: saveAddress,
3563    animEl: 'addAddressBtn'
3564 });
3565 </code></pre>
3566          * @param {Object} config Configuration options
3567          * @return {Roo.MessageBox} This message box
3568          */
3569         show : function(options)
3570         {
3571             
3572             // this causes nightmares if you show one dialog after another
3573             // especially on callbacks..
3574              
3575             if(this.isVisible()){
3576                 
3577                 this.hide();
3578                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3579                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3580                 Roo.log("New Dialog Message:" +  options.msg )
3581                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3582                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3583                 
3584             }
3585             var d = this.getDialog();
3586             opt = options;
3587             d.setTitle(opt.title || "&#160;");
3588             d.closeEl.setDisplayed(opt.closable !== false);
3589             activeTextEl = textboxEl;
3590             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3591             if(opt.prompt){
3592                 if(opt.multiline){
3593                     textboxEl.hide();
3594                     textareaEl.show();
3595                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3596                         opt.multiline : this.defaultTextHeight);
3597                     activeTextEl = textareaEl;
3598                 }else{
3599                     textboxEl.show();
3600                     textareaEl.hide();
3601                 }
3602             }else{
3603                 textboxEl.hide();
3604                 textareaEl.hide();
3605             }
3606             progressEl.setDisplayed(opt.progress === true);
3607             this.updateProgress(0);
3608             activeTextEl.dom.value = opt.value || "";
3609             if(opt.prompt){
3610                 dlg.setDefaultButton(activeTextEl);
3611             }else{
3612                 var bs = opt.buttons;
3613                 var db = null;
3614                 if(bs && bs.ok){
3615                     db = buttons["ok"];
3616                 }else if(bs && bs.yes){
3617                     db = buttons["yes"];
3618                 }
3619                 dlg.setDefaultButton(db);
3620             }
3621             bwidth = updateButtons(opt.buttons);
3622             this.updateText(opt.msg);
3623             if(opt.cls){
3624                 d.el.addClass(opt.cls);
3625             }
3626             d.proxyDrag = opt.proxyDrag === true;
3627             d.modal = opt.modal !== false;
3628             d.mask = opt.modal !== false ? mask : false;
3629             if(!d.isVisible()){
3630                 // force it to the end of the z-index stack so it gets a cursor in FF
3631                 document.body.appendChild(dlg.el.dom);
3632                 d.animateTarget = null;
3633                 d.show(options.animEl);
3634             }
3635             return this;
3636         },
3637
3638         /**
3639          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3640          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3641          * and closing the message box when the process is complete.
3642          * @param {String} title The title bar text
3643          * @param {String} msg The message box body text
3644          * @return {Roo.MessageBox} This message box
3645          */
3646         progress : function(title, msg){
3647             this.show({
3648                 title : title,
3649                 msg : msg,
3650                 buttons: false,
3651                 progress:true,
3652                 closable:false,
3653                 minWidth: this.minProgressWidth,
3654                 modal : true
3655             });
3656             return this;
3657         },
3658
3659         /**
3660          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3661          * If a callback function is passed it will be called after the user clicks the button, and the
3662          * id of the button that was clicked will be passed as the only parameter to the callback
3663          * (could also be the top-right close button).
3664          * @param {String} title The title bar text
3665          * @param {String} msg The message box body text
3666          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3667          * @param {Object} scope (optional) The scope of the callback function
3668          * @return {Roo.MessageBox} This message box
3669          */
3670         alert : function(title, msg, fn, scope)
3671         {
3672             this.show({
3673                 title : title,
3674                 msg : msg,
3675                 buttons: this.OK,
3676                 fn: fn,
3677                 closable : false,
3678                 scope : scope,
3679                 modal : true
3680             });
3681             return this;
3682         },
3683
3684         /**
3685          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3686          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3687          * You are responsible for closing the message box when the process is complete.
3688          * @param {String} msg The message box body text
3689          * @param {String} title (optional) The title bar text
3690          * @return {Roo.MessageBox} This message box
3691          */
3692         wait : function(msg, title){
3693             this.show({
3694                 title : title,
3695                 msg : msg,
3696                 buttons: false,
3697                 closable:false,
3698                 progress:true,
3699                 modal:true,
3700                 width:300,
3701                 wait:true
3702             });
3703             waitTimer = Roo.TaskMgr.start({
3704                 run: function(i){
3705                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3706                 },
3707                 interval: 1000
3708             });
3709             return this;
3710         },
3711
3712         /**
3713          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3714          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3715          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3716          * @param {String} title The title bar text
3717          * @param {String} msg The message box body text
3718          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3719          * @param {Object} scope (optional) The scope of the callback function
3720          * @return {Roo.MessageBox} This message box
3721          */
3722         confirm : function(title, msg, fn, scope){
3723             this.show({
3724                 title : title,
3725                 msg : msg,
3726                 buttons: this.YESNO,
3727                 fn: fn,
3728                 scope : scope,
3729                 modal : true
3730             });
3731             return this;
3732         },
3733
3734         /**
3735          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3736          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3737          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3738          * (could also be the top-right close button) and the text that was entered will be passed as the two
3739          * parameters to the callback.
3740          * @param {String} title The title bar text
3741          * @param {String} msg The message box body text
3742          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3743          * @param {Object} scope (optional) The scope of the callback function
3744          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3745          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3746          * @return {Roo.MessageBox} This message box
3747          */
3748         prompt : function(title, msg, fn, scope, multiline){
3749             this.show({
3750                 title : title,
3751                 msg : msg,
3752                 buttons: this.OKCANCEL,
3753                 fn: fn,
3754                 minWidth:250,
3755                 scope : scope,
3756                 prompt:true,
3757                 multiline: multiline,
3758                 modal : true
3759             });
3760             return this;
3761         },
3762
3763         /**
3764          * Button config that displays a single OK button
3765          * @type Object
3766          */
3767         OK : {ok:true},
3768         /**
3769          * Button config that displays Yes and No buttons
3770          * @type Object
3771          */
3772         YESNO : {yes:true, no:true},
3773         /**
3774          * Button config that displays OK and Cancel buttons
3775          * @type Object
3776          */
3777         OKCANCEL : {ok:true, cancel:true},
3778         /**
3779          * Button config that displays Yes, No and Cancel buttons
3780          * @type Object
3781          */
3782         YESNOCANCEL : {yes:true, no:true, cancel:true},
3783
3784         /**
3785          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3786          * @type Number
3787          */
3788         defaultTextHeight : 75,
3789         /**
3790          * The maximum width in pixels of the message box (defaults to 600)
3791          * @type Number
3792          */
3793         maxWidth : 600,
3794         /**
3795          * The minimum width in pixels of the message box (defaults to 100)
3796          * @type Number
3797          */
3798         minWidth : 100,
3799         /**
3800          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3801          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3802          * @type Number
3803          */
3804         minProgressWidth : 250,
3805         /**
3806          * An object containing the default button text strings that can be overriden for localized language support.
3807          * Supported properties are: ok, cancel, yes and no.
3808          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3809          * @type Object
3810          */
3811         buttonText : {
3812             ok : "OK",
3813             cancel : "Cancel",
3814             yes : "Yes",
3815             no : "No"
3816         }
3817     };
3818 }();
3819
3820 /**
3821  * Shorthand for {@link Roo.MessageBox}
3822  */
3823 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3824 Roo.Msg = Roo.Msg || Roo.MessageBox;
3825 /*
3826  * - LGPL
3827  *
3828  * navbar
3829  * 
3830  */
3831
3832 /**
3833  * @class Roo.bootstrap.Navbar
3834  * @extends Roo.bootstrap.Component
3835  * Bootstrap Navbar class
3836
3837  * @constructor
3838  * Create a new Navbar
3839  * @param {Object} config The config object
3840  */
3841
3842
3843 Roo.bootstrap.Navbar = function(config){
3844     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3845     this.addEvents({
3846         // raw events
3847         /**
3848          * @event beforetoggle
3849          * Fire before toggle the menu
3850          * @param {Roo.EventObject} e
3851          */
3852         "beforetoggle" : true
3853     });
3854 };
3855
3856 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3857     
3858     
3859    
3860     // private
3861     navItems : false,
3862     loadMask : false,
3863     
3864     
3865     getAutoCreate : function(){
3866         
3867         
3868         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3869         
3870     },
3871     
3872     initEvents :function ()
3873     {
3874         //Roo.log(this.el.select('.navbar-toggle',true));
3875         this.el.select('.navbar-toggle',true).on('click', function() {
3876             if(this.fireEvent('beforetoggle', this) !== false){
3877                 var ce = this.el.select('.navbar-collapse',true).first();
3878                 ce.toggleClass('in'); // old...
3879                 if (ce.hasClass('collapse')) {
3880                     // show it...
3881                     ce.removeClass('collapse');
3882                     ce.addClass('show');
3883                     var h = ce.getHeight();
3884                     Roo.log(h);
3885                     ce.removeClass('show');
3886                     // at this point we should be able to see it..
3887                     ce.addClass('collapsing');
3888                     
3889                     ce.setHeight(0); // resize it ...
3890                     ce.on('transitionend', function() {
3891                         Roo.log('done transition');
3892                         ce.removeClass('collapsing');
3893                         ce.addClass('show');
3894                         ce.removeClass('collapse');
3895
3896                         ce.dom.style.height = '';
3897                     }, this, { single: true} );
3898                     ce.setHeight(h);
3899                     
3900                 } else {
3901                     ce.setHeight(ce.getHeight());
3902                     ce.removeClass('show');
3903                     ce.addClass('collapsing');
3904                     
3905                     ce.on('transitionend', function() {
3906                         ce.dom.style.height = '';
3907                         ce.removeClass('collapsing');
3908                         ce.addClass('collapse');
3909                     }, this, { single: true} );
3910                     ce.setHeight(0);
3911                 }
3912             }
3913             
3914         }, this);
3915         
3916         var mark = {
3917             tag: "div",
3918             cls:"x-dlg-mask"
3919         };
3920         
3921         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3922         
3923         var size = this.el.getSize();
3924         this.maskEl.setSize(size.width, size.height);
3925         this.maskEl.enableDisplayMode("block");
3926         this.maskEl.hide();
3927         
3928         if(this.loadMask){
3929             this.maskEl.show();
3930         }
3931     },
3932     
3933     
3934     getChildContainer : function()
3935     {
3936         if (this.el.select('.collapse').getCount()) {
3937             return this.el.select('.collapse',true).first();
3938         }
3939         
3940         return this.el;
3941     },
3942     
3943     mask : function()
3944     {
3945         this.maskEl.show();
3946     },
3947     
3948     unmask : function()
3949     {
3950         this.maskEl.hide();
3951     } 
3952     
3953     
3954     
3955     
3956 });
3957
3958
3959
3960  
3961
3962  /*
3963  * - LGPL
3964  *
3965  * navbar
3966  * 
3967  */
3968
3969 /**
3970  * @class Roo.bootstrap.NavSimplebar
3971  * @extends Roo.bootstrap.Navbar
3972  * Bootstrap Sidebar class
3973  *
3974  * @cfg {Boolean} inverse is inverted color
3975  * 
3976  * @cfg {String} type (nav | pills | tabs)
3977  * @cfg {Boolean} arrangement stacked | justified
3978  * @cfg {String} align (left | right) alignment
3979  * 
3980  * @cfg {Boolean} main (true|false) main nav bar? default false
3981  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3982  * 
3983  * @cfg {String} tag (header|footer|nav|div) default is nav 
3984
3985  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3986  * 
3987  * 
3988  * @constructor
3989  * Create a new Sidebar
3990  * @param {Object} config The config object
3991  */
3992
3993
3994 Roo.bootstrap.NavSimplebar = function(config){
3995     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3996 };
3997
3998 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3999     
4000     inverse: false,
4001     
4002     type: false,
4003     arrangement: '',
4004     align : false,
4005     
4006     weight : 'light',
4007     
4008     main : false,
4009     
4010     
4011     tag : false,
4012     
4013     
4014     getAutoCreate : function(){
4015         
4016         
4017         var cfg = {
4018             tag : this.tag || 'div',
4019             cls : 'navbar navbar-expand-lg'
4020         };
4021         if (['light','white'].indexOf(this.weight) > -1) {
4022             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4023         }
4024         cfg.cls += ' bg-' + this.weight;
4025         
4026         if (this.inverse) {
4027             cfg.cls += ' navbar-inverse';
4028             
4029         }
4030         
4031         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4032         
4033         if (Roo.bootstrap.version == 4) {
4034             return cfg;
4035         }
4036         
4037         cfg.cn = [
4038             {
4039                 cls: 'nav',
4040                 tag : 'ul'
4041             }
4042         ];
4043         
4044          
4045         this.type = this.type || 'nav';
4046         if (['tabs','pills'].indexOf(this.type)!==-1) {
4047             cfg.cn[0].cls += ' nav-' + this.type
4048         
4049         
4050         } else {
4051             if (this.type!=='nav') {
4052                 Roo.log('nav type must be nav/tabs/pills')
4053             }
4054             cfg.cn[0].cls += ' navbar-nav'
4055         }
4056         
4057         
4058         
4059         
4060         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
4061             cfg.cn[0].cls += ' nav-' + this.arrangement;
4062         }
4063         
4064         
4065         if (this.align === 'right') {
4066             cfg.cn[0].cls += ' navbar-right';
4067         }
4068         
4069         
4070         
4071         
4072         return cfg;
4073     
4074         
4075     }
4076     
4077     
4078     
4079 });
4080
4081
4082
4083  
4084
4085  
4086        /*
4087  * - LGPL
4088  *
4089  * navbar
4090  * navbar-fixed-top
4091  * navbar-expand-md  fixed-top 
4092  */
4093
4094 /**
4095  * @class Roo.bootstrap.NavHeaderbar
4096  * @extends Roo.bootstrap.NavSimplebar
4097  * Bootstrap Sidebar class
4098  *
4099  * @cfg {String} brand what is brand
4100  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4101  * @cfg {String} brand_href href of the brand
4102  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4103  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4104  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4105  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4106  * 
4107  * @constructor
4108  * Create a new Sidebar
4109  * @param {Object} config The config object
4110  */
4111
4112
4113 Roo.bootstrap.NavHeaderbar = function(config){
4114     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4115       
4116 };
4117
4118 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4119     
4120     position: '',
4121     brand: '',
4122     brand_href: false,
4123     srButton : true,
4124     autohide : false,
4125     desktopCenter : false,
4126    
4127     
4128     getAutoCreate : function(){
4129         
4130         var   cfg = {
4131             tag: this.nav || 'nav',
4132             cls: 'navbar navbar-expand-md',
4133             role: 'navigation',
4134             cn: []
4135         };
4136         
4137         var cn = cfg.cn;
4138         if (this.desktopCenter) {
4139             cn.push({cls : 'container', cn : []});
4140             cn = cn[0].cn;
4141         }
4142         
4143         if(this.srButton){
4144             var btn = {
4145                 tag: 'button',
4146                 type: 'button',
4147                 cls: 'navbar-toggle navbar-toggler',
4148                 'data-toggle': 'collapse',
4149                 cn: [
4150                     {
4151                         tag: 'span',
4152                         cls: 'sr-only',
4153                         html: 'Toggle navigation'
4154                     },
4155                     {
4156                         tag: 'span',
4157                         cls: 'icon-bar navbar-toggler-icon'
4158                     },
4159                     {
4160                         tag: 'span',
4161                         cls: 'icon-bar'
4162                     },
4163                     {
4164                         tag: 'span',
4165                         cls: 'icon-bar'
4166                     }
4167                 ]
4168             };
4169             
4170             cn.push( Roo.bootstrap.version == 4 ? btn : {
4171                 tag: 'div',
4172                 cls: 'navbar-header',
4173                 cn: [
4174                     btn
4175                 ]
4176             });
4177         }
4178         
4179         cn.push({
4180             tag: 'div',
4181             cls: 'collapse navbar-collapse',
4182             cn : []
4183         });
4184         
4185         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4186         
4187         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4188             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4189             
4190             // tag can override this..
4191             
4192             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4193         }
4194         
4195         if (this.brand !== '') {
4196             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4197             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4198                 tag: 'a',
4199                 href: this.brand_href ? this.brand_href : '#',
4200                 cls: 'navbar-brand',
4201                 cn: [
4202                 this.brand
4203                 ]
4204             });
4205         }
4206         
4207         if(this.main){
4208             cfg.cls += ' main-nav';
4209         }
4210         
4211         
4212         return cfg;
4213
4214         
4215     },
4216     getHeaderChildContainer : function()
4217     {
4218         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4219             return this.el.select('.navbar-header',true).first();
4220         }
4221         
4222         return this.getChildContainer();
4223     },
4224     
4225     
4226     initEvents : function()
4227     {
4228         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4229         
4230         if (this.autohide) {
4231             
4232             var prevScroll = 0;
4233             var ft = this.el;
4234             
4235             Roo.get(document).on('scroll',function(e) {
4236                 var ns = Roo.get(document).getScroll().top;
4237                 var os = prevScroll;
4238                 prevScroll = ns;
4239                 
4240                 if(ns > os){
4241                     ft.removeClass('slideDown');
4242                     ft.addClass('slideUp');
4243                     return;
4244                 }
4245                 ft.removeClass('slideUp');
4246                 ft.addClass('slideDown');
4247                  
4248               
4249           },this);
4250         }
4251     }    
4252     
4253 });
4254
4255
4256
4257  
4258
4259  /*
4260  * - LGPL
4261  *
4262  * navbar
4263  * 
4264  */
4265
4266 /**
4267  * @class Roo.bootstrap.NavSidebar
4268  * @extends Roo.bootstrap.Navbar
4269  * Bootstrap Sidebar class
4270  * 
4271  * @constructor
4272  * Create a new Sidebar
4273  * @param {Object} config The config object
4274  */
4275
4276
4277 Roo.bootstrap.NavSidebar = function(config){
4278     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4279 };
4280
4281 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4282     
4283     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4284     
4285     getAutoCreate : function(){
4286         
4287         
4288         return  {
4289             tag: 'div',
4290             cls: 'sidebar sidebar-nav'
4291         };
4292     
4293         
4294     }
4295     
4296     
4297     
4298 });
4299
4300
4301
4302  
4303
4304  /*
4305  * - LGPL
4306  *
4307  * nav group
4308  * 
4309  */
4310
4311 /**
4312  * @class Roo.bootstrap.NavGroup
4313  * @extends Roo.bootstrap.Component
4314  * Bootstrap NavGroup class
4315  * @cfg {String} align (left|right)
4316  * @cfg {Boolean} inverse
4317  * @cfg {String} type (nav|pills|tab) default nav
4318  * @cfg {String} navId - reference Id for navbar.
4319
4320  * 
4321  * @constructor
4322  * Create a new nav group
4323  * @param {Object} config The config object
4324  */
4325
4326 Roo.bootstrap.NavGroup = function(config){
4327     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4328     this.navItems = [];
4329    
4330     Roo.bootstrap.NavGroup.register(this);
4331      this.addEvents({
4332         /**
4333              * @event changed
4334              * Fires when the active item changes
4335              * @param {Roo.bootstrap.NavGroup} this
4336              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4337              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4338          */
4339         'changed': true
4340      });
4341     
4342 };
4343
4344 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4345     
4346     align: '',
4347     inverse: false,
4348     form: false,
4349     type: 'nav',
4350     navId : '',
4351     // private
4352     
4353     navItems : false, 
4354     
4355     getAutoCreate : function()
4356     {
4357         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4358         
4359         cfg = {
4360             tag : 'ul',
4361             cls: 'nav' 
4362         };
4363         
4364         if (['tabs','pills'].indexOf(this.type)!==-1) {
4365             cfg.cls += ' nav-' + this.type
4366         } else {
4367             if (this.type!=='nav') {
4368                 Roo.log('nav type must be nav/tabs/pills')
4369             }
4370             cfg.cls += ' navbar-nav'
4371         }
4372         
4373         if (this.parent() && this.parent().sidebar) {
4374             cfg = {
4375                 tag: 'ul',
4376                 cls: 'dashboard-menu sidebar-menu'
4377             };
4378             
4379             return cfg;
4380         }
4381         
4382         if (this.form === true) {
4383             cfg = {
4384                 tag: 'form',
4385                 cls: 'navbar-form'
4386             };
4387             
4388             if (this.align === 'right') {
4389                 cfg.cls += ' navbar-right ml-md-auto';
4390             } else {
4391                 cfg.cls += ' navbar-left';
4392             }
4393         }
4394         
4395         if (this.align === 'right') {
4396             cfg.cls += ' navbar-right ml-md-auto';
4397         } else {
4398             cfg.cls += ' mr-auto';
4399         }
4400         
4401         if (this.inverse) {
4402             cfg.cls += ' navbar-inverse';
4403             
4404         }
4405         
4406         
4407         return cfg;
4408     },
4409     /**
4410     * sets the active Navigation item
4411     * @param {Roo.bootstrap.NavItem} the new current navitem
4412     */
4413     setActiveItem : function(item)
4414     {
4415         var prev = false;
4416         Roo.each(this.navItems, function(v){
4417             if (v == item) {
4418                 return ;
4419             }
4420             if (v.isActive()) {
4421                 v.setActive(false, true);
4422                 prev = v;
4423                 
4424             }
4425             
4426         });
4427
4428         item.setActive(true, true);
4429         this.fireEvent('changed', this, item, prev);
4430         
4431         
4432     },
4433     /**
4434     * gets the active Navigation item
4435     * @return {Roo.bootstrap.NavItem} the current navitem
4436     */
4437     getActive : function()
4438     {
4439         
4440         var prev = false;
4441         Roo.each(this.navItems, function(v){
4442             
4443             if (v.isActive()) {
4444                 prev = v;
4445                 
4446             }
4447             
4448         });
4449         return prev;
4450     },
4451     
4452     indexOfNav : function()
4453     {
4454         
4455         var prev = false;
4456         Roo.each(this.navItems, function(v,i){
4457             
4458             if (v.isActive()) {
4459                 prev = i;
4460                 
4461             }
4462             
4463         });
4464         return prev;
4465     },
4466     /**
4467     * adds a Navigation item
4468     * @param {Roo.bootstrap.NavItem} the navitem to add
4469     */
4470     addItem : function(cfg)
4471     {
4472         var cn = new Roo.bootstrap.NavItem(cfg);
4473         this.register(cn);
4474         cn.parentId = this.id;
4475         cn.onRender(this.el, null);
4476         return cn;
4477     },
4478     /**
4479     * register a Navigation item
4480     * @param {Roo.bootstrap.NavItem} the navitem to add
4481     */
4482     register : function(item)
4483     {
4484         this.navItems.push( item);
4485         item.navId = this.navId;
4486     
4487     },
4488     
4489     /**
4490     * clear all the Navigation item
4491     */
4492    
4493     clearAll : function()
4494     {
4495         this.navItems = [];
4496         this.el.dom.innerHTML = '';
4497     },
4498     
4499     getNavItem: function(tabId)
4500     {
4501         var ret = false;
4502         Roo.each(this.navItems, function(e) {
4503             if (e.tabId == tabId) {
4504                ret =  e;
4505                return false;
4506             }
4507             return true;
4508             
4509         });
4510         return ret;
4511     },
4512     
4513     setActiveNext : function()
4514     {
4515         var i = this.indexOfNav(this.getActive());
4516         if (i > this.navItems.length) {
4517             return;
4518         }
4519         this.setActiveItem(this.navItems[i+1]);
4520     },
4521     setActivePrev : function()
4522     {
4523         var i = this.indexOfNav(this.getActive());
4524         if (i  < 1) {
4525             return;
4526         }
4527         this.setActiveItem(this.navItems[i-1]);
4528     },
4529     clearWasActive : function(except) {
4530         Roo.each(this.navItems, function(e) {
4531             if (e.tabId != except.tabId && e.was_active) {
4532                e.was_active = false;
4533                return false;
4534             }
4535             return true;
4536             
4537         });
4538     },
4539     getWasActive : function ()
4540     {
4541         var r = false;
4542         Roo.each(this.navItems, function(e) {
4543             if (e.was_active) {
4544                r = e;
4545                return false;
4546             }
4547             return true;
4548             
4549         });
4550         return r;
4551     }
4552     
4553     
4554 });
4555
4556  
4557 Roo.apply(Roo.bootstrap.NavGroup, {
4558     
4559     groups: {},
4560      /**
4561     * register a Navigation Group
4562     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4563     */
4564     register : function(navgrp)
4565     {
4566         this.groups[navgrp.navId] = navgrp;
4567         
4568     },
4569     /**
4570     * fetch a Navigation Group based on the navigation ID
4571     * @param {string} the navgroup to add
4572     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4573     */
4574     get: function(navId) {
4575         if (typeof(this.groups[navId]) == 'undefined') {
4576             return false;
4577             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4578         }
4579         return this.groups[navId] ;
4580     }
4581     
4582     
4583     
4584 });
4585
4586  /*
4587  * - LGPL
4588  *
4589  * row
4590  * 
4591  */
4592
4593 /**
4594  * @class Roo.bootstrap.NavItem
4595  * @extends Roo.bootstrap.Component
4596  * Bootstrap Navbar.NavItem class
4597  * @cfg {String} href  link to
4598  * @cfg {String} html content of button
4599  * @cfg {String} badge text inside badge
4600  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4601  * @cfg {String} glyphicon DEPRICATED - use fa
4602  * @cfg {String} icon DEPRICATED - use fa
4603  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4604  * @cfg {Boolean} active Is item active
4605  * @cfg {Boolean} disabled Is item disabled
4606  
4607  * @cfg {Boolean} preventDefault (true | false) default false
4608  * @cfg {String} tabId the tab that this item activates.
4609  * @cfg {String} tagtype (a|span) render as a href or span?
4610  * @cfg {Boolean} animateRef (true|false) link to element default false  
4611   
4612  * @constructor
4613  * Create a new Navbar Item
4614  * @param {Object} config The config object
4615  */
4616 Roo.bootstrap.NavItem = function(config){
4617     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4618     this.addEvents({
4619         // raw events
4620         /**
4621          * @event click
4622          * The raw click event for the entire grid.
4623          * @param {Roo.EventObject} e
4624          */
4625         "click" : true,
4626          /**
4627             * @event changed
4628             * Fires when the active item active state changes
4629             * @param {Roo.bootstrap.NavItem} this
4630             * @param {boolean} state the new state
4631              
4632          */
4633         'changed': true,
4634         /**
4635             * @event scrollto
4636             * Fires when scroll to element
4637             * @param {Roo.bootstrap.NavItem} this
4638             * @param {Object} options
4639             * @param {Roo.EventObject} e
4640              
4641          */
4642         'scrollto': true
4643     });
4644    
4645 };
4646
4647 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4648     
4649     href: false,
4650     html: '',
4651     badge: '',
4652     icon: false,
4653     fa : false,
4654     glyphicon: false,
4655     active: false,
4656     preventDefault : false,
4657     tabId : false,
4658     tagtype : 'a',
4659     disabled : false,
4660     animateRef : false,
4661     was_active : false,
4662     
4663     getAutoCreate : function(){
4664          
4665         var cfg = {
4666             tag: 'li',
4667             cls: 'nav-item'
4668             
4669         };
4670         
4671         if (this.active) {
4672             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4673         }
4674         if (this.disabled) {
4675             cfg.cls += ' disabled';
4676         }
4677         
4678         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4679             cfg.cn = [
4680                 {
4681                     tag: this.tagtype,
4682                     href : this.href || "#",
4683                     html: this.html || ''
4684                 }
4685             ];
4686             if (this.tagtype == 'a') {
4687                 cfg.cn[0].cls = 'nav-link';
4688             }
4689             if (this.icon) {
4690                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4691             }
4692             if (this.fa) {
4693                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>'
4694             }
4695             if(this.glyphicon) {
4696                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4697             }
4698             
4699             if (this.menu) {
4700                 
4701                 cfg.cn[0].html += " <span class='caret'></span>";
4702              
4703             }
4704             
4705             if (this.badge !== '') {
4706                  
4707                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4708             }
4709         }
4710         
4711         
4712         
4713         return cfg;
4714     },
4715     initEvents: function() 
4716     {
4717         if (typeof (this.menu) != 'undefined') {
4718             this.menu.parentType = this.xtype;
4719             this.menu.triggerEl = this.el;
4720             this.menu = this.addxtype(Roo.apply({}, this.menu));
4721         }
4722         
4723         this.el.select('a',true).on('click', this.onClick, this);
4724         
4725         if(this.tagtype == 'span'){
4726             this.el.select('span',true).on('click', this.onClick, this);
4727         }
4728        
4729         // at this point parent should be available..
4730         this.parent().register(this);
4731     },
4732     
4733     onClick : function(e)
4734     {
4735         if (e.getTarget('.dropdown-menu-item')) {
4736             // did you click on a menu itemm.... - then don't trigger onclick..
4737             return;
4738         }
4739         
4740         if(
4741                 this.preventDefault || 
4742                 this.href == '#' 
4743         ){
4744             Roo.log("NavItem - prevent Default?");
4745             e.preventDefault();
4746         }
4747         
4748         if (this.disabled) {
4749             return;
4750         }
4751         
4752         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4753         if (tg && tg.transition) {
4754             Roo.log("waiting for the transitionend");
4755             return;
4756         }
4757         
4758         
4759         
4760         //Roo.log("fire event clicked");
4761         if(this.fireEvent('click', this, e) === false){
4762             return;
4763         };
4764         
4765         if(this.tagtype == 'span'){
4766             return;
4767         }
4768         
4769         //Roo.log(this.href);
4770         var ael = this.el.select('a',true).first();
4771         //Roo.log(ael);
4772         
4773         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4774             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4775             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4776                 return; // ignore... - it's a 'hash' to another page.
4777             }
4778             Roo.log("NavItem - prevent Default?");
4779             e.preventDefault();
4780             this.scrollToElement(e);
4781         }
4782         
4783         
4784         var p =  this.parent();
4785    
4786         if (['tabs','pills'].indexOf(p.type)!==-1) {
4787             if (typeof(p.setActiveItem) !== 'undefined') {
4788                 p.setActiveItem(this);
4789             }
4790         }
4791         
4792         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4793         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4794             // remove the collapsed menu expand...
4795             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4796         }
4797     },
4798     
4799     isActive: function () {
4800         return this.active
4801     },
4802     setActive : function(state, fire, is_was_active)
4803     {
4804         if (this.active && !state && this.navId) {
4805             this.was_active = true;
4806             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4807             if (nv) {
4808                 nv.clearWasActive(this);
4809             }
4810             
4811         }
4812         this.active = state;
4813         
4814         if (!state ) {
4815             this.el.removeClass('active');
4816         } else if (!this.el.hasClass('active')) {
4817             this.el.addClass('active');
4818         }
4819         if (fire) {
4820             this.fireEvent('changed', this, state);
4821         }
4822         
4823         // show a panel if it's registered and related..
4824         
4825         if (!this.navId || !this.tabId || !state || is_was_active) {
4826             return;
4827         }
4828         
4829         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4830         if (!tg) {
4831             return;
4832         }
4833         var pan = tg.getPanelByName(this.tabId);
4834         if (!pan) {
4835             return;
4836         }
4837         // if we can not flip to new panel - go back to old nav highlight..
4838         if (false == tg.showPanel(pan)) {
4839             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4840             if (nv) {
4841                 var onav = nv.getWasActive();
4842                 if (onav) {
4843                     onav.setActive(true, false, true);
4844                 }
4845             }
4846             
4847         }
4848         
4849         
4850         
4851     },
4852      // this should not be here...
4853     setDisabled : function(state)
4854     {
4855         this.disabled = state;
4856         if (!state ) {
4857             this.el.removeClass('disabled');
4858         } else if (!this.el.hasClass('disabled')) {
4859             this.el.addClass('disabled');
4860         }
4861         
4862     },
4863     
4864     /**
4865      * Fetch the element to display the tooltip on.
4866      * @return {Roo.Element} defaults to this.el
4867      */
4868     tooltipEl : function()
4869     {
4870         return this.el.select('' + this.tagtype + '', true).first();
4871     },
4872     
4873     scrollToElement : function(e)
4874     {
4875         var c = document.body;
4876         
4877         /*
4878          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4879          */
4880         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4881             c = document.documentElement;
4882         }
4883         
4884         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4885         
4886         if(!target){
4887             return;
4888         }
4889
4890         var o = target.calcOffsetsTo(c);
4891         
4892         var options = {
4893             target : target,
4894             value : o[1]
4895         };
4896         
4897         this.fireEvent('scrollto', this, options, e);
4898         
4899         Roo.get(c).scrollTo('top', options.value, true);
4900         
4901         return;
4902     }
4903 });
4904  
4905
4906  /*
4907  * - LGPL
4908  *
4909  * sidebar item
4910  *
4911  *  li
4912  *    <span> icon </span>
4913  *    <span> text </span>
4914  *    <span>badge </span>
4915  */
4916
4917 /**
4918  * @class Roo.bootstrap.NavSidebarItem
4919  * @extends Roo.bootstrap.NavItem
4920  * Bootstrap Navbar.NavSidebarItem class
4921  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4922  * {Boolean} open is the menu open
4923  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4924  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4925  * {String} buttonSize (sm|md|lg)the extra classes for the button
4926  * {Boolean} showArrow show arrow next to the text (default true)
4927  * @constructor
4928  * Create a new Navbar Button
4929  * @param {Object} config The config object
4930  */
4931 Roo.bootstrap.NavSidebarItem = function(config){
4932     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4933     this.addEvents({
4934         // raw events
4935         /**
4936          * @event click
4937          * The raw click event for the entire grid.
4938          * @param {Roo.EventObject} e
4939          */
4940         "click" : true,
4941          /**
4942             * @event changed
4943             * Fires when the active item active state changes
4944             * @param {Roo.bootstrap.NavSidebarItem} this
4945             * @param {boolean} state the new state
4946              
4947          */
4948         'changed': true
4949     });
4950    
4951 };
4952
4953 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4954     
4955     badgeWeight : 'default',
4956     
4957     open: false,
4958     
4959     buttonView : false,
4960     
4961     buttonWeight : 'default',
4962     
4963     buttonSize : 'md',
4964     
4965     showArrow : true,
4966     
4967     getAutoCreate : function(){
4968         
4969         
4970         var a = {
4971                 tag: 'a',
4972                 href : this.href || '#',
4973                 cls: '',
4974                 html : '',
4975                 cn : []
4976         };
4977         
4978         if(this.buttonView){
4979             a = {
4980                 tag: 'button',
4981                 href : this.href || '#',
4982                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4983                 html : this.html,
4984                 cn : []
4985             };
4986         }
4987         
4988         var cfg = {
4989             tag: 'li',
4990             cls: '',
4991             cn: [ a ]
4992         };
4993         
4994         if (this.active) {
4995             cfg.cls += ' active';
4996         }
4997         
4998         if (this.disabled) {
4999             cfg.cls += ' disabled';
5000         }
5001         if (this.open) {
5002             cfg.cls += ' open x-open';
5003         }
5004         // left icon..
5005         if (this.glyphicon || this.icon) {
5006             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5007             a.cn.push({ tag : 'i', cls : c }) ;
5008         }
5009         
5010         if(!this.buttonView){
5011             var span = {
5012                 tag: 'span',
5013                 html : this.html || ''
5014             };
5015
5016             a.cn.push(span);
5017             
5018         }
5019         
5020         if (this.badge !== '') {
5021             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5022         }
5023         
5024         if (this.menu) {
5025             
5026             if(this.showArrow){
5027                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5028             }
5029             
5030             a.cls += ' dropdown-toggle treeview' ;
5031         }
5032         
5033         return cfg;
5034     },
5035     
5036     initEvents : function()
5037     { 
5038         if (typeof (this.menu) != 'undefined') {
5039             this.menu.parentType = this.xtype;
5040             this.menu.triggerEl = this.el;
5041             this.menu = this.addxtype(Roo.apply({}, this.menu));
5042         }
5043         
5044         this.el.on('click', this.onClick, this);
5045         
5046         if(this.badge !== ''){
5047             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5048         }
5049         
5050     },
5051     
5052     onClick : function(e)
5053     {
5054         if(this.disabled){
5055             e.preventDefault();
5056             return;
5057         }
5058         
5059         if(this.preventDefault){
5060             e.preventDefault();
5061         }
5062         
5063         this.fireEvent('click', this);
5064     },
5065     
5066     disable : function()
5067     {
5068         this.setDisabled(true);
5069     },
5070     
5071     enable : function()
5072     {
5073         this.setDisabled(false);
5074     },
5075     
5076     setDisabled : function(state)
5077     {
5078         if(this.disabled == state){
5079             return;
5080         }
5081         
5082         this.disabled = state;
5083         
5084         if (state) {
5085             this.el.addClass('disabled');
5086             return;
5087         }
5088         
5089         this.el.removeClass('disabled');
5090         
5091         return;
5092     },
5093     
5094     setActive : function(state)
5095     {
5096         if(this.active == state){
5097             return;
5098         }
5099         
5100         this.active = state;
5101         
5102         if (state) {
5103             this.el.addClass('active');
5104             return;
5105         }
5106         
5107         this.el.removeClass('active');
5108         
5109         return;
5110     },
5111     
5112     isActive: function () 
5113     {
5114         return this.active;
5115     },
5116     
5117     setBadge : function(str)
5118     {
5119         if(!this.badgeEl){
5120             return;
5121         }
5122         
5123         this.badgeEl.dom.innerHTML = str;
5124     }
5125     
5126    
5127      
5128  
5129 });
5130  
5131
5132  /*
5133  * - LGPL
5134  *
5135  * row
5136  * 
5137  */
5138
5139 /**
5140  * @class Roo.bootstrap.Row
5141  * @extends Roo.bootstrap.Component
5142  * Bootstrap Row class (contains columns...)
5143  * 
5144  * @constructor
5145  * Create a new Row
5146  * @param {Object} config The config object
5147  */
5148
5149 Roo.bootstrap.Row = function(config){
5150     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5151 };
5152
5153 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5154     
5155     getAutoCreate : function(){
5156        return {
5157             cls: 'row clearfix'
5158        };
5159     }
5160     
5161     
5162 });
5163
5164  
5165
5166  /*
5167  * - LGPL
5168  *
5169  * element
5170  * 
5171  */
5172
5173 /**
5174  * @class Roo.bootstrap.Element
5175  * @extends Roo.bootstrap.Component
5176  * Bootstrap Element class
5177  * @cfg {String} html contents of the element
5178  * @cfg {String} tag tag of the element
5179  * @cfg {String} cls class of the element
5180  * @cfg {Boolean} preventDefault (true|false) default false
5181  * @cfg {Boolean} clickable (true|false) default false
5182  * 
5183  * @constructor
5184  * Create a new Element
5185  * @param {Object} config The config object
5186  */
5187
5188 Roo.bootstrap.Element = function(config){
5189     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5190     
5191     this.addEvents({
5192         // raw events
5193         /**
5194          * @event click
5195          * When a element is chick
5196          * @param {Roo.bootstrap.Element} this
5197          * @param {Roo.EventObject} e
5198          */
5199         "click" : true
5200     });
5201 };
5202
5203 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5204     
5205     tag: 'div',
5206     cls: '',
5207     html: '',
5208     preventDefault: false, 
5209     clickable: false,
5210     
5211     getAutoCreate : function(){
5212         
5213         var cfg = {
5214             tag: this.tag,
5215             // cls: this.cls, double assign in parent class Component.js :: onRender
5216             html: this.html
5217         };
5218         
5219         return cfg;
5220     },
5221     
5222     initEvents: function() 
5223     {
5224         Roo.bootstrap.Element.superclass.initEvents.call(this);
5225         
5226         if(this.clickable){
5227             this.el.on('click', this.onClick, this);
5228         }
5229         
5230     },
5231     
5232     onClick : function(e)
5233     {
5234         if(this.preventDefault){
5235             e.preventDefault();
5236         }
5237         
5238         this.fireEvent('click', this, e);
5239     },
5240     
5241     getValue : function()
5242     {
5243         return this.el.dom.innerHTML;
5244     },
5245     
5246     setValue : function(value)
5247     {
5248         this.el.dom.innerHTML = value;
5249     }
5250    
5251 });
5252
5253  
5254
5255  /*
5256  * - LGPL
5257  *
5258  * pagination
5259  * 
5260  */
5261
5262 /**
5263  * @class Roo.bootstrap.Pagination
5264  * @extends Roo.bootstrap.Component
5265  * Bootstrap Pagination class
5266  * @cfg {String} size xs | sm | md | lg
5267  * @cfg {Boolean} inverse false | true
5268  * 
5269  * @constructor
5270  * Create a new Pagination
5271  * @param {Object} config The config object
5272  */
5273
5274 Roo.bootstrap.Pagination = function(config){
5275     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5276 };
5277
5278 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5279     
5280     cls: false,
5281     size: false,
5282     inverse: false,
5283     
5284     getAutoCreate : function(){
5285         var cfg = {
5286             tag: 'ul',
5287                 cls: 'pagination'
5288         };
5289         if (this.inverse) {
5290             cfg.cls += ' inverse';
5291         }
5292         if (this.html) {
5293             cfg.html=this.html;
5294         }
5295         if (this.cls) {
5296             cfg.cls += " " + this.cls;
5297         }
5298         return cfg;
5299     }
5300    
5301 });
5302
5303  
5304
5305  /*
5306  * - LGPL
5307  *
5308  * Pagination item
5309  * 
5310  */
5311
5312
5313 /**
5314  * @class Roo.bootstrap.PaginationItem
5315  * @extends Roo.bootstrap.Component
5316  * Bootstrap PaginationItem class
5317  * @cfg {String} html text
5318  * @cfg {String} href the link
5319  * @cfg {Boolean} preventDefault (true | false) default true
5320  * @cfg {Boolean} active (true | false) default false
5321  * @cfg {Boolean} disabled default false
5322  * 
5323  * 
5324  * @constructor
5325  * Create a new PaginationItem
5326  * @param {Object} config The config object
5327  */
5328
5329
5330 Roo.bootstrap.PaginationItem = function(config){
5331     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5332     this.addEvents({
5333         // raw events
5334         /**
5335          * @event click
5336          * The raw click event for the entire grid.
5337          * @param {Roo.EventObject} e
5338          */
5339         "click" : true
5340     });
5341 };
5342
5343 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5344     
5345     href : false,
5346     html : false,
5347     preventDefault: true,
5348     active : false,
5349     cls : false,
5350     disabled: false,
5351     
5352     getAutoCreate : function(){
5353         var cfg= {
5354             tag: 'li',
5355             cn: [
5356                 {
5357                     tag : 'a',
5358                     href : this.href ? this.href : '#',
5359                     html : this.html ? this.html : ''
5360                 }
5361             ]
5362         };
5363         
5364         if(this.cls){
5365             cfg.cls = this.cls;
5366         }
5367         
5368         if(this.disabled){
5369             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5370         }
5371         
5372         if(this.active){
5373             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5374         }
5375         
5376         return cfg;
5377     },
5378     
5379     initEvents: function() {
5380         
5381         this.el.on('click', this.onClick, this);
5382         
5383     },
5384     onClick : function(e)
5385     {
5386         Roo.log('PaginationItem on click ');
5387         if(this.preventDefault){
5388             e.preventDefault();
5389         }
5390         
5391         if(this.disabled){
5392             return;
5393         }
5394         
5395         this.fireEvent('click', this, e);
5396     }
5397    
5398 });
5399
5400  
5401
5402  /*
5403  * - LGPL
5404  *
5405  * slider
5406  * 
5407  */
5408
5409
5410 /**
5411  * @class Roo.bootstrap.Slider
5412  * @extends Roo.bootstrap.Component
5413  * Bootstrap Slider class
5414  *    
5415  * @constructor
5416  * Create a new Slider
5417  * @param {Object} config The config object
5418  */
5419
5420 Roo.bootstrap.Slider = function(config){
5421     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5422 };
5423
5424 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5425     
5426     getAutoCreate : function(){
5427         
5428         var cfg = {
5429             tag: 'div',
5430             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5431             cn: [
5432                 {
5433                     tag: 'a',
5434                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5435                 }
5436             ]
5437         };
5438         
5439         return cfg;
5440     }
5441    
5442 });
5443
5444  /*
5445  * Based on:
5446  * Ext JS Library 1.1.1
5447  * Copyright(c) 2006-2007, Ext JS, LLC.
5448  *
5449  * Originally Released Under LGPL - original licence link has changed is not relivant.
5450  *
5451  * Fork - LGPL
5452  * <script type="text/javascript">
5453  */
5454  
5455
5456 /**
5457  * @class Roo.grid.ColumnModel
5458  * @extends Roo.util.Observable
5459  * This is the default implementation of a ColumnModel used by the Grid. It defines
5460  * the columns in the grid.
5461  * <br>Usage:<br>
5462  <pre><code>
5463  var colModel = new Roo.grid.ColumnModel([
5464         {header: "Ticker", width: 60, sortable: true, locked: true},
5465         {header: "Company Name", width: 150, sortable: true},
5466         {header: "Market Cap.", width: 100, sortable: true},
5467         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5468         {header: "Employees", width: 100, sortable: true, resizable: false}
5469  ]);
5470  </code></pre>
5471  * <p>
5472  
5473  * The config options listed for this class are options which may appear in each
5474  * individual column definition.
5475  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5476  * @constructor
5477  * @param {Object} config An Array of column config objects. See this class's
5478  * config objects for details.
5479 */
5480 Roo.grid.ColumnModel = function(config){
5481         /**
5482      * The config passed into the constructor
5483      */
5484     this.config = config;
5485     this.lookup = {};
5486
5487     // if no id, create one
5488     // if the column does not have a dataIndex mapping,
5489     // map it to the order it is in the config
5490     for(var i = 0, len = config.length; i < len; i++){
5491         var c = config[i];
5492         if(typeof c.dataIndex == "undefined"){
5493             c.dataIndex = i;
5494         }
5495         if(typeof c.renderer == "string"){
5496             c.renderer = Roo.util.Format[c.renderer];
5497         }
5498         if(typeof c.id == "undefined"){
5499             c.id = Roo.id();
5500         }
5501         if(c.editor && c.editor.xtype){
5502             c.editor  = Roo.factory(c.editor, Roo.grid);
5503         }
5504         if(c.editor && c.editor.isFormField){
5505             c.editor = new Roo.grid.GridEditor(c.editor);
5506         }
5507         this.lookup[c.id] = c;
5508     }
5509
5510     /**
5511      * The width of columns which have no width specified (defaults to 100)
5512      * @type Number
5513      */
5514     this.defaultWidth = 100;
5515
5516     /**
5517      * Default sortable of columns which have no sortable specified (defaults to false)
5518      * @type Boolean
5519      */
5520     this.defaultSortable = false;
5521
5522     this.addEvents({
5523         /**
5524              * @event widthchange
5525              * Fires when the width of a column changes.
5526              * @param {ColumnModel} this
5527              * @param {Number} columnIndex The column index
5528              * @param {Number} newWidth The new width
5529              */
5530             "widthchange": true,
5531         /**
5532              * @event headerchange
5533              * Fires when the text of a header changes.
5534              * @param {ColumnModel} this
5535              * @param {Number} columnIndex The column index
5536              * @param {Number} newText The new header text
5537              */
5538             "headerchange": true,
5539         /**
5540              * @event hiddenchange
5541              * Fires when a column is hidden or "unhidden".
5542              * @param {ColumnModel} this
5543              * @param {Number} columnIndex The column index
5544              * @param {Boolean} hidden true if hidden, false otherwise
5545              */
5546             "hiddenchange": true,
5547             /**
5548          * @event columnmoved
5549          * Fires when a column is moved.
5550          * @param {ColumnModel} this
5551          * @param {Number} oldIndex
5552          * @param {Number} newIndex
5553          */
5554         "columnmoved" : true,
5555         /**
5556          * @event columlockchange
5557          * Fires when a column's locked state is changed
5558          * @param {ColumnModel} this
5559          * @param {Number} colIndex
5560          * @param {Boolean} locked true if locked
5561          */
5562         "columnlockchange" : true
5563     });
5564     Roo.grid.ColumnModel.superclass.constructor.call(this);
5565 };
5566 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5567     /**
5568      * @cfg {String} header The header text to display in the Grid view.
5569      */
5570     /**
5571      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5572      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5573      * specified, the column's index is used as an index into the Record's data Array.
5574      */
5575     /**
5576      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5577      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5578      */
5579     /**
5580      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5581      * Defaults to the value of the {@link #defaultSortable} property.
5582      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5583      */
5584     /**
5585      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5586      */
5587     /**
5588      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5589      */
5590     /**
5591      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5592      */
5593     /**
5594      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5595      */
5596     /**
5597      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5598      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5599      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5600      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5601      */
5602        /**
5603      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5604      */
5605     /**
5606      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5607      */
5608     /**
5609      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5610      */
5611     /**
5612      * @cfg {String} cursor (Optional)
5613      */
5614     /**
5615      * @cfg {String} tooltip (Optional)
5616      */
5617     /**
5618      * @cfg {Number} xs (Optional)
5619      */
5620     /**
5621      * @cfg {Number} sm (Optional)
5622      */
5623     /**
5624      * @cfg {Number} md (Optional)
5625      */
5626     /**
5627      * @cfg {Number} lg (Optional)
5628      */
5629     /**
5630      * Returns the id of the column at the specified index.
5631      * @param {Number} index The column index
5632      * @return {String} the id
5633      */
5634     getColumnId : function(index){
5635         return this.config[index].id;
5636     },
5637
5638     /**
5639      * Returns the column for a specified id.
5640      * @param {String} id The column id
5641      * @return {Object} the column
5642      */
5643     getColumnById : function(id){
5644         return this.lookup[id];
5645     },
5646
5647     
5648     /**
5649      * Returns the column for a specified dataIndex.
5650      * @param {String} dataIndex The column dataIndex
5651      * @return {Object|Boolean} the column or false if not found
5652      */
5653     getColumnByDataIndex: function(dataIndex){
5654         var index = this.findColumnIndex(dataIndex);
5655         return index > -1 ? this.config[index] : false;
5656     },
5657     
5658     /**
5659      * Returns the index for a specified column id.
5660      * @param {String} id The column id
5661      * @return {Number} the index, or -1 if not found
5662      */
5663     getIndexById : function(id){
5664         for(var i = 0, len = this.config.length; i < len; i++){
5665             if(this.config[i].id == id){
5666                 return i;
5667             }
5668         }
5669         return -1;
5670     },
5671     
5672     /**
5673      * Returns the index for a specified column dataIndex.
5674      * @param {String} dataIndex The column dataIndex
5675      * @return {Number} the index, or -1 if not found
5676      */
5677     
5678     findColumnIndex : function(dataIndex){
5679         for(var i = 0, len = this.config.length; i < len; i++){
5680             if(this.config[i].dataIndex == dataIndex){
5681                 return i;
5682             }
5683         }
5684         return -1;
5685     },
5686     
5687     
5688     moveColumn : function(oldIndex, newIndex){
5689         var c = this.config[oldIndex];
5690         this.config.splice(oldIndex, 1);
5691         this.config.splice(newIndex, 0, c);
5692         this.dataMap = null;
5693         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5694     },
5695
5696     isLocked : function(colIndex){
5697         return this.config[colIndex].locked === true;
5698     },
5699
5700     setLocked : function(colIndex, value, suppressEvent){
5701         if(this.isLocked(colIndex) == value){
5702             return;
5703         }
5704         this.config[colIndex].locked = value;
5705         if(!suppressEvent){
5706             this.fireEvent("columnlockchange", this, colIndex, value);
5707         }
5708     },
5709
5710     getTotalLockedWidth : function(){
5711         var totalWidth = 0;
5712         for(var i = 0; i < this.config.length; i++){
5713             if(this.isLocked(i) && !this.isHidden(i)){
5714                 this.totalWidth += this.getColumnWidth(i);
5715             }
5716         }
5717         return totalWidth;
5718     },
5719
5720     getLockedCount : function(){
5721         for(var i = 0, len = this.config.length; i < len; i++){
5722             if(!this.isLocked(i)){
5723                 return i;
5724             }
5725         }
5726         
5727         return this.config.length;
5728     },
5729
5730     /**
5731      * Returns the number of columns.
5732      * @return {Number}
5733      */
5734     getColumnCount : function(visibleOnly){
5735         if(visibleOnly === true){
5736             var c = 0;
5737             for(var i = 0, len = this.config.length; i < len; i++){
5738                 if(!this.isHidden(i)){
5739                     c++;
5740                 }
5741             }
5742             return c;
5743         }
5744         return this.config.length;
5745     },
5746
5747     /**
5748      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5749      * @param {Function} fn
5750      * @param {Object} scope (optional)
5751      * @return {Array} result
5752      */
5753     getColumnsBy : function(fn, scope){
5754         var r = [];
5755         for(var i = 0, len = this.config.length; i < len; i++){
5756             var c = this.config[i];
5757             if(fn.call(scope||this, c, i) === true){
5758                 r[r.length] = c;
5759             }
5760         }
5761         return r;
5762     },
5763
5764     /**
5765      * Returns true if the specified column is sortable.
5766      * @param {Number} col The column index
5767      * @return {Boolean}
5768      */
5769     isSortable : function(col){
5770         if(typeof this.config[col].sortable == "undefined"){
5771             return this.defaultSortable;
5772         }
5773         return this.config[col].sortable;
5774     },
5775
5776     /**
5777      * Returns the rendering (formatting) function defined for the column.
5778      * @param {Number} col The column index.
5779      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5780      */
5781     getRenderer : function(col){
5782         if(!this.config[col].renderer){
5783             return Roo.grid.ColumnModel.defaultRenderer;
5784         }
5785         return this.config[col].renderer;
5786     },
5787
5788     /**
5789      * Sets the rendering (formatting) function for a column.
5790      * @param {Number} col The column index
5791      * @param {Function} fn The function to use to process the cell's raw data
5792      * to return HTML markup for the grid view. The render function is called with
5793      * the following parameters:<ul>
5794      * <li>Data value.</li>
5795      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5796      * <li>css A CSS style string to apply to the table cell.</li>
5797      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5798      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5799      * <li>Row index</li>
5800      * <li>Column index</li>
5801      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5802      */
5803     setRenderer : function(col, fn){
5804         this.config[col].renderer = fn;
5805     },
5806
5807     /**
5808      * Returns the width for the specified column.
5809      * @param {Number} col The column index
5810      * @return {Number}
5811      */
5812     getColumnWidth : function(col){
5813         return this.config[col].width * 1 || this.defaultWidth;
5814     },
5815
5816     /**
5817      * Sets the width for a column.
5818      * @param {Number} col The column index
5819      * @param {Number} width The new width
5820      */
5821     setColumnWidth : function(col, width, suppressEvent){
5822         this.config[col].width = width;
5823         this.totalWidth = null;
5824         if(!suppressEvent){
5825              this.fireEvent("widthchange", this, col, width);
5826         }
5827     },
5828
5829     /**
5830      * Returns the total width of all columns.
5831      * @param {Boolean} includeHidden True to include hidden column widths
5832      * @return {Number}
5833      */
5834     getTotalWidth : function(includeHidden){
5835         if(!this.totalWidth){
5836             this.totalWidth = 0;
5837             for(var i = 0, len = this.config.length; i < len; i++){
5838                 if(includeHidden || !this.isHidden(i)){
5839                     this.totalWidth += this.getColumnWidth(i);
5840                 }
5841             }
5842         }
5843         return this.totalWidth;
5844     },
5845
5846     /**
5847      * Returns the header for the specified column.
5848      * @param {Number} col The column index
5849      * @return {String}
5850      */
5851     getColumnHeader : function(col){
5852         return this.config[col].header;
5853     },
5854
5855     /**
5856      * Sets the header for a column.
5857      * @param {Number} col The column index
5858      * @param {String} header The new header
5859      */
5860     setColumnHeader : function(col, header){
5861         this.config[col].header = header;
5862         this.fireEvent("headerchange", this, col, header);
5863     },
5864
5865     /**
5866      * Returns the tooltip for the specified column.
5867      * @param {Number} col The column index
5868      * @return {String}
5869      */
5870     getColumnTooltip : function(col){
5871             return this.config[col].tooltip;
5872     },
5873     /**
5874      * Sets the tooltip for a column.
5875      * @param {Number} col The column index
5876      * @param {String} tooltip The new tooltip
5877      */
5878     setColumnTooltip : function(col, tooltip){
5879             this.config[col].tooltip = tooltip;
5880     },
5881
5882     /**
5883      * Returns the dataIndex for the specified column.
5884      * @param {Number} col The column index
5885      * @return {Number}
5886      */
5887     getDataIndex : function(col){
5888         return this.config[col].dataIndex;
5889     },
5890
5891     /**
5892      * Sets the dataIndex for a column.
5893      * @param {Number} col The column index
5894      * @param {Number} dataIndex The new dataIndex
5895      */
5896     setDataIndex : function(col, dataIndex){
5897         this.config[col].dataIndex = dataIndex;
5898     },
5899
5900     
5901     
5902     /**
5903      * Returns true if the cell is editable.
5904      * @param {Number} colIndex The column index
5905      * @param {Number} rowIndex The row index - this is nto actually used..?
5906      * @return {Boolean}
5907      */
5908     isCellEditable : function(colIndex, rowIndex){
5909         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5910     },
5911
5912     /**
5913      * Returns the editor defined for the cell/column.
5914      * return false or null to disable editing.
5915      * @param {Number} colIndex The column index
5916      * @param {Number} rowIndex The row index
5917      * @return {Object}
5918      */
5919     getCellEditor : function(colIndex, rowIndex){
5920         return this.config[colIndex].editor;
5921     },
5922
5923     /**
5924      * Sets if a column is editable.
5925      * @param {Number} col The column index
5926      * @param {Boolean} editable True if the column is editable
5927      */
5928     setEditable : function(col, editable){
5929         this.config[col].editable = editable;
5930     },
5931
5932
5933     /**
5934      * Returns true if the column is hidden.
5935      * @param {Number} colIndex The column index
5936      * @return {Boolean}
5937      */
5938     isHidden : function(colIndex){
5939         return this.config[colIndex].hidden;
5940     },
5941
5942
5943     /**
5944      * Returns true if the column width cannot be changed
5945      */
5946     isFixed : function(colIndex){
5947         return this.config[colIndex].fixed;
5948     },
5949
5950     /**
5951      * Returns true if the column can be resized
5952      * @return {Boolean}
5953      */
5954     isResizable : function(colIndex){
5955         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5956     },
5957     /**
5958      * Sets if a column is hidden.
5959      * @param {Number} colIndex The column index
5960      * @param {Boolean} hidden True if the column is hidden
5961      */
5962     setHidden : function(colIndex, hidden){
5963         this.config[colIndex].hidden = hidden;
5964         this.totalWidth = null;
5965         this.fireEvent("hiddenchange", this, colIndex, hidden);
5966     },
5967
5968     /**
5969      * Sets the editor for a column.
5970      * @param {Number} col The column index
5971      * @param {Object} editor The editor object
5972      */
5973     setEditor : function(col, editor){
5974         this.config[col].editor = editor;
5975     }
5976 });
5977
5978 Roo.grid.ColumnModel.defaultRenderer = function(value)
5979 {
5980     if(typeof value == "object") {
5981         return value;
5982     }
5983         if(typeof value == "string" && value.length < 1){
5984             return "&#160;";
5985         }
5986     
5987         return String.format("{0}", value);
5988 };
5989
5990 // Alias for backwards compatibility
5991 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5992 /*
5993  * Based on:
5994  * Ext JS Library 1.1.1
5995  * Copyright(c) 2006-2007, Ext JS, LLC.
5996  *
5997  * Originally Released Under LGPL - original licence link has changed is not relivant.
5998  *
5999  * Fork - LGPL
6000  * <script type="text/javascript">
6001  */
6002  
6003 /**
6004  * @class Roo.LoadMask
6005  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6006  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6007  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6008  * element's UpdateManager load indicator and will be destroyed after the initial load.
6009  * @constructor
6010  * Create a new LoadMask
6011  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6012  * @param {Object} config The config object
6013  */
6014 Roo.LoadMask = function(el, config){
6015     this.el = Roo.get(el);
6016     Roo.apply(this, config);
6017     if(this.store){
6018         this.store.on('beforeload', this.onBeforeLoad, this);
6019         this.store.on('load', this.onLoad, this);
6020         this.store.on('loadexception', this.onLoadException, this);
6021         this.removeMask = false;
6022     }else{
6023         var um = this.el.getUpdateManager();
6024         um.showLoadIndicator = false; // disable the default indicator
6025         um.on('beforeupdate', this.onBeforeLoad, this);
6026         um.on('update', this.onLoad, this);
6027         um.on('failure', this.onLoad, this);
6028         this.removeMask = true;
6029     }
6030 };
6031
6032 Roo.LoadMask.prototype = {
6033     /**
6034      * @cfg {Boolean} removeMask
6035      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6036      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6037      */
6038     /**
6039      * @cfg {String} msg
6040      * The text to display in a centered loading message box (defaults to 'Loading...')
6041      */
6042     msg : 'Loading...',
6043     /**
6044      * @cfg {String} msgCls
6045      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6046      */
6047     msgCls : 'x-mask-loading',
6048
6049     /**
6050      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6051      * @type Boolean
6052      */
6053     disabled: false,
6054
6055     /**
6056      * Disables the mask to prevent it from being displayed
6057      */
6058     disable : function(){
6059        this.disabled = true;
6060     },
6061
6062     /**
6063      * Enables the mask so that it can be displayed
6064      */
6065     enable : function(){
6066         this.disabled = false;
6067     },
6068     
6069     onLoadException : function()
6070     {
6071         Roo.log(arguments);
6072         
6073         if (typeof(arguments[3]) != 'undefined') {
6074             Roo.MessageBox.alert("Error loading",arguments[3]);
6075         } 
6076         /*
6077         try {
6078             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6079                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6080             }   
6081         } catch(e) {
6082             
6083         }
6084         */
6085     
6086         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6087     },
6088     // private
6089     onLoad : function()
6090     {
6091         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6092     },
6093
6094     // private
6095     onBeforeLoad : function(){
6096         if(!this.disabled){
6097             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6098         }
6099     },
6100
6101     // private
6102     destroy : function(){
6103         if(this.store){
6104             this.store.un('beforeload', this.onBeforeLoad, this);
6105             this.store.un('load', this.onLoad, this);
6106             this.store.un('loadexception', this.onLoadException, this);
6107         }else{
6108             var um = this.el.getUpdateManager();
6109             um.un('beforeupdate', this.onBeforeLoad, this);
6110             um.un('update', this.onLoad, this);
6111             um.un('failure', this.onLoad, this);
6112         }
6113     }
6114 };/*
6115  * - LGPL
6116  *
6117  * table
6118  * 
6119  */
6120
6121 /**
6122  * @class Roo.bootstrap.Table
6123  * @extends Roo.bootstrap.Component
6124  * Bootstrap Table class
6125  * @cfg {String} cls table class
6126  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6127  * @cfg {String} bgcolor Specifies the background color for a table
6128  * @cfg {Number} border Specifies whether the table cells should have borders or not
6129  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6130  * @cfg {Number} cellspacing Specifies the space between cells
6131  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6132  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6133  * @cfg {String} sortable Specifies that the table should be sortable
6134  * @cfg {String} summary Specifies a summary of the content of a table
6135  * @cfg {Number} width Specifies the width of a table
6136  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6137  * 
6138  * @cfg {boolean} striped Should the rows be alternative striped
6139  * @cfg {boolean} bordered Add borders to the table
6140  * @cfg {boolean} hover Add hover highlighting
6141  * @cfg {boolean} condensed Format condensed
6142  * @cfg {boolean} responsive Format condensed
6143  * @cfg {Boolean} loadMask (true|false) default false
6144  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6145  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6146  * @cfg {Boolean} rowSelection (true|false) default false
6147  * @cfg {Boolean} cellSelection (true|false) default false
6148  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6149  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6150  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6151  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6152  
6153  * 
6154  * @constructor
6155  * Create a new Table
6156  * @param {Object} config The config object
6157  */
6158
6159 Roo.bootstrap.Table = function(config){
6160     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6161     
6162   
6163     
6164     // BC...
6165     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6166     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6167     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6168     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6169     
6170     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6171     if (this.sm) {
6172         this.sm.grid = this;
6173         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6174         this.sm = this.selModel;
6175         this.sm.xmodule = this.xmodule || false;
6176     }
6177     
6178     if (this.cm && typeof(this.cm.config) == 'undefined') {
6179         this.colModel = new Roo.grid.ColumnModel(this.cm);
6180         this.cm = this.colModel;
6181         this.cm.xmodule = this.xmodule || false;
6182     }
6183     if (this.store) {
6184         this.store= Roo.factory(this.store, Roo.data);
6185         this.ds = this.store;
6186         this.ds.xmodule = this.xmodule || false;
6187          
6188     }
6189     if (this.footer && this.store) {
6190         this.footer.dataSource = this.ds;
6191         this.footer = Roo.factory(this.footer);
6192     }
6193     
6194     /** @private */
6195     this.addEvents({
6196         /**
6197          * @event cellclick
6198          * Fires when a cell is clicked
6199          * @param {Roo.bootstrap.Table} this
6200          * @param {Roo.Element} el
6201          * @param {Number} rowIndex
6202          * @param {Number} columnIndex
6203          * @param {Roo.EventObject} e
6204          */
6205         "cellclick" : true,
6206         /**
6207          * @event celldblclick
6208          * Fires when a cell is double clicked
6209          * @param {Roo.bootstrap.Table} this
6210          * @param {Roo.Element} el
6211          * @param {Number} rowIndex
6212          * @param {Number} columnIndex
6213          * @param {Roo.EventObject} e
6214          */
6215         "celldblclick" : true,
6216         /**
6217          * @event rowclick
6218          * Fires when a row is clicked
6219          * @param {Roo.bootstrap.Table} this
6220          * @param {Roo.Element} el
6221          * @param {Number} rowIndex
6222          * @param {Roo.EventObject} e
6223          */
6224         "rowclick" : true,
6225         /**
6226          * @event rowdblclick
6227          * Fires when a row is double clicked
6228          * @param {Roo.bootstrap.Table} this
6229          * @param {Roo.Element} el
6230          * @param {Number} rowIndex
6231          * @param {Roo.EventObject} e
6232          */
6233         "rowdblclick" : true,
6234         /**
6235          * @event mouseover
6236          * Fires when a mouseover occur
6237          * @param {Roo.bootstrap.Table} this
6238          * @param {Roo.Element} el
6239          * @param {Number} rowIndex
6240          * @param {Number} columnIndex
6241          * @param {Roo.EventObject} e
6242          */
6243         "mouseover" : true,
6244         /**
6245          * @event mouseout
6246          * Fires when a mouseout occur
6247          * @param {Roo.bootstrap.Table} this
6248          * @param {Roo.Element} el
6249          * @param {Number} rowIndex
6250          * @param {Number} columnIndex
6251          * @param {Roo.EventObject} e
6252          */
6253         "mouseout" : true,
6254         /**
6255          * @event rowclass
6256          * Fires when a row is rendered, so you can change add a style to it.
6257          * @param {Roo.bootstrap.Table} this
6258          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6259          */
6260         'rowclass' : true,
6261           /**
6262          * @event rowsrendered
6263          * Fires when all the  rows have been rendered
6264          * @param {Roo.bootstrap.Table} this
6265          */
6266         'rowsrendered' : true,
6267         /**
6268          * @event contextmenu
6269          * The raw contextmenu event for the entire grid.
6270          * @param {Roo.EventObject} e
6271          */
6272         "contextmenu" : true,
6273         /**
6274          * @event rowcontextmenu
6275          * Fires when a row is right clicked
6276          * @param {Roo.bootstrap.Table} this
6277          * @param {Number} rowIndex
6278          * @param {Roo.EventObject} e
6279          */
6280         "rowcontextmenu" : true,
6281         /**
6282          * @event cellcontextmenu
6283          * Fires when a cell is right clicked
6284          * @param {Roo.bootstrap.Table} this
6285          * @param {Number} rowIndex
6286          * @param {Number} cellIndex
6287          * @param {Roo.EventObject} e
6288          */
6289          "cellcontextmenu" : true,
6290          /**
6291          * @event headercontextmenu
6292          * Fires when a header is right clicked
6293          * @param {Roo.bootstrap.Table} this
6294          * @param {Number} columnIndex
6295          * @param {Roo.EventObject} e
6296          */
6297         "headercontextmenu" : true
6298     });
6299 };
6300
6301 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6302     
6303     cls: false,
6304     align: false,
6305     bgcolor: false,
6306     border: false,
6307     cellpadding: false,
6308     cellspacing: false,
6309     frame: false,
6310     rules: false,
6311     sortable: false,
6312     summary: false,
6313     width: false,
6314     striped : false,
6315     scrollBody : false,
6316     bordered: false,
6317     hover:  false,
6318     condensed : false,
6319     responsive : false,
6320     sm : false,
6321     cm : false,
6322     store : false,
6323     loadMask : false,
6324     footerShow : true,
6325     headerShow : true,
6326   
6327     rowSelection : false,
6328     cellSelection : false,
6329     layout : false,
6330     
6331     // Roo.Element - the tbody
6332     mainBody: false,
6333     // Roo.Element - thead element
6334     mainHead: false,
6335     
6336     container: false, // used by gridpanel...
6337     
6338     lazyLoad : false,
6339     
6340     CSS : Roo.util.CSS,
6341     
6342     auto_hide_footer : false,
6343     
6344     getAutoCreate : function()
6345     {
6346         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6347         
6348         cfg = {
6349             tag: 'table',
6350             cls : 'table',
6351             cn : []
6352         };
6353         if (this.scrollBody) {
6354             cfg.cls += ' table-body-fixed';
6355         }    
6356         if (this.striped) {
6357             cfg.cls += ' table-striped';
6358         }
6359         
6360         if (this.hover) {
6361             cfg.cls += ' table-hover';
6362         }
6363         if (this.bordered) {
6364             cfg.cls += ' table-bordered';
6365         }
6366         if (this.condensed) {
6367             cfg.cls += ' table-condensed';
6368         }
6369         if (this.responsive) {
6370             cfg.cls += ' table-responsive';
6371         }
6372         
6373         if (this.cls) {
6374             cfg.cls+=  ' ' +this.cls;
6375         }
6376         
6377         // this lot should be simplifed...
6378         var _t = this;
6379         var cp = [
6380             'align',
6381             'bgcolor',
6382             'border',
6383             'cellpadding',
6384             'cellspacing',
6385             'frame',
6386             'rules',
6387             'sortable',
6388             'summary',
6389             'width'
6390         ].forEach(function(k) {
6391             if (_t[k]) {
6392                 cfg[k] = _t[k];
6393             }
6394         });
6395         
6396         
6397         if (this.layout) {
6398             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6399         }
6400         
6401         if(this.store || this.cm){
6402             if(this.headerShow){
6403                 cfg.cn.push(this.renderHeader());
6404             }
6405             
6406             cfg.cn.push(this.renderBody());
6407             
6408             if(this.footerShow){
6409                 cfg.cn.push(this.renderFooter());
6410             }
6411             // where does this come from?
6412             //cfg.cls+=  ' TableGrid';
6413         }
6414         
6415         return { cn : [ cfg ] };
6416     },
6417     
6418     initEvents : function()
6419     {   
6420         if(!this.store || !this.cm){
6421             return;
6422         }
6423         if (this.selModel) {
6424             this.selModel.initEvents();
6425         }
6426         
6427         
6428         //Roo.log('initEvents with ds!!!!');
6429         
6430         this.mainBody = this.el.select('tbody', true).first();
6431         this.mainHead = this.el.select('thead', true).first();
6432         this.mainFoot = this.el.select('tfoot', true).first();
6433         
6434         
6435         
6436         var _this = this;
6437         
6438         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6439             e.on('click', _this.sort, _this);
6440         });
6441         
6442         this.mainBody.on("click", this.onClick, this);
6443         this.mainBody.on("dblclick", this.onDblClick, this);
6444         
6445         // why is this done????? = it breaks dialogs??
6446         //this.parent().el.setStyle('position', 'relative');
6447         
6448         
6449         if (this.footer) {
6450             this.footer.parentId = this.id;
6451             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6452             
6453             if(this.lazyLoad){
6454                 this.el.select('tfoot tr td').first().addClass('hide');
6455             }
6456         } 
6457         
6458         if(this.loadMask) {
6459             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6460         }
6461         
6462         this.store.on('load', this.onLoad, this);
6463         this.store.on('beforeload', this.onBeforeLoad, this);
6464         this.store.on('update', this.onUpdate, this);
6465         this.store.on('add', this.onAdd, this);
6466         this.store.on("clear", this.clear, this);
6467         
6468         this.el.on("contextmenu", this.onContextMenu, this);
6469         
6470         this.mainBody.on('scroll', this.onBodyScroll, this);
6471         
6472         this.cm.on("headerchange", this.onHeaderChange, this);
6473         
6474         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6475         
6476     },
6477     
6478     onContextMenu : function(e, t)
6479     {
6480         this.processEvent("contextmenu", e);
6481     },
6482     
6483     processEvent : function(name, e)
6484     {
6485         if (name != 'touchstart' ) {
6486             this.fireEvent(name, e);    
6487         }
6488         
6489         var t = e.getTarget();
6490         
6491         var cell = Roo.get(t);
6492         
6493         if(!cell){
6494             return;
6495         }
6496         
6497         if(cell.findParent('tfoot', false, true)){
6498             return;
6499         }
6500         
6501         if(cell.findParent('thead', false, true)){
6502             
6503             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6504                 cell = Roo.get(t).findParent('th', false, true);
6505                 if (!cell) {
6506                     Roo.log("failed to find th in thead?");
6507                     Roo.log(e.getTarget());
6508                     return;
6509                 }
6510             }
6511             
6512             var cellIndex = cell.dom.cellIndex;
6513             
6514             var ename = name == 'touchstart' ? 'click' : name;
6515             this.fireEvent("header" + ename, this, cellIndex, e);
6516             
6517             return;
6518         }
6519         
6520         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6521             cell = Roo.get(t).findParent('td', false, true);
6522             if (!cell) {
6523                 Roo.log("failed to find th in tbody?");
6524                 Roo.log(e.getTarget());
6525                 return;
6526             }
6527         }
6528         
6529         var row = cell.findParent('tr', false, true);
6530         var cellIndex = cell.dom.cellIndex;
6531         var rowIndex = row.dom.rowIndex - 1;
6532         
6533         if(row !== false){
6534             
6535             this.fireEvent("row" + name, this, rowIndex, e);
6536             
6537             if(cell !== false){
6538             
6539                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6540             }
6541         }
6542         
6543     },
6544     
6545     onMouseover : function(e, el)
6546     {
6547         var cell = Roo.get(el);
6548         
6549         if(!cell){
6550             return;
6551         }
6552         
6553         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6554             cell = cell.findParent('td', false, true);
6555         }
6556         
6557         var row = cell.findParent('tr', false, true);
6558         var cellIndex = cell.dom.cellIndex;
6559         var rowIndex = row.dom.rowIndex - 1; // start from 0
6560         
6561         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6562         
6563     },
6564     
6565     onMouseout : function(e, el)
6566     {
6567         var cell = Roo.get(el);
6568         
6569         if(!cell){
6570             return;
6571         }
6572         
6573         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6574             cell = cell.findParent('td', false, true);
6575         }
6576         
6577         var row = cell.findParent('tr', false, true);
6578         var cellIndex = cell.dom.cellIndex;
6579         var rowIndex = row.dom.rowIndex - 1; // start from 0
6580         
6581         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6582         
6583     },
6584     
6585     onClick : function(e, el)
6586     {
6587         var cell = Roo.get(el);
6588         
6589         if(!cell || (!this.cellSelection && !this.rowSelection)){
6590             return;
6591         }
6592         
6593         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6594             cell = cell.findParent('td', false, true);
6595         }
6596         
6597         if(!cell || typeof(cell) == 'undefined'){
6598             return;
6599         }
6600         
6601         var row = cell.findParent('tr', false, true);
6602         
6603         if(!row || typeof(row) == 'undefined'){
6604             return;
6605         }
6606         
6607         var cellIndex = cell.dom.cellIndex;
6608         var rowIndex = this.getRowIndex(row);
6609         
6610         // why??? - should these not be based on SelectionModel?
6611         if(this.cellSelection){
6612             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6613         }
6614         
6615         if(this.rowSelection){
6616             this.fireEvent('rowclick', this, row, rowIndex, e);
6617         }
6618         
6619         
6620     },
6621         
6622     onDblClick : function(e,el)
6623     {
6624         var cell = Roo.get(el);
6625         
6626         if(!cell || (!this.cellSelection && !this.rowSelection)){
6627             return;
6628         }
6629         
6630         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6631             cell = cell.findParent('td', false, true);
6632         }
6633         
6634         if(!cell || typeof(cell) == 'undefined'){
6635             return;
6636         }
6637         
6638         var row = cell.findParent('tr', false, true);
6639         
6640         if(!row || typeof(row) == 'undefined'){
6641             return;
6642         }
6643         
6644         var cellIndex = cell.dom.cellIndex;
6645         var rowIndex = this.getRowIndex(row);
6646         
6647         if(this.cellSelection){
6648             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6649         }
6650         
6651         if(this.rowSelection){
6652             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6653         }
6654     },
6655     
6656     sort : function(e,el)
6657     {
6658         var col = Roo.get(el);
6659         
6660         if(!col.hasClass('sortable')){
6661             return;
6662         }
6663         
6664         var sort = col.attr('sort');
6665         var dir = 'ASC';
6666         
6667         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6668             dir = 'DESC';
6669         }
6670         
6671         this.store.sortInfo = {field : sort, direction : dir};
6672         
6673         if (this.footer) {
6674             Roo.log("calling footer first");
6675             this.footer.onClick('first');
6676         } else {
6677         
6678             this.store.load({ params : { start : 0 } });
6679         }
6680     },
6681     
6682     renderHeader : function()
6683     {
6684         var header = {
6685             tag: 'thead',
6686             cn : []
6687         };
6688         
6689         var cm = this.cm;
6690         this.totalWidth = 0;
6691         
6692         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6693             
6694             var config = cm.config[i];
6695             
6696             var c = {
6697                 tag: 'th',
6698                 cls : 'x-hcol-' + i,
6699                 style : '',
6700                 html: cm.getColumnHeader(i)
6701             };
6702             
6703             var hh = '';
6704             
6705             if(typeof(config.sortable) != 'undefined' && config.sortable){
6706                 c.cls = 'sortable';
6707                 c.html = '<i class="glyphicon"></i>' + c.html;
6708             }
6709             
6710             if(typeof(config.lgHeader) != 'undefined'){
6711                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6712             }
6713             
6714             if(typeof(config.mdHeader) != 'undefined'){
6715                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6716             }
6717             
6718             if(typeof(config.smHeader) != 'undefined'){
6719                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6720             }
6721             
6722             if(typeof(config.xsHeader) != 'undefined'){
6723                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6724             }
6725             
6726             if(hh.length){
6727                 c.html = hh;
6728             }
6729             
6730             if(typeof(config.tooltip) != 'undefined'){
6731                 c.tooltip = config.tooltip;
6732             }
6733             
6734             if(typeof(config.colspan) != 'undefined'){
6735                 c.colspan = config.colspan;
6736             }
6737             
6738             if(typeof(config.hidden) != 'undefined' && config.hidden){
6739                 c.style += ' display:none;';
6740             }
6741             
6742             if(typeof(config.dataIndex) != 'undefined'){
6743                 c.sort = config.dataIndex;
6744             }
6745             
6746            
6747             
6748             if(typeof(config.align) != 'undefined' && config.align.length){
6749                 c.style += ' text-align:' + config.align + ';';
6750             }
6751             
6752             if(typeof(config.width) != 'undefined'){
6753                 c.style += ' width:' + config.width + 'px;';
6754                 this.totalWidth += config.width;
6755             } else {
6756                 this.totalWidth += 100; // assume minimum of 100 per column?
6757             }
6758             
6759             if(typeof(config.cls) != 'undefined'){
6760                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6761             }
6762             
6763             ['xs','sm','md','lg'].map(function(size){
6764                 
6765                 if(typeof(config[size]) == 'undefined'){
6766                     return;
6767                 }
6768                 
6769                 if (!config[size]) { // 0 = hidden
6770                     c.cls += ' hidden-' + size;
6771                     return;
6772                 }
6773                 
6774                 c.cls += ' col-' + size + '-' + config[size];
6775
6776             });
6777             
6778             header.cn.push(c)
6779         }
6780         
6781         return header;
6782     },
6783     
6784     renderBody : function()
6785     {
6786         var body = {
6787             tag: 'tbody',
6788             cn : [
6789                 {
6790                     tag: 'tr',
6791                     cn : [
6792                         {
6793                             tag : 'td',
6794                             colspan :  this.cm.getColumnCount()
6795                         }
6796                     ]
6797                 }
6798             ]
6799         };
6800         
6801         return body;
6802     },
6803     
6804     renderFooter : function()
6805     {
6806         var footer = {
6807             tag: 'tfoot',
6808             cn : [
6809                 {
6810                     tag: 'tr',
6811                     cn : [
6812                         {
6813                             tag : 'td',
6814                             colspan :  this.cm.getColumnCount()
6815                         }
6816                     ]
6817                 }
6818             ]
6819         };
6820         
6821         return footer;
6822     },
6823     
6824     
6825     
6826     onLoad : function()
6827     {
6828 //        Roo.log('ds onload');
6829         this.clear();
6830         
6831         var _this = this;
6832         var cm = this.cm;
6833         var ds = this.store;
6834         
6835         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6836             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6837             if (_this.store.sortInfo) {
6838                     
6839                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6840                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6841                 }
6842                 
6843                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6844                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6845                 }
6846             }
6847         });
6848         
6849         var tbody =  this.mainBody;
6850               
6851         if(ds.getCount() > 0){
6852             ds.data.each(function(d,rowIndex){
6853                 var row =  this.renderRow(cm, ds, rowIndex);
6854                 
6855                 tbody.createChild(row);
6856                 
6857                 var _this = this;
6858                 
6859                 if(row.cellObjects.length){
6860                     Roo.each(row.cellObjects, function(r){
6861                         _this.renderCellObject(r);
6862                     })
6863                 }
6864                 
6865             }, this);
6866         }
6867         
6868         var tfoot = this.el.select('tfoot', true).first();
6869         
6870         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6871             
6872             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6873             
6874             var total = this.ds.getTotalCount();
6875             
6876             if(this.footer.pageSize < total){
6877                 this.mainFoot.show();
6878             }
6879         }
6880         
6881         Roo.each(this.el.select('tbody td', true).elements, function(e){
6882             e.on('mouseover', _this.onMouseover, _this);
6883         });
6884         
6885         Roo.each(this.el.select('tbody td', true).elements, function(e){
6886             e.on('mouseout', _this.onMouseout, _this);
6887         });
6888         this.fireEvent('rowsrendered', this);
6889         
6890         this.autoSize();
6891     },
6892     
6893     
6894     onUpdate : function(ds,record)
6895     {
6896         this.refreshRow(record);
6897         this.autoSize();
6898     },
6899     
6900     onRemove : function(ds, record, index, isUpdate){
6901         if(isUpdate !== true){
6902             this.fireEvent("beforerowremoved", this, index, record);
6903         }
6904         var bt = this.mainBody.dom;
6905         
6906         var rows = this.el.select('tbody > tr', true).elements;
6907         
6908         if(typeof(rows[index]) != 'undefined'){
6909             bt.removeChild(rows[index].dom);
6910         }
6911         
6912 //        if(bt.rows[index]){
6913 //            bt.removeChild(bt.rows[index]);
6914 //        }
6915         
6916         if(isUpdate !== true){
6917             //this.stripeRows(index);
6918             //this.syncRowHeights(index, index);
6919             //this.layout();
6920             this.fireEvent("rowremoved", this, index, record);
6921         }
6922     },
6923     
6924     onAdd : function(ds, records, rowIndex)
6925     {
6926         //Roo.log('on Add called');
6927         // - note this does not handle multiple adding very well..
6928         var bt = this.mainBody.dom;
6929         for (var i =0 ; i < records.length;i++) {
6930             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6931             //Roo.log(records[i]);
6932             //Roo.log(this.store.getAt(rowIndex+i));
6933             this.insertRow(this.store, rowIndex + i, false);
6934             return;
6935         }
6936         
6937     },
6938     
6939     
6940     refreshRow : function(record){
6941         var ds = this.store, index;
6942         if(typeof record == 'number'){
6943             index = record;
6944             record = ds.getAt(index);
6945         }else{
6946             index = ds.indexOf(record);
6947         }
6948         this.insertRow(ds, index, true);
6949         this.autoSize();
6950         this.onRemove(ds, record, index+1, true);
6951         this.autoSize();
6952         //this.syncRowHeights(index, index);
6953         //this.layout();
6954         this.fireEvent("rowupdated", this, index, record);
6955     },
6956     
6957     insertRow : function(dm, rowIndex, isUpdate){
6958         
6959         if(!isUpdate){
6960             this.fireEvent("beforerowsinserted", this, rowIndex);
6961         }
6962             //var s = this.getScrollState();
6963         var row = this.renderRow(this.cm, this.store, rowIndex);
6964         // insert before rowIndex..
6965         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6966         
6967         var _this = this;
6968                 
6969         if(row.cellObjects.length){
6970             Roo.each(row.cellObjects, function(r){
6971                 _this.renderCellObject(r);
6972             })
6973         }
6974             
6975         if(!isUpdate){
6976             this.fireEvent("rowsinserted", this, rowIndex);
6977             //this.syncRowHeights(firstRow, lastRow);
6978             //this.stripeRows(firstRow);
6979             //this.layout();
6980         }
6981         
6982     },
6983     
6984     
6985     getRowDom : function(rowIndex)
6986     {
6987         var rows = this.el.select('tbody > tr', true).elements;
6988         
6989         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6990         
6991     },
6992     // returns the object tree for a tr..
6993   
6994     
6995     renderRow : function(cm, ds, rowIndex) 
6996     {
6997         var d = ds.getAt(rowIndex);
6998         
6999         var row = {
7000             tag : 'tr',
7001             cls : 'x-row-' + rowIndex,
7002             cn : []
7003         };
7004             
7005         var cellObjects = [];
7006         
7007         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7008             var config = cm.config[i];
7009             
7010             var renderer = cm.getRenderer(i);
7011             var value = '';
7012             var id = false;
7013             
7014             if(typeof(renderer) !== 'undefined'){
7015                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7016             }
7017             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7018             // and are rendered into the cells after the row is rendered - using the id for the element.
7019             
7020             if(typeof(value) === 'object'){
7021                 id = Roo.id();
7022                 cellObjects.push({
7023                     container : id,
7024                     cfg : value 
7025                 })
7026             }
7027             
7028             var rowcfg = {
7029                 record: d,
7030                 rowIndex : rowIndex,
7031                 colIndex : i,
7032                 rowClass : ''
7033             };
7034
7035             this.fireEvent('rowclass', this, rowcfg);
7036             
7037             var td = {
7038                 tag: 'td',
7039                 cls : rowcfg.rowClass + ' x-col-' + i,
7040                 style: '',
7041                 html: (typeof(value) === 'object') ? '' : value
7042             };
7043             
7044             if (id) {
7045                 td.id = id;
7046             }
7047             
7048             if(typeof(config.colspan) != 'undefined'){
7049                 td.colspan = config.colspan;
7050             }
7051             
7052             if(typeof(config.hidden) != 'undefined' && config.hidden){
7053                 td.style += ' display:none;';
7054             }
7055             
7056             if(typeof(config.align) != 'undefined' && config.align.length){
7057                 td.style += ' text-align:' + config.align + ';';
7058             }
7059             if(typeof(config.valign) != 'undefined' && config.valign.length){
7060                 td.style += ' vertical-align:' + config.valign + ';';
7061             }
7062             
7063             if(typeof(config.width) != 'undefined'){
7064                 td.style += ' width:' +  config.width + 'px;';
7065             }
7066             
7067             if(typeof(config.cursor) != 'undefined'){
7068                 td.style += ' cursor:' +  config.cursor + ';';
7069             }
7070             
7071             if(typeof(config.cls) != 'undefined'){
7072                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7073             }
7074             
7075             ['xs','sm','md','lg'].map(function(size){
7076                 
7077                 if(typeof(config[size]) == 'undefined'){
7078                     return;
7079                 }
7080                 
7081                 if (!config[size]) { // 0 = hidden
7082                     td.cls += ' hidden-' + size;
7083                     return;
7084                 }
7085                 
7086                 td.cls += ' col-' + size + '-' + config[size];
7087
7088             });
7089             
7090             row.cn.push(td);
7091            
7092         }
7093         
7094         row.cellObjects = cellObjects;
7095         
7096         return row;
7097           
7098     },
7099     
7100     
7101     
7102     onBeforeLoad : function()
7103     {
7104         
7105     },
7106      /**
7107      * Remove all rows
7108      */
7109     clear : function()
7110     {
7111         this.el.select('tbody', true).first().dom.innerHTML = '';
7112     },
7113     /**
7114      * Show or hide a row.
7115      * @param {Number} rowIndex to show or hide
7116      * @param {Boolean} state hide
7117      */
7118     setRowVisibility : function(rowIndex, state)
7119     {
7120         var bt = this.mainBody.dom;
7121         
7122         var rows = this.el.select('tbody > tr', true).elements;
7123         
7124         if(typeof(rows[rowIndex]) == 'undefined'){
7125             return;
7126         }
7127         rows[rowIndex].dom.style.display = state ? '' : 'none';
7128     },
7129     
7130     
7131     getSelectionModel : function(){
7132         if(!this.selModel){
7133             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7134         }
7135         return this.selModel;
7136     },
7137     /*
7138      * Render the Roo.bootstrap object from renderder
7139      */
7140     renderCellObject : function(r)
7141     {
7142         var _this = this;
7143         
7144         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7145         
7146         var t = r.cfg.render(r.container);
7147         
7148         if(r.cfg.cn){
7149             Roo.each(r.cfg.cn, function(c){
7150                 var child = {
7151                     container: t.getChildContainer(),
7152                     cfg: c
7153                 };
7154                 _this.renderCellObject(child);
7155             })
7156         }
7157     },
7158     
7159     getRowIndex : function(row)
7160     {
7161         var rowIndex = -1;
7162         
7163         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7164             if(el != row){
7165                 return;
7166             }
7167             
7168             rowIndex = index;
7169         });
7170         
7171         return rowIndex;
7172     },
7173      /**
7174      * Returns the grid's underlying element = used by panel.Grid
7175      * @return {Element} The element
7176      */
7177     getGridEl : function(){
7178         return this.el;
7179     },
7180      /**
7181      * Forces a resize - used by panel.Grid
7182      * @return {Element} The element
7183      */
7184     autoSize : function()
7185     {
7186         //var ctr = Roo.get(this.container.dom.parentElement);
7187         var ctr = Roo.get(this.el.dom);
7188         
7189         var thd = this.getGridEl().select('thead',true).first();
7190         var tbd = this.getGridEl().select('tbody', true).first();
7191         var tfd = this.getGridEl().select('tfoot', true).first();
7192         
7193         var cw = ctr.getWidth();
7194         
7195         if (tbd) {
7196             
7197             tbd.setSize(ctr.getWidth(),
7198                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7199             );
7200             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7201             cw -= barsize;
7202         }
7203         cw = Math.max(cw, this.totalWidth);
7204         this.getGridEl().select('tr',true).setWidth(cw);
7205         // resize 'expandable coloumn?
7206         
7207         return; // we doe not have a view in this design..
7208         
7209     },
7210     onBodyScroll: function()
7211     {
7212         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7213         if(this.mainHead){
7214             this.mainHead.setStyle({
7215                 'position' : 'relative',
7216                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7217             });
7218         }
7219         
7220         if(this.lazyLoad){
7221             
7222             var scrollHeight = this.mainBody.dom.scrollHeight;
7223             
7224             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7225             
7226             var height = this.mainBody.getHeight();
7227             
7228             if(scrollHeight - height == scrollTop) {
7229                 
7230                 var total = this.ds.getTotalCount();
7231                 
7232                 if(this.footer.cursor + this.footer.pageSize < total){
7233                     
7234                     this.footer.ds.load({
7235                         params : {
7236                             start : this.footer.cursor + this.footer.pageSize,
7237                             limit : this.footer.pageSize
7238                         },
7239                         add : true
7240                     });
7241                 }
7242             }
7243             
7244         }
7245     },
7246     
7247     onHeaderChange : function()
7248     {
7249         var header = this.renderHeader();
7250         var table = this.el.select('table', true).first();
7251         
7252         this.mainHead.remove();
7253         this.mainHead = table.createChild(header, this.mainBody, false);
7254     },
7255     
7256     onHiddenChange : function(colModel, colIndex, hidden)
7257     {
7258         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7259         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7260         
7261         this.CSS.updateRule(thSelector, "display", "");
7262         this.CSS.updateRule(tdSelector, "display", "");
7263         
7264         if(hidden){
7265             this.CSS.updateRule(thSelector, "display", "none");
7266             this.CSS.updateRule(tdSelector, "display", "none");
7267         }
7268         
7269         this.onHeaderChange();
7270         this.onLoad();
7271     },
7272     
7273     setColumnWidth: function(col_index, width)
7274     {
7275         // width = "md-2 xs-2..."
7276         if(!this.colModel.config[col_index]) {
7277             return;
7278         }
7279         
7280         var w = width.split(" ");
7281         
7282         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7283         
7284         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7285         
7286         
7287         for(var j = 0; j < w.length; j++) {
7288             
7289             if(!w[j]) {
7290                 continue;
7291             }
7292             
7293             var size_cls = w[j].split("-");
7294             
7295             if(!Number.isInteger(size_cls[1] * 1)) {
7296                 continue;
7297             }
7298             
7299             if(!this.colModel.config[col_index][size_cls[0]]) {
7300                 continue;
7301             }
7302             
7303             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7304                 continue;
7305             }
7306             
7307             h_row[0].classList.replace(
7308                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7309                 "col-"+size_cls[0]+"-"+size_cls[1]
7310             );
7311             
7312             for(var i = 0; i < rows.length; i++) {
7313                 
7314                 var size_cls = w[j].split("-");
7315                 
7316                 if(!Number.isInteger(size_cls[1] * 1)) {
7317                     continue;
7318                 }
7319                 
7320                 if(!this.colModel.config[col_index][size_cls[0]]) {
7321                     continue;
7322                 }
7323                 
7324                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7325                     continue;
7326                 }
7327                 
7328                 rows[i].classList.replace(
7329                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7330                     "col-"+size_cls[0]+"-"+size_cls[1]
7331                 );
7332             }
7333             
7334             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7335         }
7336     }
7337 });
7338
7339  
7340
7341  /*
7342  * - LGPL
7343  *
7344  * table cell
7345  * 
7346  */
7347
7348 /**
7349  * @class Roo.bootstrap.TableCell
7350  * @extends Roo.bootstrap.Component
7351  * Bootstrap TableCell class
7352  * @cfg {String} html cell contain text
7353  * @cfg {String} cls cell class
7354  * @cfg {String} tag cell tag (td|th) default td
7355  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7356  * @cfg {String} align Aligns the content in a cell
7357  * @cfg {String} axis Categorizes cells
7358  * @cfg {String} bgcolor Specifies the background color of a cell
7359  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7360  * @cfg {Number} colspan Specifies the number of columns a cell should span
7361  * @cfg {String} headers Specifies one or more header cells a cell is related to
7362  * @cfg {Number} height Sets the height of a cell
7363  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7364  * @cfg {Number} rowspan Sets the number of rows a cell should span
7365  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7366  * @cfg {String} valign Vertical aligns the content in a cell
7367  * @cfg {Number} width Specifies the width of a cell
7368  * 
7369  * @constructor
7370  * Create a new TableCell
7371  * @param {Object} config The config object
7372  */
7373
7374 Roo.bootstrap.TableCell = function(config){
7375     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7376 };
7377
7378 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7379     
7380     html: false,
7381     cls: false,
7382     tag: false,
7383     abbr: false,
7384     align: false,
7385     axis: false,
7386     bgcolor: false,
7387     charoff: false,
7388     colspan: false,
7389     headers: false,
7390     height: false,
7391     nowrap: false,
7392     rowspan: false,
7393     scope: false,
7394     valign: false,
7395     width: false,
7396     
7397     
7398     getAutoCreate : function(){
7399         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7400         
7401         cfg = {
7402             tag: 'td'
7403         };
7404         
7405         if(this.tag){
7406             cfg.tag = this.tag;
7407         }
7408         
7409         if (this.html) {
7410             cfg.html=this.html
7411         }
7412         if (this.cls) {
7413             cfg.cls=this.cls
7414         }
7415         if (this.abbr) {
7416             cfg.abbr=this.abbr
7417         }
7418         if (this.align) {
7419             cfg.align=this.align
7420         }
7421         if (this.axis) {
7422             cfg.axis=this.axis
7423         }
7424         if (this.bgcolor) {
7425             cfg.bgcolor=this.bgcolor
7426         }
7427         if (this.charoff) {
7428             cfg.charoff=this.charoff
7429         }
7430         if (this.colspan) {
7431             cfg.colspan=this.colspan
7432         }
7433         if (this.headers) {
7434             cfg.headers=this.headers
7435         }
7436         if (this.height) {
7437             cfg.height=this.height
7438         }
7439         if (this.nowrap) {
7440             cfg.nowrap=this.nowrap
7441         }
7442         if (this.rowspan) {
7443             cfg.rowspan=this.rowspan
7444         }
7445         if (this.scope) {
7446             cfg.scope=this.scope
7447         }
7448         if (this.valign) {
7449             cfg.valign=this.valign
7450         }
7451         if (this.width) {
7452             cfg.width=this.width
7453         }
7454         
7455         
7456         return cfg;
7457     }
7458    
7459 });
7460
7461  
7462
7463  /*
7464  * - LGPL
7465  *
7466  * table row
7467  * 
7468  */
7469
7470 /**
7471  * @class Roo.bootstrap.TableRow
7472  * @extends Roo.bootstrap.Component
7473  * Bootstrap TableRow class
7474  * @cfg {String} cls row class
7475  * @cfg {String} align Aligns the content in a table row
7476  * @cfg {String} bgcolor Specifies a background color for a table row
7477  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7478  * @cfg {String} valign Vertical aligns the content in a table row
7479  * 
7480  * @constructor
7481  * Create a new TableRow
7482  * @param {Object} config The config object
7483  */
7484
7485 Roo.bootstrap.TableRow = function(config){
7486     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7487 };
7488
7489 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7490     
7491     cls: false,
7492     align: false,
7493     bgcolor: false,
7494     charoff: false,
7495     valign: false,
7496     
7497     getAutoCreate : function(){
7498         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7499         
7500         cfg = {
7501             tag: 'tr'
7502         };
7503             
7504         if(this.cls){
7505             cfg.cls = this.cls;
7506         }
7507         if(this.align){
7508             cfg.align = this.align;
7509         }
7510         if(this.bgcolor){
7511             cfg.bgcolor = this.bgcolor;
7512         }
7513         if(this.charoff){
7514             cfg.charoff = this.charoff;
7515         }
7516         if(this.valign){
7517             cfg.valign = this.valign;
7518         }
7519         
7520         return cfg;
7521     }
7522    
7523 });
7524
7525  
7526
7527  /*
7528  * - LGPL
7529  *
7530  * table body
7531  * 
7532  */
7533
7534 /**
7535  * @class Roo.bootstrap.TableBody
7536  * @extends Roo.bootstrap.Component
7537  * Bootstrap TableBody class
7538  * @cfg {String} cls element class
7539  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7540  * @cfg {String} align Aligns the content inside the element
7541  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7542  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7543  * 
7544  * @constructor
7545  * Create a new TableBody
7546  * @param {Object} config The config object
7547  */
7548
7549 Roo.bootstrap.TableBody = function(config){
7550     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7551 };
7552
7553 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7554     
7555     cls: false,
7556     tag: false,
7557     align: false,
7558     charoff: false,
7559     valign: false,
7560     
7561     getAutoCreate : function(){
7562         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7563         
7564         cfg = {
7565             tag: 'tbody'
7566         };
7567             
7568         if (this.cls) {
7569             cfg.cls=this.cls
7570         }
7571         if(this.tag){
7572             cfg.tag = this.tag;
7573         }
7574         
7575         if(this.align){
7576             cfg.align = this.align;
7577         }
7578         if(this.charoff){
7579             cfg.charoff = this.charoff;
7580         }
7581         if(this.valign){
7582             cfg.valign = this.valign;
7583         }
7584         
7585         return cfg;
7586     }
7587     
7588     
7589 //    initEvents : function()
7590 //    {
7591 //        
7592 //        if(!this.store){
7593 //            return;
7594 //        }
7595 //        
7596 //        this.store = Roo.factory(this.store, Roo.data);
7597 //        this.store.on('load', this.onLoad, this);
7598 //        
7599 //        this.store.load();
7600 //        
7601 //    },
7602 //    
7603 //    onLoad: function () 
7604 //    {   
7605 //        this.fireEvent('load', this);
7606 //    }
7607 //    
7608 //   
7609 });
7610
7611  
7612
7613  /*
7614  * Based on:
7615  * Ext JS Library 1.1.1
7616  * Copyright(c) 2006-2007, Ext JS, LLC.
7617  *
7618  * Originally Released Under LGPL - original licence link has changed is not relivant.
7619  *
7620  * Fork - LGPL
7621  * <script type="text/javascript">
7622  */
7623
7624 // as we use this in bootstrap.
7625 Roo.namespace('Roo.form');
7626  /**
7627  * @class Roo.form.Action
7628  * Internal Class used to handle form actions
7629  * @constructor
7630  * @param {Roo.form.BasicForm} el The form element or its id
7631  * @param {Object} config Configuration options
7632  */
7633
7634  
7635  
7636 // define the action interface
7637 Roo.form.Action = function(form, options){
7638     this.form = form;
7639     this.options = options || {};
7640 };
7641 /**
7642  * Client Validation Failed
7643  * @const 
7644  */
7645 Roo.form.Action.CLIENT_INVALID = 'client';
7646 /**
7647  * Server Validation Failed
7648  * @const 
7649  */
7650 Roo.form.Action.SERVER_INVALID = 'server';
7651  /**
7652  * Connect to Server Failed
7653  * @const 
7654  */
7655 Roo.form.Action.CONNECT_FAILURE = 'connect';
7656 /**
7657  * Reading Data from Server Failed
7658  * @const 
7659  */
7660 Roo.form.Action.LOAD_FAILURE = 'load';
7661
7662 Roo.form.Action.prototype = {
7663     type : 'default',
7664     failureType : undefined,
7665     response : undefined,
7666     result : undefined,
7667
7668     // interface method
7669     run : function(options){
7670
7671     },
7672
7673     // interface method
7674     success : function(response){
7675
7676     },
7677
7678     // interface method
7679     handleResponse : function(response){
7680
7681     },
7682
7683     // default connection failure
7684     failure : function(response){
7685         
7686         this.response = response;
7687         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7688         this.form.afterAction(this, false);
7689     },
7690
7691     processResponse : function(response){
7692         this.response = response;
7693         if(!response.responseText){
7694             return true;
7695         }
7696         this.result = this.handleResponse(response);
7697         return this.result;
7698     },
7699
7700     // utility functions used internally
7701     getUrl : function(appendParams){
7702         var url = this.options.url || this.form.url || this.form.el.dom.action;
7703         if(appendParams){
7704             var p = this.getParams();
7705             if(p){
7706                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7707             }
7708         }
7709         return url;
7710     },
7711
7712     getMethod : function(){
7713         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7714     },
7715
7716     getParams : function(){
7717         var bp = this.form.baseParams;
7718         var p = this.options.params;
7719         if(p){
7720             if(typeof p == "object"){
7721                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7722             }else if(typeof p == 'string' && bp){
7723                 p += '&' + Roo.urlEncode(bp);
7724             }
7725         }else if(bp){
7726             p = Roo.urlEncode(bp);
7727         }
7728         return p;
7729     },
7730
7731     createCallback : function(){
7732         return {
7733             success: this.success,
7734             failure: this.failure,
7735             scope: this,
7736             timeout: (this.form.timeout*1000),
7737             upload: this.form.fileUpload ? this.success : undefined
7738         };
7739     }
7740 };
7741
7742 Roo.form.Action.Submit = function(form, options){
7743     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7744 };
7745
7746 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7747     type : 'submit',
7748
7749     haveProgress : false,
7750     uploadComplete : false,
7751     
7752     // uploadProgress indicator.
7753     uploadProgress : function()
7754     {
7755         if (!this.form.progressUrl) {
7756             return;
7757         }
7758         
7759         if (!this.haveProgress) {
7760             Roo.MessageBox.progress("Uploading", "Uploading");
7761         }
7762         if (this.uploadComplete) {
7763            Roo.MessageBox.hide();
7764            return;
7765         }
7766         
7767         this.haveProgress = true;
7768    
7769         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7770         
7771         var c = new Roo.data.Connection();
7772         c.request({
7773             url : this.form.progressUrl,
7774             params: {
7775                 id : uid
7776             },
7777             method: 'GET',
7778             success : function(req){
7779                //console.log(data);
7780                 var rdata = false;
7781                 var edata;
7782                 try  {
7783                    rdata = Roo.decode(req.responseText)
7784                 } catch (e) {
7785                     Roo.log("Invalid data from server..");
7786                     Roo.log(edata);
7787                     return;
7788                 }
7789                 if (!rdata || !rdata.success) {
7790                     Roo.log(rdata);
7791                     Roo.MessageBox.alert(Roo.encode(rdata));
7792                     return;
7793                 }
7794                 var data = rdata.data;
7795                 
7796                 if (this.uploadComplete) {
7797                    Roo.MessageBox.hide();
7798                    return;
7799                 }
7800                    
7801                 if (data){
7802                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7803                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7804                     );
7805                 }
7806                 this.uploadProgress.defer(2000,this);
7807             },
7808        
7809             failure: function(data) {
7810                 Roo.log('progress url failed ');
7811                 Roo.log(data);
7812             },
7813             scope : this
7814         });
7815            
7816     },
7817     
7818     
7819     run : function()
7820     {
7821         // run get Values on the form, so it syncs any secondary forms.
7822         this.form.getValues();
7823         
7824         var o = this.options;
7825         var method = this.getMethod();
7826         var isPost = method == 'POST';
7827         if(o.clientValidation === false || this.form.isValid()){
7828             
7829             if (this.form.progressUrl) {
7830                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7831                     (new Date() * 1) + '' + Math.random());
7832                     
7833             } 
7834             
7835             
7836             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7837                 form:this.form.el.dom,
7838                 url:this.getUrl(!isPost),
7839                 method: method,
7840                 params:isPost ? this.getParams() : null,
7841                 isUpload: this.form.fileUpload
7842             }));
7843             
7844             this.uploadProgress();
7845
7846         }else if (o.clientValidation !== false){ // client validation failed
7847             this.failureType = Roo.form.Action.CLIENT_INVALID;
7848             this.form.afterAction(this, false);
7849         }
7850     },
7851
7852     success : function(response)
7853     {
7854         this.uploadComplete= true;
7855         if (this.haveProgress) {
7856             Roo.MessageBox.hide();
7857         }
7858         
7859         
7860         var result = this.processResponse(response);
7861         if(result === true || result.success){
7862             this.form.afterAction(this, true);
7863             return;
7864         }
7865         if(result.errors){
7866             this.form.markInvalid(result.errors);
7867             this.failureType = Roo.form.Action.SERVER_INVALID;
7868         }
7869         this.form.afterAction(this, false);
7870     },
7871     failure : function(response)
7872     {
7873         this.uploadComplete= true;
7874         if (this.haveProgress) {
7875             Roo.MessageBox.hide();
7876         }
7877         
7878         this.response = response;
7879         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7880         this.form.afterAction(this, false);
7881     },
7882     
7883     handleResponse : function(response){
7884         if(this.form.errorReader){
7885             var rs = this.form.errorReader.read(response);
7886             var errors = [];
7887             if(rs.records){
7888                 for(var i = 0, len = rs.records.length; i < len; i++) {
7889                     var r = rs.records[i];
7890                     errors[i] = r.data;
7891                 }
7892             }
7893             if(errors.length < 1){
7894                 errors = null;
7895             }
7896             return {
7897                 success : rs.success,
7898                 errors : errors
7899             };
7900         }
7901         var ret = false;
7902         try {
7903             ret = Roo.decode(response.responseText);
7904         } catch (e) {
7905             ret = {
7906                 success: false,
7907                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7908                 errors : []
7909             };
7910         }
7911         return ret;
7912         
7913     }
7914 });
7915
7916
7917 Roo.form.Action.Load = function(form, options){
7918     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7919     this.reader = this.form.reader;
7920 };
7921
7922 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7923     type : 'load',
7924
7925     run : function(){
7926         
7927         Roo.Ajax.request(Roo.apply(
7928                 this.createCallback(), {
7929                     method:this.getMethod(),
7930                     url:this.getUrl(false),
7931                     params:this.getParams()
7932         }));
7933     },
7934
7935     success : function(response){
7936         
7937         var result = this.processResponse(response);
7938         if(result === true || !result.success || !result.data){
7939             this.failureType = Roo.form.Action.LOAD_FAILURE;
7940             this.form.afterAction(this, false);
7941             return;
7942         }
7943         this.form.clearInvalid();
7944         this.form.setValues(result.data);
7945         this.form.afterAction(this, true);
7946     },
7947
7948     handleResponse : function(response){
7949         if(this.form.reader){
7950             var rs = this.form.reader.read(response);
7951             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7952             return {
7953                 success : rs.success,
7954                 data : data
7955             };
7956         }
7957         return Roo.decode(response.responseText);
7958     }
7959 });
7960
7961 Roo.form.Action.ACTION_TYPES = {
7962     'load' : Roo.form.Action.Load,
7963     'submit' : Roo.form.Action.Submit
7964 };/*
7965  * - LGPL
7966  *
7967  * form
7968  *
7969  */
7970
7971 /**
7972  * @class Roo.bootstrap.Form
7973  * @extends Roo.bootstrap.Component
7974  * Bootstrap Form class
7975  * @cfg {String} method  GET | POST (default POST)
7976  * @cfg {String} labelAlign top | left (default top)
7977  * @cfg {String} align left  | right - for navbars
7978  * @cfg {Boolean} loadMask load mask when submit (default true)
7979
7980  *
7981  * @constructor
7982  * Create a new Form
7983  * @param {Object} config The config object
7984  */
7985
7986
7987 Roo.bootstrap.Form = function(config){
7988     
7989     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7990     
7991     Roo.bootstrap.Form.popover.apply();
7992     
7993     this.addEvents({
7994         /**
7995          * @event clientvalidation
7996          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7997          * @param {Form} this
7998          * @param {Boolean} valid true if the form has passed client-side validation
7999          */
8000         clientvalidation: true,
8001         /**
8002          * @event beforeaction
8003          * Fires before any action is performed. Return false to cancel the action.
8004          * @param {Form} this
8005          * @param {Action} action The action to be performed
8006          */
8007         beforeaction: true,
8008         /**
8009          * @event actionfailed
8010          * Fires when an action fails.
8011          * @param {Form} this
8012          * @param {Action} action The action that failed
8013          */
8014         actionfailed : true,
8015         /**
8016          * @event actioncomplete
8017          * Fires when an action is completed.
8018          * @param {Form} this
8019          * @param {Action} action The action that completed
8020          */
8021         actioncomplete : true
8022     });
8023 };
8024
8025 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8026
8027      /**
8028      * @cfg {String} method
8029      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8030      */
8031     method : 'POST',
8032     /**
8033      * @cfg {String} url
8034      * The URL to use for form actions if one isn't supplied in the action options.
8035      */
8036     /**
8037      * @cfg {Boolean} fileUpload
8038      * Set to true if this form is a file upload.
8039      */
8040
8041     /**
8042      * @cfg {Object} baseParams
8043      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8044      */
8045
8046     /**
8047      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8048      */
8049     timeout: 30,
8050     /**
8051      * @cfg {Sting} align (left|right) for navbar forms
8052      */
8053     align : 'left',
8054
8055     // private
8056     activeAction : null,
8057
8058     /**
8059      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8060      * element by passing it or its id or mask the form itself by passing in true.
8061      * @type Mixed
8062      */
8063     waitMsgTarget : false,
8064
8065     loadMask : true,
8066     
8067     /**
8068      * @cfg {Boolean} errorMask (true|false) default false
8069      */
8070     errorMask : false,
8071     
8072     /**
8073      * @cfg {Number} maskOffset Default 100
8074      */
8075     maskOffset : 100,
8076     
8077     /**
8078      * @cfg {Boolean} maskBody
8079      */
8080     maskBody : false,
8081
8082     getAutoCreate : function(){
8083
8084         var cfg = {
8085             tag: 'form',
8086             method : this.method || 'POST',
8087             id : this.id || Roo.id(),
8088             cls : ''
8089         };
8090         if (this.parent().xtype.match(/^Nav/)) {
8091             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8092
8093         }
8094
8095         if (this.labelAlign == 'left' ) {
8096             cfg.cls += ' form-horizontal';
8097         }
8098
8099
8100         return cfg;
8101     },
8102     initEvents : function()
8103     {
8104         this.el.on('submit', this.onSubmit, this);
8105         // this was added as random key presses on the form where triggering form submit.
8106         this.el.on('keypress', function(e) {
8107             if (e.getCharCode() != 13) {
8108                 return true;
8109             }
8110             // we might need to allow it for textareas.. and some other items.
8111             // check e.getTarget().
8112
8113             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8114                 return true;
8115             }
8116
8117             Roo.log("keypress blocked");
8118
8119             e.preventDefault();
8120             return false;
8121         });
8122         
8123     },
8124     // private
8125     onSubmit : function(e){
8126         e.stopEvent();
8127     },
8128
8129      /**
8130      * Returns true if client-side validation on the form is successful.
8131      * @return Boolean
8132      */
8133     isValid : function(){
8134         var items = this.getItems();
8135         var valid = true;
8136         var target = false;
8137         
8138         items.each(function(f){
8139             
8140             if(f.validate()){
8141                 return;
8142             }
8143             
8144             Roo.log('invalid field: ' + f.name);
8145             
8146             valid = false;
8147
8148             if(!target && f.el.isVisible(true)){
8149                 target = f;
8150             }
8151            
8152         });
8153         
8154         if(this.errorMask && !valid){
8155             Roo.bootstrap.Form.popover.mask(this, target);
8156         }
8157         
8158         return valid;
8159     },
8160     
8161     /**
8162      * Returns true if any fields in this form have changed since their original load.
8163      * @return Boolean
8164      */
8165     isDirty : function(){
8166         var dirty = false;
8167         var items = this.getItems();
8168         items.each(function(f){
8169            if(f.isDirty()){
8170                dirty = true;
8171                return false;
8172            }
8173            return true;
8174         });
8175         return dirty;
8176     },
8177      /**
8178      * Performs a predefined action (submit or load) or custom actions you define on this form.
8179      * @param {String} actionName The name of the action type
8180      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8181      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8182      * accept other config options):
8183      * <pre>
8184 Property          Type             Description
8185 ----------------  ---------------  ----------------------------------------------------------------------------------
8186 url               String           The url for the action (defaults to the form's url)
8187 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8188 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8189 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8190                                    validate the form on the client (defaults to false)
8191      * </pre>
8192      * @return {BasicForm} this
8193      */
8194     doAction : function(action, options){
8195         if(typeof action == 'string'){
8196             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8197         }
8198         if(this.fireEvent('beforeaction', this, action) !== false){
8199             this.beforeAction(action);
8200             action.run.defer(100, action);
8201         }
8202         return this;
8203     },
8204
8205     // private
8206     beforeAction : function(action){
8207         var o = action.options;
8208         
8209         if(this.loadMask){
8210             
8211             if(this.maskBody){
8212                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8213             } else {
8214                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8215             }
8216         }
8217         // not really supported yet.. ??
8218
8219         //if(this.waitMsgTarget === true){
8220         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8221         //}else if(this.waitMsgTarget){
8222         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8223         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8224         //}else {
8225         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8226        // }
8227
8228     },
8229
8230     // private
8231     afterAction : function(action, success){
8232         this.activeAction = null;
8233         var o = action.options;
8234
8235         if(this.loadMask){
8236             
8237             if(this.maskBody){
8238                 Roo.get(document.body).unmask();
8239             } else {
8240                 this.el.unmask();
8241             }
8242         }
8243         
8244         //if(this.waitMsgTarget === true){
8245 //            this.el.unmask();
8246         //}else if(this.waitMsgTarget){
8247         //    this.waitMsgTarget.unmask();
8248         //}else{
8249         //    Roo.MessageBox.updateProgress(1);
8250         //    Roo.MessageBox.hide();
8251        // }
8252         //
8253         if(success){
8254             if(o.reset){
8255                 this.reset();
8256             }
8257             Roo.callback(o.success, o.scope, [this, action]);
8258             this.fireEvent('actioncomplete', this, action);
8259
8260         }else{
8261
8262             // failure condition..
8263             // we have a scenario where updates need confirming.
8264             // eg. if a locking scenario exists..
8265             // we look for { errors : { needs_confirm : true }} in the response.
8266             if (
8267                 (typeof(action.result) != 'undefined')  &&
8268                 (typeof(action.result.errors) != 'undefined')  &&
8269                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8270            ){
8271                 var _t = this;
8272                 Roo.log("not supported yet");
8273                  /*
8274
8275                 Roo.MessageBox.confirm(
8276                     "Change requires confirmation",
8277                     action.result.errorMsg,
8278                     function(r) {
8279                         if (r != 'yes') {
8280                             return;
8281                         }
8282                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8283                     }
8284
8285                 );
8286                 */
8287
8288
8289                 return;
8290             }
8291
8292             Roo.callback(o.failure, o.scope, [this, action]);
8293             // show an error message if no failed handler is set..
8294             if (!this.hasListener('actionfailed')) {
8295                 Roo.log("need to add dialog support");
8296                 /*
8297                 Roo.MessageBox.alert("Error",
8298                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8299                         action.result.errorMsg :
8300                         "Saving Failed, please check your entries or try again"
8301                 );
8302                 */
8303             }
8304
8305             this.fireEvent('actionfailed', this, action);
8306         }
8307
8308     },
8309     /**
8310      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8311      * @param {String} id The value to search for
8312      * @return Field
8313      */
8314     findField : function(id){
8315         var items = this.getItems();
8316         var field = items.get(id);
8317         if(!field){
8318              items.each(function(f){
8319                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8320                     field = f;
8321                     return false;
8322                 }
8323                 return true;
8324             });
8325         }
8326         return field || null;
8327     },
8328      /**
8329      * Mark fields in this form invalid in bulk.
8330      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8331      * @return {BasicForm} this
8332      */
8333     markInvalid : function(errors){
8334         if(errors instanceof Array){
8335             for(var i = 0, len = errors.length; i < len; i++){
8336                 var fieldError = errors[i];
8337                 var f = this.findField(fieldError.id);
8338                 if(f){
8339                     f.markInvalid(fieldError.msg);
8340                 }
8341             }
8342         }else{
8343             var field, id;
8344             for(id in errors){
8345                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8346                     field.markInvalid(errors[id]);
8347                 }
8348             }
8349         }
8350         //Roo.each(this.childForms || [], function (f) {
8351         //    f.markInvalid(errors);
8352         //});
8353
8354         return this;
8355     },
8356
8357     /**
8358      * Set values for fields in this form in bulk.
8359      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8360      * @return {BasicForm} this
8361      */
8362     setValues : function(values){
8363         if(values instanceof Array){ // array of objects
8364             for(var i = 0, len = values.length; i < len; i++){
8365                 var v = values[i];
8366                 var f = this.findField(v.id);
8367                 if(f){
8368                     f.setValue(v.value);
8369                     if(this.trackResetOnLoad){
8370                         f.originalValue = f.getValue();
8371                     }
8372                 }
8373             }
8374         }else{ // object hash
8375             var field, id;
8376             for(id in values){
8377                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8378
8379                     if (field.setFromData &&
8380                         field.valueField &&
8381                         field.displayField &&
8382                         // combos' with local stores can
8383                         // be queried via setValue()
8384                         // to set their value..
8385                         (field.store && !field.store.isLocal)
8386                         ) {
8387                         // it's a combo
8388                         var sd = { };
8389                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8390                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8391                         field.setFromData(sd);
8392
8393                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8394                         
8395                         field.setFromData(values);
8396                         
8397                     } else {
8398                         field.setValue(values[id]);
8399                     }
8400
8401
8402                     if(this.trackResetOnLoad){
8403                         field.originalValue = field.getValue();
8404                     }
8405                 }
8406             }
8407         }
8408
8409         //Roo.each(this.childForms || [], function (f) {
8410         //    f.setValues(values);
8411         //});
8412
8413         return this;
8414     },
8415
8416     /**
8417      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8418      * they are returned as an array.
8419      * @param {Boolean} asString
8420      * @return {Object}
8421      */
8422     getValues : function(asString){
8423         //if (this.childForms) {
8424             // copy values from the child forms
8425         //    Roo.each(this.childForms, function (f) {
8426         //        this.setValues(f.getValues());
8427         //    }, this);
8428         //}
8429
8430
8431
8432         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8433         if(asString === true){
8434             return fs;
8435         }
8436         return Roo.urlDecode(fs);
8437     },
8438
8439     /**
8440      * Returns the fields in this form as an object with key/value pairs.
8441      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8442      * @return {Object}
8443      */
8444     getFieldValues : function(with_hidden)
8445     {
8446         var items = this.getItems();
8447         var ret = {};
8448         items.each(function(f){
8449             
8450             if (!f.getName()) {
8451                 return;
8452             }
8453             
8454             var v = f.getValue();
8455             
8456             if (f.inputType =='radio') {
8457                 if (typeof(ret[f.getName()]) == 'undefined') {
8458                     ret[f.getName()] = ''; // empty..
8459                 }
8460
8461                 if (!f.el.dom.checked) {
8462                     return;
8463
8464                 }
8465                 v = f.el.dom.value;
8466
8467             }
8468             
8469             if(f.xtype == 'MoneyField'){
8470                 ret[f.currencyName] = f.getCurrency();
8471             }
8472
8473             // not sure if this supported any more..
8474             if ((typeof(v) == 'object') && f.getRawValue) {
8475                 v = f.getRawValue() ; // dates..
8476             }
8477             // combo boxes where name != hiddenName...
8478             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8479                 ret[f.name] = f.getRawValue();
8480             }
8481             ret[f.getName()] = v;
8482         });
8483
8484         return ret;
8485     },
8486
8487     /**
8488      * Clears all invalid messages in this form.
8489      * @return {BasicForm} this
8490      */
8491     clearInvalid : function(){
8492         var items = this.getItems();
8493
8494         items.each(function(f){
8495            f.clearInvalid();
8496         });
8497
8498         return this;
8499     },
8500
8501     /**
8502      * Resets this form.
8503      * @return {BasicForm} this
8504      */
8505     reset : function(){
8506         var items = this.getItems();
8507         items.each(function(f){
8508             f.reset();
8509         });
8510
8511         Roo.each(this.childForms || [], function (f) {
8512             f.reset();
8513         });
8514
8515
8516         return this;
8517     },
8518     
8519     getItems : function()
8520     {
8521         var r=new Roo.util.MixedCollection(false, function(o){
8522             return o.id || (o.id = Roo.id());
8523         });
8524         var iter = function(el) {
8525             if (el.inputEl) {
8526                 r.add(el);
8527             }
8528             if (!el.items) {
8529                 return;
8530             }
8531             Roo.each(el.items,function(e) {
8532                 iter(e);
8533             });
8534         };
8535
8536         iter(this);
8537         return r;
8538     },
8539     
8540     hideFields : function(items)
8541     {
8542         Roo.each(items, function(i){
8543             
8544             var f = this.findField(i);
8545             
8546             if(!f){
8547                 return;
8548             }
8549             
8550             f.hide();
8551             
8552         }, this);
8553     },
8554     
8555     showFields : function(items)
8556     {
8557         Roo.each(items, function(i){
8558             
8559             var f = this.findField(i);
8560             
8561             if(!f){
8562                 return;
8563             }
8564             
8565             f.show();
8566             
8567         }, this);
8568     }
8569
8570 });
8571
8572 Roo.apply(Roo.bootstrap.Form, {
8573     
8574     popover : {
8575         
8576         padding : 5,
8577         
8578         isApplied : false,
8579         
8580         isMasked : false,
8581         
8582         form : false,
8583         
8584         target : false,
8585         
8586         toolTip : false,
8587         
8588         intervalID : false,
8589         
8590         maskEl : false,
8591         
8592         apply : function()
8593         {
8594             if(this.isApplied){
8595                 return;
8596             }
8597             
8598             this.maskEl = {
8599                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8600                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8601                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8602                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8603             };
8604             
8605             this.maskEl.top.enableDisplayMode("block");
8606             this.maskEl.left.enableDisplayMode("block");
8607             this.maskEl.bottom.enableDisplayMode("block");
8608             this.maskEl.right.enableDisplayMode("block");
8609             
8610             this.toolTip = new Roo.bootstrap.Tooltip({
8611                 cls : 'roo-form-error-popover',
8612                 alignment : {
8613                     'left' : ['r-l', [-2,0], 'right'],
8614                     'right' : ['l-r', [2,0], 'left'],
8615                     'bottom' : ['tl-bl', [0,2], 'top'],
8616                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8617                 }
8618             });
8619             
8620             this.toolTip.render(Roo.get(document.body));
8621
8622             this.toolTip.el.enableDisplayMode("block");
8623             
8624             Roo.get(document.body).on('click', function(){
8625                 this.unmask();
8626             }, this);
8627             
8628             Roo.get(document.body).on('touchstart', function(){
8629                 this.unmask();
8630             }, this);
8631             
8632             this.isApplied = true
8633         },
8634         
8635         mask : function(form, target)
8636         {
8637             this.form = form;
8638             
8639             this.target = target;
8640             
8641             if(!this.form.errorMask || !target.el){
8642                 return;
8643             }
8644             
8645             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8646             
8647             Roo.log(scrollable);
8648             
8649             var ot = this.target.el.calcOffsetsTo(scrollable);
8650             
8651             var scrollTo = ot[1] - this.form.maskOffset;
8652             
8653             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8654             
8655             scrollable.scrollTo('top', scrollTo);
8656             
8657             var box = this.target.el.getBox();
8658             Roo.log(box);
8659             var zIndex = Roo.bootstrap.Modal.zIndex++;
8660
8661             
8662             this.maskEl.top.setStyle('position', 'absolute');
8663             this.maskEl.top.setStyle('z-index', zIndex);
8664             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8665             this.maskEl.top.setLeft(0);
8666             this.maskEl.top.setTop(0);
8667             this.maskEl.top.show();
8668             
8669             this.maskEl.left.setStyle('position', 'absolute');
8670             this.maskEl.left.setStyle('z-index', zIndex);
8671             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8672             this.maskEl.left.setLeft(0);
8673             this.maskEl.left.setTop(box.y - this.padding);
8674             this.maskEl.left.show();
8675
8676             this.maskEl.bottom.setStyle('position', 'absolute');
8677             this.maskEl.bottom.setStyle('z-index', zIndex);
8678             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8679             this.maskEl.bottom.setLeft(0);
8680             this.maskEl.bottom.setTop(box.bottom + this.padding);
8681             this.maskEl.bottom.show();
8682
8683             this.maskEl.right.setStyle('position', 'absolute');
8684             this.maskEl.right.setStyle('z-index', zIndex);
8685             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8686             this.maskEl.right.setLeft(box.right + this.padding);
8687             this.maskEl.right.setTop(box.y - this.padding);
8688             this.maskEl.right.show();
8689
8690             this.toolTip.bindEl = this.target.el;
8691
8692             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8693
8694             var tip = this.target.blankText;
8695
8696             if(this.target.getValue() !== '' ) {
8697                 
8698                 if (this.target.invalidText.length) {
8699                     tip = this.target.invalidText;
8700                 } else if (this.target.regexText.length){
8701                     tip = this.target.regexText;
8702                 }
8703             }
8704
8705             this.toolTip.show(tip);
8706
8707             this.intervalID = window.setInterval(function() {
8708                 Roo.bootstrap.Form.popover.unmask();
8709             }, 10000);
8710
8711             window.onwheel = function(){ return false;};
8712             
8713             (function(){ this.isMasked = true; }).defer(500, this);
8714             
8715         },
8716         
8717         unmask : function()
8718         {
8719             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8720                 return;
8721             }
8722             
8723             this.maskEl.top.setStyle('position', 'absolute');
8724             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8725             this.maskEl.top.hide();
8726
8727             this.maskEl.left.setStyle('position', 'absolute');
8728             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8729             this.maskEl.left.hide();
8730
8731             this.maskEl.bottom.setStyle('position', 'absolute');
8732             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8733             this.maskEl.bottom.hide();
8734
8735             this.maskEl.right.setStyle('position', 'absolute');
8736             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8737             this.maskEl.right.hide();
8738             
8739             this.toolTip.hide();
8740             
8741             this.toolTip.el.hide();
8742             
8743             window.onwheel = function(){ return true;};
8744             
8745             if(this.intervalID){
8746                 window.clearInterval(this.intervalID);
8747                 this.intervalID = false;
8748             }
8749             
8750             this.isMasked = false;
8751             
8752         }
8753         
8754     }
8755     
8756 });
8757
8758 /*
8759  * Based on:
8760  * Ext JS Library 1.1.1
8761  * Copyright(c) 2006-2007, Ext JS, LLC.
8762  *
8763  * Originally Released Under LGPL - original licence link has changed is not relivant.
8764  *
8765  * Fork - LGPL
8766  * <script type="text/javascript">
8767  */
8768 /**
8769  * @class Roo.form.VTypes
8770  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8771  * @singleton
8772  */
8773 Roo.form.VTypes = function(){
8774     // closure these in so they are only created once.
8775     var alpha = /^[a-zA-Z_]+$/;
8776     var alphanum = /^[a-zA-Z0-9_]+$/;
8777     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8778     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8779
8780     // All these messages and functions are configurable
8781     return {
8782         /**
8783          * The function used to validate email addresses
8784          * @param {String} value The email address
8785          */
8786         'email' : function(v){
8787             return email.test(v);
8788         },
8789         /**
8790          * The error text to display when the email validation function returns false
8791          * @type String
8792          */
8793         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8794         /**
8795          * The keystroke filter mask to be applied on email input
8796          * @type RegExp
8797          */
8798         'emailMask' : /[a-z0-9_\.\-@]/i,
8799
8800         /**
8801          * The function used to validate URLs
8802          * @param {String} value The URL
8803          */
8804         'url' : function(v){
8805             return url.test(v);
8806         },
8807         /**
8808          * The error text to display when the url validation function returns false
8809          * @type String
8810          */
8811         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8812         
8813         /**
8814          * The function used to validate alpha values
8815          * @param {String} value The value
8816          */
8817         'alpha' : function(v){
8818             return alpha.test(v);
8819         },
8820         /**
8821          * The error text to display when the alpha validation function returns false
8822          * @type String
8823          */
8824         'alphaText' : 'This field should only contain letters and _',
8825         /**
8826          * The keystroke filter mask to be applied on alpha input
8827          * @type RegExp
8828          */
8829         'alphaMask' : /[a-z_]/i,
8830
8831         /**
8832          * The function used to validate alphanumeric values
8833          * @param {String} value The value
8834          */
8835         'alphanum' : function(v){
8836             return alphanum.test(v);
8837         },
8838         /**
8839          * The error text to display when the alphanumeric validation function returns false
8840          * @type String
8841          */
8842         'alphanumText' : 'This field should only contain letters, numbers and _',
8843         /**
8844          * The keystroke filter mask to be applied on alphanumeric input
8845          * @type RegExp
8846          */
8847         'alphanumMask' : /[a-z0-9_]/i
8848     };
8849 }();/*
8850  * - LGPL
8851  *
8852  * Input
8853  * 
8854  */
8855
8856 /**
8857  * @class Roo.bootstrap.Input
8858  * @extends Roo.bootstrap.Component
8859  * Bootstrap Input class
8860  * @cfg {Boolean} disabled is it disabled
8861  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8862  * @cfg {String} name name of the input
8863  * @cfg {string} fieldLabel - the label associated
8864  * @cfg {string} placeholder - placeholder to put in text.
8865  * @cfg {string}  before - input group add on before
8866  * @cfg {string} after - input group add on after
8867  * @cfg {string} size - (lg|sm) or leave empty..
8868  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8869  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8870  * @cfg {Number} md colspan out of 12 for computer-sized screens
8871  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8872  * @cfg {string} value default value of the input
8873  * @cfg {Number} labelWidth set the width of label 
8874  * @cfg {Number} labellg set the width of label (1-12)
8875  * @cfg {Number} labelmd set the width of label (1-12)
8876  * @cfg {Number} labelsm set the width of label (1-12)
8877  * @cfg {Number} labelxs set the width of label (1-12)
8878  * @cfg {String} labelAlign (top|left)
8879  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8880  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8881  * @cfg {String} indicatorpos (left|right) default left
8882  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8883  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8884
8885  * @cfg {String} align (left|center|right) Default left
8886  * @cfg {Boolean} forceFeedback (true|false) Default false
8887  * 
8888  * @constructor
8889  * Create a new Input
8890  * @param {Object} config The config object
8891  */
8892
8893 Roo.bootstrap.Input = function(config){
8894     
8895     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8896     
8897     this.addEvents({
8898         /**
8899          * @event focus
8900          * Fires when this field receives input focus.
8901          * @param {Roo.form.Field} this
8902          */
8903         focus : true,
8904         /**
8905          * @event blur
8906          * Fires when this field loses input focus.
8907          * @param {Roo.form.Field} this
8908          */
8909         blur : true,
8910         /**
8911          * @event specialkey
8912          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8913          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8914          * @param {Roo.form.Field} this
8915          * @param {Roo.EventObject} e The event object
8916          */
8917         specialkey : true,
8918         /**
8919          * @event change
8920          * Fires just before the field blurs if the field value has changed.
8921          * @param {Roo.form.Field} this
8922          * @param {Mixed} newValue The new value
8923          * @param {Mixed} oldValue The original value
8924          */
8925         change : true,
8926         /**
8927          * @event invalid
8928          * Fires after the field has been marked as invalid.
8929          * @param {Roo.form.Field} this
8930          * @param {String} msg The validation message
8931          */
8932         invalid : true,
8933         /**
8934          * @event valid
8935          * Fires after the field has been validated with no errors.
8936          * @param {Roo.form.Field} this
8937          */
8938         valid : true,
8939          /**
8940          * @event keyup
8941          * Fires after the key up
8942          * @param {Roo.form.Field} this
8943          * @param {Roo.EventObject}  e The event Object
8944          */
8945         keyup : true
8946     });
8947 };
8948
8949 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8950      /**
8951      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8952       automatic validation (defaults to "keyup").
8953      */
8954     validationEvent : "keyup",
8955      /**
8956      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8957      */
8958     validateOnBlur : true,
8959     /**
8960      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8961      */
8962     validationDelay : 250,
8963      /**
8964      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8965      */
8966     focusClass : "x-form-focus",  // not needed???
8967     
8968        
8969     /**
8970      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8971      */
8972     invalidClass : "has-warning",
8973     
8974     /**
8975      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8976      */
8977     validClass : "has-success",
8978     
8979     /**
8980      * @cfg {Boolean} hasFeedback (true|false) default true
8981      */
8982     hasFeedback : true,
8983     
8984     /**
8985      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8986      */
8987     invalidFeedbackClass : "glyphicon-warning-sign",
8988     
8989     /**
8990      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8991      */
8992     validFeedbackClass : "glyphicon-ok",
8993     
8994     /**
8995      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8996      */
8997     selectOnFocus : false,
8998     
8999      /**
9000      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9001      */
9002     maskRe : null,
9003        /**
9004      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9005      */
9006     vtype : null,
9007     
9008       /**
9009      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9010      */
9011     disableKeyFilter : false,
9012     
9013        /**
9014      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9015      */
9016     disabled : false,
9017      /**
9018      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9019      */
9020     allowBlank : true,
9021     /**
9022      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9023      */
9024     blankText : "Please complete this mandatory field",
9025     
9026      /**
9027      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9028      */
9029     minLength : 0,
9030     /**
9031      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9032      */
9033     maxLength : Number.MAX_VALUE,
9034     /**
9035      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9036      */
9037     minLengthText : "The minimum length for this field is {0}",
9038     /**
9039      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9040      */
9041     maxLengthText : "The maximum length for this field is {0}",
9042   
9043     
9044     /**
9045      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9046      * If available, this function will be called only after the basic validators all return true, and will be passed the
9047      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9048      */
9049     validator : null,
9050     /**
9051      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9052      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9053      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9054      */
9055     regex : null,
9056     /**
9057      * @cfg {String} regexText -- Depricated - use Invalid Text
9058      */
9059     regexText : "",
9060     
9061     /**
9062      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9063      */
9064     invalidText : "",
9065     
9066     
9067     
9068     autocomplete: false,
9069     
9070     
9071     fieldLabel : '',
9072     inputType : 'text',
9073     
9074     name : false,
9075     placeholder: false,
9076     before : false,
9077     after : false,
9078     size : false,
9079     hasFocus : false,
9080     preventMark: false,
9081     isFormField : true,
9082     value : '',
9083     labelWidth : 2,
9084     labelAlign : false,
9085     readOnly : false,
9086     align : false,
9087     formatedValue : false,
9088     forceFeedback : false,
9089     
9090     indicatorpos : 'left',
9091     
9092     labellg : 0,
9093     labelmd : 0,
9094     labelsm : 0,
9095     labelxs : 0,
9096     
9097     capture : '',
9098     accept : '',
9099     
9100     parentLabelAlign : function()
9101     {
9102         var parent = this;
9103         while (parent.parent()) {
9104             parent = parent.parent();
9105             if (typeof(parent.labelAlign) !='undefined') {
9106                 return parent.labelAlign;
9107             }
9108         }
9109         return 'left';
9110         
9111     },
9112     
9113     getAutoCreate : function()
9114     {
9115         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9116         
9117         var id = Roo.id();
9118         
9119         var cfg = {};
9120         
9121         if(this.inputType != 'hidden'){
9122             cfg.cls = 'form-group' //input-group
9123         }
9124         
9125         var input =  {
9126             tag: 'input',
9127             id : id,
9128             type : this.inputType,
9129             value : this.value,
9130             cls : 'form-control',
9131             placeholder : this.placeholder || '',
9132             autocomplete : this.autocomplete || 'new-password'
9133         };
9134         
9135         if(this.capture.length){
9136             input.capture = this.capture;
9137         }
9138         
9139         if(this.accept.length){
9140             input.accept = this.accept + "/*";
9141         }
9142         
9143         if(this.align){
9144             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9145         }
9146         
9147         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9148             input.maxLength = this.maxLength;
9149         }
9150         
9151         if (this.disabled) {
9152             input.disabled=true;
9153         }
9154         
9155         if (this.readOnly) {
9156             input.readonly=true;
9157         }
9158         
9159         if (this.name) {
9160             input.name = this.name;
9161         }
9162         
9163         if (this.size) {
9164             input.cls += ' input-' + this.size;
9165         }
9166         
9167         var settings=this;
9168         ['xs','sm','md','lg'].map(function(size){
9169             if (settings[size]) {
9170                 cfg.cls += ' col-' + size + '-' + settings[size];
9171             }
9172         });
9173         
9174         var inputblock = input;
9175         
9176         var feedback = {
9177             tag: 'span',
9178             cls: 'glyphicon form-control-feedback'
9179         };
9180             
9181         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9182             
9183             inputblock = {
9184                 cls : 'has-feedback',
9185                 cn :  [
9186                     input,
9187                     feedback
9188                 ] 
9189             };  
9190         }
9191         
9192         if (this.before || this.after) {
9193             
9194             inputblock = {
9195                 cls : 'input-group',
9196                 cn :  [] 
9197             };
9198             
9199             if (this.before && typeof(this.before) == 'string') {
9200                 
9201                 inputblock.cn.push({
9202                     tag :'span',
9203                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9204                     html : this.before
9205                 });
9206             }
9207             if (this.before && typeof(this.before) == 'object') {
9208                 this.before = Roo.factory(this.before);
9209                 
9210                 inputblock.cn.push({
9211                     tag :'span',
9212                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9213                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9214                 });
9215             }
9216             
9217             inputblock.cn.push(input);
9218             
9219             if (this.after && typeof(this.after) == 'string') {
9220                 inputblock.cn.push({
9221                     tag :'span',
9222                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9223                     html : this.after
9224                 });
9225             }
9226             if (this.after && typeof(this.after) == 'object') {
9227                 this.after = Roo.factory(this.after);
9228                 
9229                 inputblock.cn.push({
9230                     tag :'span',
9231                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9232                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9233                 });
9234             }
9235             
9236             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9237                 inputblock.cls += ' has-feedback';
9238                 inputblock.cn.push(feedback);
9239             }
9240         };
9241         var indicator = {
9242             tag : 'i',
9243             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9244             tooltip : 'This field is required'
9245         };
9246         if (Roo.bootstrap.version == 4) {
9247             indicator = {
9248                 tag : 'i',
9249                 style : 'display-none'
9250             };
9251         }
9252         if (align ==='left' && this.fieldLabel.length) {
9253             
9254             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9255             
9256             cfg.cn = [
9257                 indicator,
9258                 {
9259                     tag: 'label',
9260                     'for' :  id,
9261                     cls : 'control-label col-form-label',
9262                     html : this.fieldLabel
9263
9264                 },
9265                 {
9266                     cls : "", 
9267                     cn: [
9268                         inputblock
9269                     ]
9270                 }
9271             ];
9272             
9273             var labelCfg = cfg.cn[1];
9274             var contentCfg = cfg.cn[2];
9275             
9276             if(this.indicatorpos == 'right'){
9277                 cfg.cn = [
9278                     {
9279                         tag: 'label',
9280                         'for' :  id,
9281                         cls : 'control-label col-form-label',
9282                         cn : [
9283                             {
9284                                 tag : 'span',
9285                                 html : this.fieldLabel
9286                             },
9287                             indicator
9288                         ]
9289                     },
9290                     {
9291                         cls : "",
9292                         cn: [
9293                             inputblock
9294                         ]
9295                     }
9296
9297                 ];
9298                 
9299                 labelCfg = cfg.cn[0];
9300                 contentCfg = cfg.cn[1];
9301             
9302             }
9303             
9304             if(this.labelWidth > 12){
9305                 labelCfg.style = "width: " + this.labelWidth + 'px';
9306             }
9307             
9308             if(this.labelWidth < 13 && this.labelmd == 0){
9309                 this.labelmd = this.labelWidth;
9310             }
9311             
9312             if(this.labellg > 0){
9313                 labelCfg.cls += ' col-lg-' + this.labellg;
9314                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9315             }
9316             
9317             if(this.labelmd > 0){
9318                 labelCfg.cls += ' col-md-' + this.labelmd;
9319                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9320             }
9321             
9322             if(this.labelsm > 0){
9323                 labelCfg.cls += ' col-sm-' + this.labelsm;
9324                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9325             }
9326             
9327             if(this.labelxs > 0){
9328                 labelCfg.cls += ' col-xs-' + this.labelxs;
9329                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9330             }
9331             
9332             
9333         } else if ( this.fieldLabel.length) {
9334                 
9335             cfg.cn = [
9336                 {
9337                     tag : 'i',
9338                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9339                     tooltip : 'This field is required'
9340                 },
9341                 {
9342                     tag: 'label',
9343                    //cls : 'input-group-addon',
9344                     html : this.fieldLabel
9345
9346                 },
9347
9348                inputblock
9349
9350            ];
9351            
9352            if(this.indicatorpos == 'right'){
9353                 
9354                 cfg.cn = [
9355                     {
9356                         tag: 'label',
9357                        //cls : 'input-group-addon',
9358                         html : this.fieldLabel
9359
9360                     },
9361                     {
9362                         tag : 'i',
9363                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9364                         tooltip : 'This field is required'
9365                     },
9366
9367                    inputblock
9368
9369                ];
9370
9371             }
9372
9373         } else {
9374             
9375             cfg.cn = [
9376
9377                     inputblock
9378
9379             ];
9380                 
9381                 
9382         };
9383         
9384         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9385            cfg.cls += ' navbar-form';
9386         }
9387         
9388         if (this.parentType === 'NavGroup') {
9389            cfg.cls += ' navbar-form';
9390            cfg.tag = 'li';
9391         }
9392         
9393         return cfg;
9394         
9395     },
9396     /**
9397      * return the real input element.
9398      */
9399     inputEl: function ()
9400     {
9401         return this.el.select('input.form-control',true).first();
9402     },
9403     
9404     tooltipEl : function()
9405     {
9406         return this.inputEl();
9407     },
9408     
9409     indicatorEl : function()
9410     {
9411         if (Roo.bootstrap.version == 4) {
9412             return false; // not enabled in v4 yet.
9413         }
9414         
9415         var indicator = this.el.select('i.roo-required-indicator',true).first();
9416         
9417         if(!indicator){
9418             return false;
9419         }
9420         
9421         return indicator;
9422         
9423     },
9424     
9425     setDisabled : function(v)
9426     {
9427         var i  = this.inputEl().dom;
9428         if (!v) {
9429             i.removeAttribute('disabled');
9430             return;
9431             
9432         }
9433         i.setAttribute('disabled','true');
9434     },
9435     initEvents : function()
9436     {
9437           
9438         this.inputEl().on("keydown" , this.fireKey,  this);
9439         this.inputEl().on("focus", this.onFocus,  this);
9440         this.inputEl().on("blur", this.onBlur,  this);
9441         
9442         this.inputEl().relayEvent('keyup', this);
9443         
9444         this.indicator = this.indicatorEl();
9445         
9446         if(this.indicator){
9447             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9448         }
9449  
9450         // reference to original value for reset
9451         this.originalValue = this.getValue();
9452         //Roo.form.TextField.superclass.initEvents.call(this);
9453         if(this.validationEvent == 'keyup'){
9454             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9455             this.inputEl().on('keyup', this.filterValidation, this);
9456         }
9457         else if(this.validationEvent !== false){
9458             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9459         }
9460         
9461         if(this.selectOnFocus){
9462             this.on("focus", this.preFocus, this);
9463             
9464         }
9465         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9466             this.inputEl().on("keypress", this.filterKeys, this);
9467         } else {
9468             this.inputEl().relayEvent('keypress', this);
9469         }
9470        /* if(this.grow){
9471             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9472             this.el.on("click", this.autoSize,  this);
9473         }
9474         */
9475         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9476             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9477         }
9478         
9479         if (typeof(this.before) == 'object') {
9480             this.before.render(this.el.select('.roo-input-before',true).first());
9481         }
9482         if (typeof(this.after) == 'object') {
9483             this.after.render(this.el.select('.roo-input-after',true).first());
9484         }
9485         
9486         this.inputEl().on('change', this.onChange, this);
9487         
9488     },
9489     filterValidation : function(e){
9490         if(!e.isNavKeyPress()){
9491             this.validationTask.delay(this.validationDelay);
9492         }
9493     },
9494      /**
9495      * Validates the field value
9496      * @return {Boolean} True if the value is valid, else false
9497      */
9498     validate : function(){
9499         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9500         if(this.disabled || this.validateValue(this.getRawValue())){
9501             this.markValid();
9502             return true;
9503         }
9504         
9505         this.markInvalid();
9506         return false;
9507     },
9508     
9509     
9510     /**
9511      * Validates a value according to the field's validation rules and marks the field as invalid
9512      * if the validation fails
9513      * @param {Mixed} value The value to validate
9514      * @return {Boolean} True if the value is valid, else false
9515      */
9516     validateValue : function(value)
9517     {
9518         if(this.getVisibilityEl().hasClass('hidden')){
9519             return true;
9520         }
9521         
9522         if(value.length < 1)  { // if it's blank
9523             if(this.allowBlank){
9524                 return true;
9525             }
9526             return false;
9527         }
9528         
9529         if(value.length < this.minLength){
9530             return false;
9531         }
9532         if(value.length > this.maxLength){
9533             return false;
9534         }
9535         if(this.vtype){
9536             var vt = Roo.form.VTypes;
9537             if(!vt[this.vtype](value, this)){
9538                 return false;
9539             }
9540         }
9541         if(typeof this.validator == "function"){
9542             var msg = this.validator(value);
9543             if(msg !== true){
9544                 return false;
9545             }
9546             if (typeof(msg) == 'string') {
9547                 this.invalidText = msg;
9548             }
9549         }
9550         
9551         if(this.regex && !this.regex.test(value)){
9552             return false;
9553         }
9554         
9555         return true;
9556     },
9557     
9558      // private
9559     fireKey : function(e){
9560         //Roo.log('field ' + e.getKey());
9561         if(e.isNavKeyPress()){
9562             this.fireEvent("specialkey", this, e);
9563         }
9564     },
9565     focus : function (selectText){
9566         if(this.rendered){
9567             this.inputEl().focus();
9568             if(selectText === true){
9569                 this.inputEl().dom.select();
9570             }
9571         }
9572         return this;
9573     } ,
9574     
9575     onFocus : function(){
9576         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9577            // this.el.addClass(this.focusClass);
9578         }
9579         if(!this.hasFocus){
9580             this.hasFocus = true;
9581             this.startValue = this.getValue();
9582             this.fireEvent("focus", this);
9583         }
9584     },
9585     
9586     beforeBlur : Roo.emptyFn,
9587
9588     
9589     // private
9590     onBlur : function(){
9591         this.beforeBlur();
9592         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9593             //this.el.removeClass(this.focusClass);
9594         }
9595         this.hasFocus = false;
9596         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9597             this.validate();
9598         }
9599         var v = this.getValue();
9600         if(String(v) !== String(this.startValue)){
9601             this.fireEvent('change', this, v, this.startValue);
9602         }
9603         this.fireEvent("blur", this);
9604     },
9605     
9606     onChange : function(e)
9607     {
9608         var v = this.getValue();
9609         if(String(v) !== String(this.startValue)){
9610             this.fireEvent('change', this, v, this.startValue);
9611         }
9612         
9613     },
9614     
9615     /**
9616      * Resets the current field value to the originally loaded value and clears any validation messages
9617      */
9618     reset : function(){
9619         this.setValue(this.originalValue);
9620         this.validate();
9621     },
9622      /**
9623      * Returns the name of the field
9624      * @return {Mixed} name The name field
9625      */
9626     getName: function(){
9627         return this.name;
9628     },
9629      /**
9630      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9631      * @return {Mixed} value The field value
9632      */
9633     getValue : function(){
9634         
9635         var v = this.inputEl().getValue();
9636         
9637         return v;
9638     },
9639     /**
9640      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9641      * @return {Mixed} value The field value
9642      */
9643     getRawValue : function(){
9644         var v = this.inputEl().getValue();
9645         
9646         return v;
9647     },
9648     
9649     /**
9650      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9651      * @param {Mixed} value The value to set
9652      */
9653     setRawValue : function(v){
9654         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9655     },
9656     
9657     selectText : function(start, end){
9658         var v = this.getRawValue();
9659         if(v.length > 0){
9660             start = start === undefined ? 0 : start;
9661             end = end === undefined ? v.length : end;
9662             var d = this.inputEl().dom;
9663             if(d.setSelectionRange){
9664                 d.setSelectionRange(start, end);
9665             }else if(d.createTextRange){
9666                 var range = d.createTextRange();
9667                 range.moveStart("character", start);
9668                 range.moveEnd("character", v.length-end);
9669                 range.select();
9670             }
9671         }
9672     },
9673     
9674     /**
9675      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9676      * @param {Mixed} value The value to set
9677      */
9678     setValue : function(v){
9679         this.value = v;
9680         if(this.rendered){
9681             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9682             this.validate();
9683         }
9684     },
9685     
9686     /*
9687     processValue : function(value){
9688         if(this.stripCharsRe){
9689             var newValue = value.replace(this.stripCharsRe, '');
9690             if(newValue !== value){
9691                 this.setRawValue(newValue);
9692                 return newValue;
9693             }
9694         }
9695         return value;
9696     },
9697   */
9698     preFocus : function(){
9699         
9700         if(this.selectOnFocus){
9701             this.inputEl().dom.select();
9702         }
9703     },
9704     filterKeys : function(e){
9705         var k = e.getKey();
9706         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9707             return;
9708         }
9709         var c = e.getCharCode(), cc = String.fromCharCode(c);
9710         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9711             return;
9712         }
9713         if(!this.maskRe.test(cc)){
9714             e.stopEvent();
9715         }
9716     },
9717      /**
9718      * Clear any invalid styles/messages for this field
9719      */
9720     clearInvalid : function(){
9721         
9722         if(!this.el || this.preventMark){ // not rendered
9723             return;
9724         }
9725         
9726      
9727         this.el.removeClass(this.invalidClass);
9728         
9729         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9730             
9731             var feedback = this.el.select('.form-control-feedback', true).first();
9732             
9733             if(feedback){
9734                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9735             }
9736             
9737         }
9738         
9739         if(this.indicator){
9740             this.indicator.removeClass('visible');
9741             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9742         }
9743         
9744         this.fireEvent('valid', this);
9745     },
9746     
9747      /**
9748      * Mark this field as valid
9749      */
9750     markValid : function()
9751     {
9752         if(!this.el  || this.preventMark){ // not rendered...
9753             return;
9754         }
9755         
9756         this.el.removeClass([this.invalidClass, this.validClass]);
9757         
9758         var feedback = this.el.select('.form-control-feedback', true).first();
9759             
9760         if(feedback){
9761             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9762         }
9763         
9764         if(this.indicator){
9765             this.indicator.removeClass('visible');
9766             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9767         }
9768         
9769         if(this.disabled){
9770             return;
9771         }
9772         
9773         if(this.allowBlank && !this.getRawValue().length){
9774             return;
9775         }
9776         
9777         this.el.addClass(this.validClass);
9778         
9779         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9780             
9781             var feedback = this.el.select('.form-control-feedback', true).first();
9782             
9783             if(feedback){
9784                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9785                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9786             }
9787             
9788         }
9789         
9790         this.fireEvent('valid', this);
9791     },
9792     
9793      /**
9794      * Mark this field as invalid
9795      * @param {String} msg The validation message
9796      */
9797     markInvalid : function(msg)
9798     {
9799         if(!this.el  || this.preventMark){ // not rendered
9800             return;
9801         }
9802         
9803         this.el.removeClass([this.invalidClass, this.validClass]);
9804         
9805         var feedback = this.el.select('.form-control-feedback', true).first();
9806             
9807         if(feedback){
9808             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9809         }
9810
9811         if(this.disabled){
9812             return;
9813         }
9814         
9815         if(this.allowBlank && !this.getRawValue().length){
9816             return;
9817         }
9818         
9819         if(this.indicator){
9820             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9821             this.indicator.addClass('visible');
9822         }
9823         
9824         this.el.addClass(this.invalidClass);
9825         
9826         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9827             
9828             var feedback = this.el.select('.form-control-feedback', true).first();
9829             
9830             if(feedback){
9831                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9832                 
9833                 if(this.getValue().length || this.forceFeedback){
9834                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9835                 }
9836                 
9837             }
9838             
9839         }
9840         
9841         this.fireEvent('invalid', this, msg);
9842     },
9843     // private
9844     SafariOnKeyDown : function(event)
9845     {
9846         // this is a workaround for a password hang bug on chrome/ webkit.
9847         if (this.inputEl().dom.type != 'password') {
9848             return;
9849         }
9850         
9851         var isSelectAll = false;
9852         
9853         if(this.inputEl().dom.selectionEnd > 0){
9854             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9855         }
9856         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9857             event.preventDefault();
9858             this.setValue('');
9859             return;
9860         }
9861         
9862         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9863             
9864             event.preventDefault();
9865             // this is very hacky as keydown always get's upper case.
9866             //
9867             var cc = String.fromCharCode(event.getCharCode());
9868             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9869             
9870         }
9871     },
9872     adjustWidth : function(tag, w){
9873         tag = tag.toLowerCase();
9874         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9875             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9876                 if(tag == 'input'){
9877                     return w + 2;
9878                 }
9879                 if(tag == 'textarea'){
9880                     return w-2;
9881                 }
9882             }else if(Roo.isOpera){
9883                 if(tag == 'input'){
9884                     return w + 2;
9885                 }
9886                 if(tag == 'textarea'){
9887                     return w-2;
9888                 }
9889             }
9890         }
9891         return w;
9892     },
9893     
9894     setFieldLabel : function(v)
9895     {
9896         if(!this.rendered){
9897             return;
9898         }
9899         
9900         if(this.indicatorEl()){
9901             var ar = this.el.select('label > span',true);
9902             
9903             if (ar.elements.length) {
9904                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9905                 this.fieldLabel = v;
9906                 return;
9907             }
9908             
9909             var br = this.el.select('label',true);
9910             
9911             if(br.elements.length) {
9912                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9913                 this.fieldLabel = v;
9914                 return;
9915             }
9916             
9917             Roo.log('Cannot Found any of label > span || label in input');
9918             return;
9919         }
9920         
9921         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9922         this.fieldLabel = v;
9923         
9924         
9925     }
9926 });
9927
9928  
9929 /*
9930  * - LGPL
9931  *
9932  * Input
9933  * 
9934  */
9935
9936 /**
9937  * @class Roo.bootstrap.TextArea
9938  * @extends Roo.bootstrap.Input
9939  * Bootstrap TextArea class
9940  * @cfg {Number} cols Specifies the visible width of a text area
9941  * @cfg {Number} rows Specifies the visible number of lines in a text area
9942  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9943  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9944  * @cfg {string} html text
9945  * 
9946  * @constructor
9947  * Create a new TextArea
9948  * @param {Object} config The config object
9949  */
9950
9951 Roo.bootstrap.TextArea = function(config){
9952     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9953    
9954 };
9955
9956 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9957      
9958     cols : false,
9959     rows : 5,
9960     readOnly : false,
9961     warp : 'soft',
9962     resize : false,
9963     value: false,
9964     html: false,
9965     
9966     getAutoCreate : function(){
9967         
9968         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9969         
9970         var id = Roo.id();
9971         
9972         var cfg = {};
9973         
9974         if(this.inputType != 'hidden'){
9975             cfg.cls = 'form-group' //input-group
9976         }
9977         
9978         var input =  {
9979             tag: 'textarea',
9980             id : id,
9981             warp : this.warp,
9982             rows : this.rows,
9983             value : this.value || '',
9984             html: this.html || '',
9985             cls : 'form-control',
9986             placeholder : this.placeholder || '' 
9987             
9988         };
9989         
9990         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9991             input.maxLength = this.maxLength;
9992         }
9993         
9994         if(this.resize){
9995             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9996         }
9997         
9998         if(this.cols){
9999             input.cols = this.cols;
10000         }
10001         
10002         if (this.readOnly) {
10003             input.readonly = true;
10004         }
10005         
10006         if (this.name) {
10007             input.name = this.name;
10008         }
10009         
10010         if (this.size) {
10011             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10012         }
10013         
10014         var settings=this;
10015         ['xs','sm','md','lg'].map(function(size){
10016             if (settings[size]) {
10017                 cfg.cls += ' col-' + size + '-' + settings[size];
10018             }
10019         });
10020         
10021         var inputblock = input;
10022         
10023         if(this.hasFeedback && !this.allowBlank){
10024             
10025             var feedback = {
10026                 tag: 'span',
10027                 cls: 'glyphicon form-control-feedback'
10028             };
10029
10030             inputblock = {
10031                 cls : 'has-feedback',
10032                 cn :  [
10033                     input,
10034                     feedback
10035                 ] 
10036             };  
10037         }
10038         
10039         
10040         if (this.before || this.after) {
10041             
10042             inputblock = {
10043                 cls : 'input-group',
10044                 cn :  [] 
10045             };
10046             if (this.before) {
10047                 inputblock.cn.push({
10048                     tag :'span',
10049                     cls : 'input-group-addon',
10050                     html : this.before
10051                 });
10052             }
10053             
10054             inputblock.cn.push(input);
10055             
10056             if(this.hasFeedback && !this.allowBlank){
10057                 inputblock.cls += ' has-feedback';
10058                 inputblock.cn.push(feedback);
10059             }
10060             
10061             if (this.after) {
10062                 inputblock.cn.push({
10063                     tag :'span',
10064                     cls : 'input-group-addon',
10065                     html : this.after
10066                 });
10067             }
10068             
10069         }
10070         
10071         if (align ==='left' && this.fieldLabel.length) {
10072             cfg.cn = [
10073                 {
10074                     tag: 'label',
10075                     'for' :  id,
10076                     cls : 'control-label',
10077                     html : this.fieldLabel
10078                 },
10079                 {
10080                     cls : "",
10081                     cn: [
10082                         inputblock
10083                     ]
10084                 }
10085
10086             ];
10087             
10088             if(this.labelWidth > 12){
10089                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10090             }
10091
10092             if(this.labelWidth < 13 && this.labelmd == 0){
10093                 this.labelmd = this.labelWidth;
10094             }
10095
10096             if(this.labellg > 0){
10097                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10098                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10099             }
10100
10101             if(this.labelmd > 0){
10102                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10103                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10104             }
10105
10106             if(this.labelsm > 0){
10107                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10108                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10109             }
10110
10111             if(this.labelxs > 0){
10112                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10113                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10114             }
10115             
10116         } else if ( this.fieldLabel.length) {
10117             cfg.cn = [
10118
10119                {
10120                    tag: 'label',
10121                    //cls : 'input-group-addon',
10122                    html : this.fieldLabel
10123
10124                },
10125
10126                inputblock
10127
10128            ];
10129
10130         } else {
10131
10132             cfg.cn = [
10133
10134                 inputblock
10135
10136             ];
10137                 
10138         }
10139         
10140         if (this.disabled) {
10141             input.disabled=true;
10142         }
10143         
10144         return cfg;
10145         
10146     },
10147     /**
10148      * return the real textarea element.
10149      */
10150     inputEl: function ()
10151     {
10152         return this.el.select('textarea.form-control',true).first();
10153     },
10154     
10155     /**
10156      * Clear any invalid styles/messages for this field
10157      */
10158     clearInvalid : function()
10159     {
10160         
10161         if(!this.el || this.preventMark){ // not rendered
10162             return;
10163         }
10164         
10165         var label = this.el.select('label', true).first();
10166         var icon = this.el.select('i.fa-star', true).first();
10167         
10168         if(label && icon){
10169             icon.remove();
10170         }
10171         
10172         this.el.removeClass(this.invalidClass);
10173         
10174         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10175             
10176             var feedback = this.el.select('.form-control-feedback', true).first();
10177             
10178             if(feedback){
10179                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10180             }
10181             
10182         }
10183         
10184         this.fireEvent('valid', this);
10185     },
10186     
10187      /**
10188      * Mark this field as valid
10189      */
10190     markValid : function()
10191     {
10192         if(!this.el  || this.preventMark){ // not rendered
10193             return;
10194         }
10195         
10196         this.el.removeClass([this.invalidClass, this.validClass]);
10197         
10198         var feedback = this.el.select('.form-control-feedback', true).first();
10199             
10200         if(feedback){
10201             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10202         }
10203
10204         if(this.disabled || this.allowBlank){
10205             return;
10206         }
10207         
10208         var label = this.el.select('label', true).first();
10209         var icon = this.el.select('i.fa-star', true).first();
10210         
10211         if(label && icon){
10212             icon.remove();
10213         }
10214         
10215         this.el.addClass(this.validClass);
10216         
10217         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10218             
10219             var feedback = this.el.select('.form-control-feedback', true).first();
10220             
10221             if(feedback){
10222                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10223                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10224             }
10225             
10226         }
10227         
10228         this.fireEvent('valid', this);
10229     },
10230     
10231      /**
10232      * Mark this field as invalid
10233      * @param {String} msg The validation message
10234      */
10235     markInvalid : function(msg)
10236     {
10237         if(!this.el  || this.preventMark){ // not rendered
10238             return;
10239         }
10240         
10241         this.el.removeClass([this.invalidClass, this.validClass]);
10242         
10243         var feedback = this.el.select('.form-control-feedback', true).first();
10244             
10245         if(feedback){
10246             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10247         }
10248
10249         if(this.disabled || this.allowBlank){
10250             return;
10251         }
10252         
10253         var label = this.el.select('label', true).first();
10254         var icon = this.el.select('i.fa-star', true).first();
10255         
10256         if(!this.getValue().length && label && !icon){
10257             this.el.createChild({
10258                 tag : 'i',
10259                 cls : 'text-danger fa fa-lg fa-star',
10260                 tooltip : 'This field is required',
10261                 style : 'margin-right:5px;'
10262             }, label, true);
10263         }
10264
10265         this.el.addClass(this.invalidClass);
10266         
10267         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10268             
10269             var feedback = this.el.select('.form-control-feedback', true).first();
10270             
10271             if(feedback){
10272                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10273                 
10274                 if(this.getValue().length || this.forceFeedback){
10275                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10276                 }
10277                 
10278             }
10279             
10280         }
10281         
10282         this.fireEvent('invalid', this, msg);
10283     }
10284 });
10285
10286  
10287 /*
10288  * - LGPL
10289  *
10290  * trigger field - base class for combo..
10291  * 
10292  */
10293  
10294 /**
10295  * @class Roo.bootstrap.TriggerField
10296  * @extends Roo.bootstrap.Input
10297  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10298  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10299  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10300  * for which you can provide a custom implementation.  For example:
10301  * <pre><code>
10302 var trigger = new Roo.bootstrap.TriggerField();
10303 trigger.onTriggerClick = myTriggerFn;
10304 trigger.applyTo('my-field');
10305 </code></pre>
10306  *
10307  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10308  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10309  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10310  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10311  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10312
10313  * @constructor
10314  * Create a new TriggerField.
10315  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10316  * to the base TextField)
10317  */
10318 Roo.bootstrap.TriggerField = function(config){
10319     this.mimicing = false;
10320     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10321 };
10322
10323 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10324     /**
10325      * @cfg {String} triggerClass A CSS class to apply to the trigger
10326      */
10327      /**
10328      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10329      */
10330     hideTrigger:false,
10331
10332     /**
10333      * @cfg {Boolean} removable (true|false) special filter default false
10334      */
10335     removable : false,
10336     
10337     /** @cfg {Boolean} grow @hide */
10338     /** @cfg {Number} growMin @hide */
10339     /** @cfg {Number} growMax @hide */
10340
10341     /**
10342      * @hide 
10343      * @method
10344      */
10345     autoSize: Roo.emptyFn,
10346     // private
10347     monitorTab : true,
10348     // private
10349     deferHeight : true,
10350
10351     
10352     actionMode : 'wrap',
10353     
10354     caret : false,
10355     
10356     
10357     getAutoCreate : function(){
10358        
10359         var align = this.labelAlign || this.parentLabelAlign();
10360         
10361         var id = Roo.id();
10362         
10363         var cfg = {
10364             cls: 'form-group' //input-group
10365         };
10366         
10367         
10368         var input =  {
10369             tag: 'input',
10370             id : id,
10371             type : this.inputType,
10372             cls : 'form-control',
10373             autocomplete: 'new-password',
10374             placeholder : this.placeholder || '' 
10375             
10376         };
10377         if (this.name) {
10378             input.name = this.name;
10379         }
10380         if (this.size) {
10381             input.cls += ' input-' + this.size;
10382         }
10383         
10384         if (this.disabled) {
10385             input.disabled=true;
10386         }
10387         
10388         var inputblock = input;
10389         
10390         if(this.hasFeedback && !this.allowBlank){
10391             
10392             var feedback = {
10393                 tag: 'span',
10394                 cls: 'glyphicon form-control-feedback'
10395             };
10396             
10397             if(this.removable && !this.editable && !this.tickable){
10398                 inputblock = {
10399                     cls : 'has-feedback',
10400                     cn :  [
10401                         inputblock,
10402                         {
10403                             tag: 'button',
10404                             html : 'x',
10405                             cls : 'roo-combo-removable-btn close'
10406                         },
10407                         feedback
10408                     ] 
10409                 };
10410             } else {
10411                 inputblock = {
10412                     cls : 'has-feedback',
10413                     cn :  [
10414                         inputblock,
10415                         feedback
10416                     ] 
10417                 };
10418             }
10419
10420         } else {
10421             if(this.removable && !this.editable && !this.tickable){
10422                 inputblock = {
10423                     cls : 'roo-removable',
10424                     cn :  [
10425                         inputblock,
10426                         {
10427                             tag: 'button',
10428                             html : 'x',
10429                             cls : 'roo-combo-removable-btn close'
10430                         }
10431                     ] 
10432                 };
10433             }
10434         }
10435         
10436         if (this.before || this.after) {
10437             
10438             inputblock = {
10439                 cls : 'input-group',
10440                 cn :  [] 
10441             };
10442             if (this.before) {
10443                 inputblock.cn.push({
10444                     tag :'span',
10445                     cls : 'input-group-addon input-group-prepend input-group-text',
10446                     html : this.before
10447                 });
10448             }
10449             
10450             inputblock.cn.push(input);
10451             
10452             if(this.hasFeedback && !this.allowBlank){
10453                 inputblock.cls += ' has-feedback';
10454                 inputblock.cn.push(feedback);
10455             }
10456             
10457             if (this.after) {
10458                 inputblock.cn.push({
10459                     tag :'span',
10460                     cls : 'input-group-addon input-group-append input-group-text',
10461                     html : this.after
10462                 });
10463             }
10464             
10465         };
10466         
10467       
10468         
10469         var ibwrap = inputblock;
10470         
10471         if(this.multiple){
10472             ibwrap = {
10473                 tag: 'ul',
10474                 cls: 'roo-select2-choices',
10475                 cn:[
10476                     {
10477                         tag: 'li',
10478                         cls: 'roo-select2-search-field',
10479                         cn: [
10480
10481                             inputblock
10482                         ]
10483                     }
10484                 ]
10485             };
10486                 
10487         }
10488         
10489         var combobox = {
10490             cls: 'roo-select2-container input-group',
10491             cn: [
10492                  {
10493                     tag: 'input',
10494                     type : 'hidden',
10495                     cls: 'form-hidden-field'
10496                 },
10497                 ibwrap
10498             ]
10499         };
10500         
10501         if(!this.multiple && this.showToggleBtn){
10502             
10503             var caret = {
10504                         tag: 'span',
10505                         cls: 'caret'
10506              };
10507             if (this.caret != false) {
10508                 caret = {
10509                      tag: 'i',
10510                      cls: 'fa fa-' + this.caret
10511                 };
10512                 
10513             }
10514             
10515             combobox.cn.push({
10516                 tag :'span',
10517                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10518                 cn : [
10519                     caret,
10520                     {
10521                         tag: 'span',
10522                         cls: 'combobox-clear',
10523                         cn  : [
10524                             {
10525                                 tag : 'i',
10526                                 cls: 'icon-remove'
10527                             }
10528                         ]
10529                     }
10530                 ]
10531
10532             })
10533         }
10534         
10535         if(this.multiple){
10536             combobox.cls += ' roo-select2-container-multi';
10537         }
10538          var indicator = {
10539             tag : 'i',
10540             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10541             tooltip : 'This field is required'
10542         };
10543         if (Roo.bootstrap.version == 4) {
10544             indicator = {
10545                 tag : 'i',
10546                 style : 'display:none'
10547             };
10548         }
10549         
10550         
10551         if (align ==='left' && this.fieldLabel.length) {
10552             
10553             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10554
10555             cfg.cn = [
10556                 indicator,
10557                 {
10558                     tag: 'label',
10559                     'for' :  id,
10560                     cls : 'control-label',
10561                     html : this.fieldLabel
10562
10563                 },
10564                 {
10565                     cls : "", 
10566                     cn: [
10567                         combobox
10568                     ]
10569                 }
10570
10571             ];
10572             
10573             var labelCfg = cfg.cn[1];
10574             var contentCfg = cfg.cn[2];
10575             
10576             if(this.indicatorpos == 'right'){
10577                 cfg.cn = [
10578                     {
10579                         tag: 'label',
10580                         'for' :  id,
10581                         cls : 'control-label',
10582                         cn : [
10583                             {
10584                                 tag : 'span',
10585                                 html : this.fieldLabel
10586                             },
10587                             indicator
10588                         ]
10589                     },
10590                     {
10591                         cls : "", 
10592                         cn: [
10593                             combobox
10594                         ]
10595                     }
10596
10597                 ];
10598                 
10599                 labelCfg = cfg.cn[0];
10600                 contentCfg = cfg.cn[1];
10601             }
10602             
10603             if(this.labelWidth > 12){
10604                 labelCfg.style = "width: " + this.labelWidth + 'px';
10605             }
10606             
10607             if(this.labelWidth < 13 && this.labelmd == 0){
10608                 this.labelmd = this.labelWidth;
10609             }
10610             
10611             if(this.labellg > 0){
10612                 labelCfg.cls += ' col-lg-' + this.labellg;
10613                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10614             }
10615             
10616             if(this.labelmd > 0){
10617                 labelCfg.cls += ' col-md-' + this.labelmd;
10618                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10619             }
10620             
10621             if(this.labelsm > 0){
10622                 labelCfg.cls += ' col-sm-' + this.labelsm;
10623                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10624             }
10625             
10626             if(this.labelxs > 0){
10627                 labelCfg.cls += ' col-xs-' + this.labelxs;
10628                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10629             }
10630             
10631         } else if ( this.fieldLabel.length) {
10632 //                Roo.log(" label");
10633             cfg.cn = [
10634                 indicator,
10635                {
10636                    tag: 'label',
10637                    //cls : 'input-group-addon',
10638                    html : this.fieldLabel
10639
10640                },
10641
10642                combobox
10643
10644             ];
10645             
10646             if(this.indicatorpos == 'right'){
10647                 
10648                 cfg.cn = [
10649                     {
10650                        tag: 'label',
10651                        cn : [
10652                            {
10653                                tag : 'span',
10654                                html : this.fieldLabel
10655                            },
10656                            indicator
10657                        ]
10658
10659                     },
10660                     combobox
10661
10662                 ];
10663
10664             }
10665
10666         } else {
10667             
10668 //                Roo.log(" no label && no align");
10669                 cfg = combobox
10670                      
10671                 
10672         }
10673         
10674         var settings=this;
10675         ['xs','sm','md','lg'].map(function(size){
10676             if (settings[size]) {
10677                 cfg.cls += ' col-' + size + '-' + settings[size];
10678             }
10679         });
10680         
10681         return cfg;
10682         
10683     },
10684     
10685     
10686     
10687     // private
10688     onResize : function(w, h){
10689 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10690 //        if(typeof w == 'number'){
10691 //            var x = w - this.trigger.getWidth();
10692 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10693 //            this.trigger.setStyle('left', x+'px');
10694 //        }
10695     },
10696
10697     // private
10698     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10699
10700     // private
10701     getResizeEl : function(){
10702         return this.inputEl();
10703     },
10704
10705     // private
10706     getPositionEl : function(){
10707         return this.inputEl();
10708     },
10709
10710     // private
10711     alignErrorIcon : function(){
10712         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10713     },
10714
10715     // private
10716     initEvents : function(){
10717         
10718         this.createList();
10719         
10720         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10721         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10722         if(!this.multiple && this.showToggleBtn){
10723             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10724             if(this.hideTrigger){
10725                 this.trigger.setDisplayed(false);
10726             }
10727             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10728         }
10729         
10730         if(this.multiple){
10731             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10732         }
10733         
10734         if(this.removable && !this.editable && !this.tickable){
10735             var close = this.closeTriggerEl();
10736             
10737             if(close){
10738                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10739                 close.on('click', this.removeBtnClick, this, close);
10740             }
10741         }
10742         
10743         //this.trigger.addClassOnOver('x-form-trigger-over');
10744         //this.trigger.addClassOnClick('x-form-trigger-click');
10745         
10746         //if(!this.width){
10747         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10748         //}
10749     },
10750     
10751     closeTriggerEl : function()
10752     {
10753         var close = this.el.select('.roo-combo-removable-btn', true).first();
10754         return close ? close : false;
10755     },
10756     
10757     removeBtnClick : function(e, h, el)
10758     {
10759         e.preventDefault();
10760         
10761         if(this.fireEvent("remove", this) !== false){
10762             this.reset();
10763             this.fireEvent("afterremove", this)
10764         }
10765     },
10766     
10767     createList : function()
10768     {
10769         this.list = Roo.get(document.body).createChild({
10770             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10771             cls: 'typeahead typeahead-long dropdown-menu',
10772             style: 'display:none'
10773         });
10774         
10775         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10776         
10777     },
10778
10779     // private
10780     initTrigger : function(){
10781        
10782     },
10783
10784     // private
10785     onDestroy : function(){
10786         if(this.trigger){
10787             this.trigger.removeAllListeners();
10788           //  this.trigger.remove();
10789         }
10790         //if(this.wrap){
10791         //    this.wrap.remove();
10792         //}
10793         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10794     },
10795
10796     // private
10797     onFocus : function(){
10798         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10799         /*
10800         if(!this.mimicing){
10801             this.wrap.addClass('x-trigger-wrap-focus');
10802             this.mimicing = true;
10803             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10804             if(this.monitorTab){
10805                 this.el.on("keydown", this.checkTab, this);
10806             }
10807         }
10808         */
10809     },
10810
10811     // private
10812     checkTab : function(e){
10813         if(e.getKey() == e.TAB){
10814             this.triggerBlur();
10815         }
10816     },
10817
10818     // private
10819     onBlur : function(){
10820         // do nothing
10821     },
10822
10823     // private
10824     mimicBlur : function(e, t){
10825         /*
10826         if(!this.wrap.contains(t) && this.validateBlur()){
10827             this.triggerBlur();
10828         }
10829         */
10830     },
10831
10832     // private
10833     triggerBlur : function(){
10834         this.mimicing = false;
10835         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10836         if(this.monitorTab){
10837             this.el.un("keydown", this.checkTab, this);
10838         }
10839         //this.wrap.removeClass('x-trigger-wrap-focus');
10840         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10841     },
10842
10843     // private
10844     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10845     validateBlur : function(e, t){
10846         return true;
10847     },
10848
10849     // private
10850     onDisable : function(){
10851         this.inputEl().dom.disabled = true;
10852         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10853         //if(this.wrap){
10854         //    this.wrap.addClass('x-item-disabled');
10855         //}
10856     },
10857
10858     // private
10859     onEnable : function(){
10860         this.inputEl().dom.disabled = false;
10861         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10862         //if(this.wrap){
10863         //    this.el.removeClass('x-item-disabled');
10864         //}
10865     },
10866
10867     // private
10868     onShow : function(){
10869         var ae = this.getActionEl();
10870         
10871         if(ae){
10872             ae.dom.style.display = '';
10873             ae.dom.style.visibility = 'visible';
10874         }
10875     },
10876
10877     // private
10878     
10879     onHide : function(){
10880         var ae = this.getActionEl();
10881         ae.dom.style.display = 'none';
10882     },
10883
10884     /**
10885      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10886      * by an implementing function.
10887      * @method
10888      * @param {EventObject} e
10889      */
10890     onTriggerClick : Roo.emptyFn
10891 });
10892  /*
10893  * Based on:
10894  * Ext JS Library 1.1.1
10895  * Copyright(c) 2006-2007, Ext JS, LLC.
10896  *
10897  * Originally Released Under LGPL - original licence link has changed is not relivant.
10898  *
10899  * Fork - LGPL
10900  * <script type="text/javascript">
10901  */
10902
10903
10904 /**
10905  * @class Roo.data.SortTypes
10906  * @singleton
10907  * Defines the default sorting (casting?) comparison functions used when sorting data.
10908  */
10909 Roo.data.SortTypes = {
10910     /**
10911      * Default sort that does nothing
10912      * @param {Mixed} s The value being converted
10913      * @return {Mixed} The comparison value
10914      */
10915     none : function(s){
10916         return s;
10917     },
10918     
10919     /**
10920      * The regular expression used to strip tags
10921      * @type {RegExp}
10922      * @property
10923      */
10924     stripTagsRE : /<\/?[^>]+>/gi,
10925     
10926     /**
10927      * Strips all HTML tags to sort on text only
10928      * @param {Mixed} s The value being converted
10929      * @return {String} The comparison value
10930      */
10931     asText : function(s){
10932         return String(s).replace(this.stripTagsRE, "");
10933     },
10934     
10935     /**
10936      * Strips all HTML tags to sort on text only - Case insensitive
10937      * @param {Mixed} s The value being converted
10938      * @return {String} The comparison value
10939      */
10940     asUCText : function(s){
10941         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10942     },
10943     
10944     /**
10945      * Case insensitive string
10946      * @param {Mixed} s The value being converted
10947      * @return {String} The comparison value
10948      */
10949     asUCString : function(s) {
10950         return String(s).toUpperCase();
10951     },
10952     
10953     /**
10954      * Date sorting
10955      * @param {Mixed} s The value being converted
10956      * @return {Number} The comparison value
10957      */
10958     asDate : function(s) {
10959         if(!s){
10960             return 0;
10961         }
10962         if(s instanceof Date){
10963             return s.getTime();
10964         }
10965         return Date.parse(String(s));
10966     },
10967     
10968     /**
10969      * Float sorting
10970      * @param {Mixed} s The value being converted
10971      * @return {Float} The comparison value
10972      */
10973     asFloat : function(s) {
10974         var val = parseFloat(String(s).replace(/,/g, ""));
10975         if(isNaN(val)) {
10976             val = 0;
10977         }
10978         return val;
10979     },
10980     
10981     /**
10982      * Integer sorting
10983      * @param {Mixed} s The value being converted
10984      * @return {Number} The comparison value
10985      */
10986     asInt : function(s) {
10987         var val = parseInt(String(s).replace(/,/g, ""));
10988         if(isNaN(val)) {
10989             val = 0;
10990         }
10991         return val;
10992     }
10993 };/*
10994  * Based on:
10995  * Ext JS Library 1.1.1
10996  * Copyright(c) 2006-2007, Ext JS, LLC.
10997  *
10998  * Originally Released Under LGPL - original licence link has changed is not relivant.
10999  *
11000  * Fork - LGPL
11001  * <script type="text/javascript">
11002  */
11003
11004 /**
11005 * @class Roo.data.Record
11006  * Instances of this class encapsulate both record <em>definition</em> information, and record
11007  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11008  * to access Records cached in an {@link Roo.data.Store} object.<br>
11009  * <p>
11010  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11011  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11012  * objects.<br>
11013  * <p>
11014  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11015  * @constructor
11016  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11017  * {@link #create}. The parameters are the same.
11018  * @param {Array} data An associative Array of data values keyed by the field name.
11019  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11020  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11021  * not specified an integer id is generated.
11022  */
11023 Roo.data.Record = function(data, id){
11024     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11025     this.data = data;
11026 };
11027
11028 /**
11029  * Generate a constructor for a specific record layout.
11030  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11031  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11032  * Each field definition object may contain the following properties: <ul>
11033  * <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,
11034  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11035  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11036  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11037  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11038  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11039  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11040  * this may be omitted.</p></li>
11041  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11042  * <ul><li>auto (Default, implies no conversion)</li>
11043  * <li>string</li>
11044  * <li>int</li>
11045  * <li>float</li>
11046  * <li>boolean</li>
11047  * <li>date</li></ul></p></li>
11048  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11049  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11050  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11051  * by the Reader into an object that will be stored in the Record. It is passed the
11052  * following parameters:<ul>
11053  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11054  * </ul></p></li>
11055  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11056  * </ul>
11057  * <br>usage:<br><pre><code>
11058 var TopicRecord = Roo.data.Record.create(
11059     {name: 'title', mapping: 'topic_title'},
11060     {name: 'author', mapping: 'username'},
11061     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11062     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11063     {name: 'lastPoster', mapping: 'user2'},
11064     {name: 'excerpt', mapping: 'post_text'}
11065 );
11066
11067 var myNewRecord = new TopicRecord({
11068     title: 'Do my job please',
11069     author: 'noobie',
11070     totalPosts: 1,
11071     lastPost: new Date(),
11072     lastPoster: 'Animal',
11073     excerpt: 'No way dude!'
11074 });
11075 myStore.add(myNewRecord);
11076 </code></pre>
11077  * @method create
11078  * @static
11079  */
11080 Roo.data.Record.create = function(o){
11081     var f = function(){
11082         f.superclass.constructor.apply(this, arguments);
11083     };
11084     Roo.extend(f, Roo.data.Record);
11085     var p = f.prototype;
11086     p.fields = new Roo.util.MixedCollection(false, function(field){
11087         return field.name;
11088     });
11089     for(var i = 0, len = o.length; i < len; i++){
11090         p.fields.add(new Roo.data.Field(o[i]));
11091     }
11092     f.getField = function(name){
11093         return p.fields.get(name);  
11094     };
11095     return f;
11096 };
11097
11098 Roo.data.Record.AUTO_ID = 1000;
11099 Roo.data.Record.EDIT = 'edit';
11100 Roo.data.Record.REJECT = 'reject';
11101 Roo.data.Record.COMMIT = 'commit';
11102
11103 Roo.data.Record.prototype = {
11104     /**
11105      * Readonly flag - true if this record has been modified.
11106      * @type Boolean
11107      */
11108     dirty : false,
11109     editing : false,
11110     error: null,
11111     modified: null,
11112
11113     // private
11114     join : function(store){
11115         this.store = store;
11116     },
11117
11118     /**
11119      * Set the named field to the specified value.
11120      * @param {String} name The name of the field to set.
11121      * @param {Object} value The value to set the field to.
11122      */
11123     set : function(name, value){
11124         if(this.data[name] == value){
11125             return;
11126         }
11127         this.dirty = true;
11128         if(!this.modified){
11129             this.modified = {};
11130         }
11131         if(typeof this.modified[name] == 'undefined'){
11132             this.modified[name] = this.data[name];
11133         }
11134         this.data[name] = value;
11135         if(!this.editing && this.store){
11136             this.store.afterEdit(this);
11137         }       
11138     },
11139
11140     /**
11141      * Get the value of the named field.
11142      * @param {String} name The name of the field to get the value of.
11143      * @return {Object} The value of the field.
11144      */
11145     get : function(name){
11146         return this.data[name]; 
11147     },
11148
11149     // private
11150     beginEdit : function(){
11151         this.editing = true;
11152         this.modified = {}; 
11153     },
11154
11155     // private
11156     cancelEdit : function(){
11157         this.editing = false;
11158         delete this.modified;
11159     },
11160
11161     // private
11162     endEdit : function(){
11163         this.editing = false;
11164         if(this.dirty && this.store){
11165             this.store.afterEdit(this);
11166         }
11167     },
11168
11169     /**
11170      * Usually called by the {@link Roo.data.Store} which owns the Record.
11171      * Rejects all changes made to the Record since either creation, or the last commit operation.
11172      * Modified fields are reverted to their original values.
11173      * <p>
11174      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11175      * of reject operations.
11176      */
11177     reject : function(){
11178         var m = this.modified;
11179         for(var n in m){
11180             if(typeof m[n] != "function"){
11181                 this.data[n] = m[n];
11182             }
11183         }
11184         this.dirty = false;
11185         delete this.modified;
11186         this.editing = false;
11187         if(this.store){
11188             this.store.afterReject(this);
11189         }
11190     },
11191
11192     /**
11193      * Usually called by the {@link Roo.data.Store} which owns the Record.
11194      * Commits all changes made to the Record since either creation, or the last commit operation.
11195      * <p>
11196      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11197      * of commit operations.
11198      */
11199     commit : function(){
11200         this.dirty = false;
11201         delete this.modified;
11202         this.editing = false;
11203         if(this.store){
11204             this.store.afterCommit(this);
11205         }
11206     },
11207
11208     // private
11209     hasError : function(){
11210         return this.error != null;
11211     },
11212
11213     // private
11214     clearError : function(){
11215         this.error = null;
11216     },
11217
11218     /**
11219      * Creates a copy of this record.
11220      * @param {String} id (optional) A new record id if you don't want to use this record's id
11221      * @return {Record}
11222      */
11223     copy : function(newId) {
11224         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11225     }
11226 };/*
11227  * Based on:
11228  * Ext JS Library 1.1.1
11229  * Copyright(c) 2006-2007, Ext JS, LLC.
11230  *
11231  * Originally Released Under LGPL - original licence link has changed is not relivant.
11232  *
11233  * Fork - LGPL
11234  * <script type="text/javascript">
11235  */
11236
11237
11238
11239 /**
11240  * @class Roo.data.Store
11241  * @extends Roo.util.Observable
11242  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11243  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11244  * <p>
11245  * 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
11246  * has no knowledge of the format of the data returned by the Proxy.<br>
11247  * <p>
11248  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11249  * instances from the data object. These records are cached and made available through accessor functions.
11250  * @constructor
11251  * Creates a new Store.
11252  * @param {Object} config A config object containing the objects needed for the Store to access data,
11253  * and read the data into Records.
11254  */
11255 Roo.data.Store = function(config){
11256     this.data = new Roo.util.MixedCollection(false);
11257     this.data.getKey = function(o){
11258         return o.id;
11259     };
11260     this.baseParams = {};
11261     // private
11262     this.paramNames = {
11263         "start" : "start",
11264         "limit" : "limit",
11265         "sort" : "sort",
11266         "dir" : "dir",
11267         "multisort" : "_multisort"
11268     };
11269
11270     if(config && config.data){
11271         this.inlineData = config.data;
11272         delete config.data;
11273     }
11274
11275     Roo.apply(this, config);
11276     
11277     if(this.reader){ // reader passed
11278         this.reader = Roo.factory(this.reader, Roo.data);
11279         this.reader.xmodule = this.xmodule || false;
11280         if(!this.recordType){
11281             this.recordType = this.reader.recordType;
11282         }
11283         if(this.reader.onMetaChange){
11284             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11285         }
11286     }
11287
11288     if(this.recordType){
11289         this.fields = this.recordType.prototype.fields;
11290     }
11291     this.modified = [];
11292
11293     this.addEvents({
11294         /**
11295          * @event datachanged
11296          * Fires when the data cache has changed, and a widget which is using this Store
11297          * as a Record cache should refresh its view.
11298          * @param {Store} this
11299          */
11300         datachanged : true,
11301         /**
11302          * @event metachange
11303          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11304          * @param {Store} this
11305          * @param {Object} meta The JSON metadata
11306          */
11307         metachange : true,
11308         /**
11309          * @event add
11310          * Fires when Records have been added to the Store
11311          * @param {Store} this
11312          * @param {Roo.data.Record[]} records The array of Records added
11313          * @param {Number} index The index at which the record(s) were added
11314          */
11315         add : true,
11316         /**
11317          * @event remove
11318          * Fires when a Record has been removed from the Store
11319          * @param {Store} this
11320          * @param {Roo.data.Record} record The Record that was removed
11321          * @param {Number} index The index at which the record was removed
11322          */
11323         remove : true,
11324         /**
11325          * @event update
11326          * Fires when a Record has been updated
11327          * @param {Store} this
11328          * @param {Roo.data.Record} record The Record that was updated
11329          * @param {String} operation The update operation being performed.  Value may be one of:
11330          * <pre><code>
11331  Roo.data.Record.EDIT
11332  Roo.data.Record.REJECT
11333  Roo.data.Record.COMMIT
11334          * </code></pre>
11335          */
11336         update : true,
11337         /**
11338          * @event clear
11339          * Fires when the data cache has been cleared.
11340          * @param {Store} this
11341          */
11342         clear : true,
11343         /**
11344          * @event beforeload
11345          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11346          * the load action will be canceled.
11347          * @param {Store} this
11348          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11349          */
11350         beforeload : true,
11351         /**
11352          * @event beforeloadadd
11353          * Fires after a new set of Records has been loaded.
11354          * @param {Store} this
11355          * @param {Roo.data.Record[]} records The Records that were loaded
11356          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11357          */
11358         beforeloadadd : true,
11359         /**
11360          * @event load
11361          * Fires after a new set of Records has been loaded, before they are added to the store.
11362          * @param {Store} this
11363          * @param {Roo.data.Record[]} records The Records that were loaded
11364          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11365          * @params {Object} return from reader
11366          */
11367         load : true,
11368         /**
11369          * @event loadexception
11370          * Fires if an exception occurs in the Proxy during loading.
11371          * Called with the signature of the Proxy's "loadexception" event.
11372          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11373          * 
11374          * @param {Proxy} 
11375          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11376          * @param {Object} load options 
11377          * @param {Object} jsonData from your request (normally this contains the Exception)
11378          */
11379         loadexception : true
11380     });
11381     
11382     if(this.proxy){
11383         this.proxy = Roo.factory(this.proxy, Roo.data);
11384         this.proxy.xmodule = this.xmodule || false;
11385         this.relayEvents(this.proxy,  ["loadexception"]);
11386     }
11387     this.sortToggle = {};
11388     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11389
11390     Roo.data.Store.superclass.constructor.call(this);
11391
11392     if(this.inlineData){
11393         this.loadData(this.inlineData);
11394         delete this.inlineData;
11395     }
11396 };
11397
11398 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11399      /**
11400     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11401     * without a remote query - used by combo/forms at present.
11402     */
11403     
11404     /**
11405     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11406     */
11407     /**
11408     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11409     */
11410     /**
11411     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11412     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11413     */
11414     /**
11415     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11416     * on any HTTP request
11417     */
11418     /**
11419     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11420     */
11421     /**
11422     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11423     */
11424     multiSort: false,
11425     /**
11426     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11427     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11428     */
11429     remoteSort : false,
11430
11431     /**
11432     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11433      * loaded or when a record is removed. (defaults to false).
11434     */
11435     pruneModifiedRecords : false,
11436
11437     // private
11438     lastOptions : null,
11439
11440     /**
11441      * Add Records to the Store and fires the add event.
11442      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11443      */
11444     add : function(records){
11445         records = [].concat(records);
11446         for(var i = 0, len = records.length; i < len; i++){
11447             records[i].join(this);
11448         }
11449         var index = this.data.length;
11450         this.data.addAll(records);
11451         this.fireEvent("add", this, records, index);
11452     },
11453
11454     /**
11455      * Remove a Record from the Store and fires the remove event.
11456      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11457      */
11458     remove : function(record){
11459         var index = this.data.indexOf(record);
11460         this.data.removeAt(index);
11461  
11462         if(this.pruneModifiedRecords){
11463             this.modified.remove(record);
11464         }
11465         this.fireEvent("remove", this, record, index);
11466     },
11467
11468     /**
11469      * Remove all Records from the Store and fires the clear event.
11470      */
11471     removeAll : function(){
11472         this.data.clear();
11473         if(this.pruneModifiedRecords){
11474             this.modified = [];
11475         }
11476         this.fireEvent("clear", this);
11477     },
11478
11479     /**
11480      * Inserts Records to the Store at the given index and fires the add event.
11481      * @param {Number} index The start index at which to insert the passed Records.
11482      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11483      */
11484     insert : function(index, records){
11485         records = [].concat(records);
11486         for(var i = 0, len = records.length; i < len; i++){
11487             this.data.insert(index, records[i]);
11488             records[i].join(this);
11489         }
11490         this.fireEvent("add", this, records, index);
11491     },
11492
11493     /**
11494      * Get the index within the cache of the passed Record.
11495      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11496      * @return {Number} The index of the passed Record. Returns -1 if not found.
11497      */
11498     indexOf : function(record){
11499         return this.data.indexOf(record);
11500     },
11501
11502     /**
11503      * Get the index within the cache of the Record with the passed id.
11504      * @param {String} id The id of the Record to find.
11505      * @return {Number} The index of the Record. Returns -1 if not found.
11506      */
11507     indexOfId : function(id){
11508         return this.data.indexOfKey(id);
11509     },
11510
11511     /**
11512      * Get the Record with the specified id.
11513      * @param {String} id The id of the Record to find.
11514      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11515      */
11516     getById : function(id){
11517         return this.data.key(id);
11518     },
11519
11520     /**
11521      * Get the Record at the specified index.
11522      * @param {Number} index The index of the Record to find.
11523      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11524      */
11525     getAt : function(index){
11526         return this.data.itemAt(index);
11527     },
11528
11529     /**
11530      * Returns a range of Records between specified indices.
11531      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11532      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11533      * @return {Roo.data.Record[]} An array of Records
11534      */
11535     getRange : function(start, end){
11536         return this.data.getRange(start, end);
11537     },
11538
11539     // private
11540     storeOptions : function(o){
11541         o = Roo.apply({}, o);
11542         delete o.callback;
11543         delete o.scope;
11544         this.lastOptions = o;
11545     },
11546
11547     /**
11548      * Loads the Record cache from the configured Proxy using the configured Reader.
11549      * <p>
11550      * If using remote paging, then the first load call must specify the <em>start</em>
11551      * and <em>limit</em> properties in the options.params property to establish the initial
11552      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11553      * <p>
11554      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11555      * and this call will return before the new data has been loaded. Perform any post-processing
11556      * in a callback function, or in a "load" event handler.</strong>
11557      * <p>
11558      * @param {Object} options An object containing properties which control loading options:<ul>
11559      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11560      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11561      * passed the following arguments:<ul>
11562      * <li>r : Roo.data.Record[]</li>
11563      * <li>options: Options object from the load call</li>
11564      * <li>success: Boolean success indicator</li></ul></li>
11565      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11566      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11567      * </ul>
11568      */
11569     load : function(options){
11570         options = options || {};
11571         if(this.fireEvent("beforeload", this, options) !== false){
11572             this.storeOptions(options);
11573             var p = Roo.apply(options.params || {}, this.baseParams);
11574             // if meta was not loaded from remote source.. try requesting it.
11575             if (!this.reader.metaFromRemote) {
11576                 p._requestMeta = 1;
11577             }
11578             if(this.sortInfo && this.remoteSort){
11579                 var pn = this.paramNames;
11580                 p[pn["sort"]] = this.sortInfo.field;
11581                 p[pn["dir"]] = this.sortInfo.direction;
11582             }
11583             if (this.multiSort) {
11584                 var pn = this.paramNames;
11585                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11586             }
11587             
11588             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11589         }
11590     },
11591
11592     /**
11593      * Reloads the Record cache from the configured Proxy using the configured Reader and
11594      * the options from the last load operation performed.
11595      * @param {Object} options (optional) An object containing properties which may override the options
11596      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11597      * the most recently used options are reused).
11598      */
11599     reload : function(options){
11600         this.load(Roo.applyIf(options||{}, this.lastOptions));
11601     },
11602
11603     // private
11604     // Called as a callback by the Reader during a load operation.
11605     loadRecords : function(o, options, success){
11606         if(!o || success === false){
11607             if(success !== false){
11608                 this.fireEvent("load", this, [], options, o);
11609             }
11610             if(options.callback){
11611                 options.callback.call(options.scope || this, [], options, false);
11612             }
11613             return;
11614         }
11615         // if data returned failure - throw an exception.
11616         if (o.success === false) {
11617             // show a message if no listener is registered.
11618             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11619                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11620             }
11621             // loadmask wil be hooked into this..
11622             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11623             return;
11624         }
11625         var r = o.records, t = o.totalRecords || r.length;
11626         
11627         this.fireEvent("beforeloadadd", this, r, options, o);
11628         
11629         if(!options || options.add !== true){
11630             if(this.pruneModifiedRecords){
11631                 this.modified = [];
11632             }
11633             for(var i = 0, len = r.length; i < len; i++){
11634                 r[i].join(this);
11635             }
11636             if(this.snapshot){
11637                 this.data = this.snapshot;
11638                 delete this.snapshot;
11639             }
11640             this.data.clear();
11641             this.data.addAll(r);
11642             this.totalLength = t;
11643             this.applySort();
11644             this.fireEvent("datachanged", this);
11645         }else{
11646             this.totalLength = Math.max(t, this.data.length+r.length);
11647             this.add(r);
11648         }
11649         
11650         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11651                 
11652             var e = new Roo.data.Record({});
11653
11654             e.set(this.parent.displayField, this.parent.emptyTitle);
11655             e.set(this.parent.valueField, '');
11656
11657             this.insert(0, e);
11658         }
11659             
11660         this.fireEvent("load", this, r, options, o);
11661         if(options.callback){
11662             options.callback.call(options.scope || this, r, options, true);
11663         }
11664     },
11665
11666
11667     /**
11668      * Loads data from a passed data block. A Reader which understands the format of the data
11669      * must have been configured in the constructor.
11670      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11671      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11672      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11673      */
11674     loadData : function(o, append){
11675         var r = this.reader.readRecords(o);
11676         this.loadRecords(r, {add: append}, true);
11677     },
11678
11679     /**
11680      * Gets the number of cached records.
11681      * <p>
11682      * <em>If using paging, this may not be the total size of the dataset. If the data object
11683      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11684      * the data set size</em>
11685      */
11686     getCount : function(){
11687         return this.data.length || 0;
11688     },
11689
11690     /**
11691      * Gets the total number of records in the dataset as returned by the server.
11692      * <p>
11693      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11694      * the dataset size</em>
11695      */
11696     getTotalCount : function(){
11697         return this.totalLength || 0;
11698     },
11699
11700     /**
11701      * Returns the sort state of the Store as an object with two properties:
11702      * <pre><code>
11703  field {String} The name of the field by which the Records are sorted
11704  direction {String} The sort order, "ASC" or "DESC"
11705      * </code></pre>
11706      */
11707     getSortState : function(){
11708         return this.sortInfo;
11709     },
11710
11711     // private
11712     applySort : function(){
11713         if(this.sortInfo && !this.remoteSort){
11714             var s = this.sortInfo, f = s.field;
11715             var st = this.fields.get(f).sortType;
11716             var fn = function(r1, r2){
11717                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11718                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11719             };
11720             this.data.sort(s.direction, fn);
11721             if(this.snapshot && this.snapshot != this.data){
11722                 this.snapshot.sort(s.direction, fn);
11723             }
11724         }
11725     },
11726
11727     /**
11728      * Sets the default sort column and order to be used by the next load operation.
11729      * @param {String} fieldName The name of the field to sort by.
11730      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11731      */
11732     setDefaultSort : function(field, dir){
11733         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11734     },
11735
11736     /**
11737      * Sort the Records.
11738      * If remote sorting is used, the sort is performed on the server, and the cache is
11739      * reloaded. If local sorting is used, the cache is sorted internally.
11740      * @param {String} fieldName The name of the field to sort by.
11741      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11742      */
11743     sort : function(fieldName, dir){
11744         var f = this.fields.get(fieldName);
11745         if(!dir){
11746             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11747             
11748             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11749                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11750             }else{
11751                 dir = f.sortDir;
11752             }
11753         }
11754         this.sortToggle[f.name] = dir;
11755         this.sortInfo = {field: f.name, direction: dir};
11756         if(!this.remoteSort){
11757             this.applySort();
11758             this.fireEvent("datachanged", this);
11759         }else{
11760             this.load(this.lastOptions);
11761         }
11762     },
11763
11764     /**
11765      * Calls the specified function for each of the Records in the cache.
11766      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11767      * Returning <em>false</em> aborts and exits the iteration.
11768      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11769      */
11770     each : function(fn, scope){
11771         this.data.each(fn, scope);
11772     },
11773
11774     /**
11775      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11776      * (e.g., during paging).
11777      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11778      */
11779     getModifiedRecords : function(){
11780         return this.modified;
11781     },
11782
11783     // private
11784     createFilterFn : function(property, value, anyMatch){
11785         if(!value.exec){ // not a regex
11786             value = String(value);
11787             if(value.length == 0){
11788                 return false;
11789             }
11790             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11791         }
11792         return function(r){
11793             return value.test(r.data[property]);
11794         };
11795     },
11796
11797     /**
11798      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11799      * @param {String} property A field on your records
11800      * @param {Number} start The record index to start at (defaults to 0)
11801      * @param {Number} end The last record index to include (defaults to length - 1)
11802      * @return {Number} The sum
11803      */
11804     sum : function(property, start, end){
11805         var rs = this.data.items, v = 0;
11806         start = start || 0;
11807         end = (end || end === 0) ? end : rs.length-1;
11808
11809         for(var i = start; i <= end; i++){
11810             v += (rs[i].data[property] || 0);
11811         }
11812         return v;
11813     },
11814
11815     /**
11816      * Filter the records by a specified property.
11817      * @param {String} field A field on your records
11818      * @param {String/RegExp} value Either a string that the field
11819      * should start with or a RegExp to test against the field
11820      * @param {Boolean} anyMatch True to match any part not just the beginning
11821      */
11822     filter : function(property, value, anyMatch){
11823         var fn = this.createFilterFn(property, value, anyMatch);
11824         return fn ? this.filterBy(fn) : this.clearFilter();
11825     },
11826
11827     /**
11828      * Filter by a function. The specified function will be called with each
11829      * record in this data source. If the function returns true the record is included,
11830      * otherwise it is filtered.
11831      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11832      * @param {Object} scope (optional) The scope of the function (defaults to this)
11833      */
11834     filterBy : function(fn, scope){
11835         this.snapshot = this.snapshot || this.data;
11836         this.data = this.queryBy(fn, scope||this);
11837         this.fireEvent("datachanged", this);
11838     },
11839
11840     /**
11841      * Query the records by a specified property.
11842      * @param {String} field A field on your records
11843      * @param {String/RegExp} value Either a string that the field
11844      * should start with or a RegExp to test against the field
11845      * @param {Boolean} anyMatch True to match any part not just the beginning
11846      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11847      */
11848     query : function(property, value, anyMatch){
11849         var fn = this.createFilterFn(property, value, anyMatch);
11850         return fn ? this.queryBy(fn) : this.data.clone();
11851     },
11852
11853     /**
11854      * Query by a function. The specified function will be called with each
11855      * record in this data source. If the function returns true the record is included
11856      * in the results.
11857      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11858      * @param {Object} scope (optional) The scope of the function (defaults to this)
11859       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11860      **/
11861     queryBy : function(fn, scope){
11862         var data = this.snapshot || this.data;
11863         return data.filterBy(fn, scope||this);
11864     },
11865
11866     /**
11867      * Collects unique values for a particular dataIndex from this store.
11868      * @param {String} dataIndex The property to collect
11869      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11870      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11871      * @return {Array} An array of the unique values
11872      **/
11873     collect : function(dataIndex, allowNull, bypassFilter){
11874         var d = (bypassFilter === true && this.snapshot) ?
11875                 this.snapshot.items : this.data.items;
11876         var v, sv, r = [], l = {};
11877         for(var i = 0, len = d.length; i < len; i++){
11878             v = d[i].data[dataIndex];
11879             sv = String(v);
11880             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11881                 l[sv] = true;
11882                 r[r.length] = v;
11883             }
11884         }
11885         return r;
11886     },
11887
11888     /**
11889      * Revert to a view of the Record cache with no filtering applied.
11890      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11891      */
11892     clearFilter : function(suppressEvent){
11893         if(this.snapshot && this.snapshot != this.data){
11894             this.data = this.snapshot;
11895             delete this.snapshot;
11896             if(suppressEvent !== true){
11897                 this.fireEvent("datachanged", this);
11898             }
11899         }
11900     },
11901
11902     // private
11903     afterEdit : function(record){
11904         if(this.modified.indexOf(record) == -1){
11905             this.modified.push(record);
11906         }
11907         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11908     },
11909     
11910     // private
11911     afterReject : function(record){
11912         this.modified.remove(record);
11913         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11914     },
11915
11916     // private
11917     afterCommit : function(record){
11918         this.modified.remove(record);
11919         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11920     },
11921
11922     /**
11923      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11924      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11925      */
11926     commitChanges : function(){
11927         var m = this.modified.slice(0);
11928         this.modified = [];
11929         for(var i = 0, len = m.length; i < len; i++){
11930             m[i].commit();
11931         }
11932     },
11933
11934     /**
11935      * Cancel outstanding changes on all changed records.
11936      */
11937     rejectChanges : function(){
11938         var m = this.modified.slice(0);
11939         this.modified = [];
11940         for(var i = 0, len = m.length; i < len; i++){
11941             m[i].reject();
11942         }
11943     },
11944
11945     onMetaChange : function(meta, rtype, o){
11946         this.recordType = rtype;
11947         this.fields = rtype.prototype.fields;
11948         delete this.snapshot;
11949         this.sortInfo = meta.sortInfo || this.sortInfo;
11950         this.modified = [];
11951         this.fireEvent('metachange', this, this.reader.meta);
11952     },
11953     
11954     moveIndex : function(data, type)
11955     {
11956         var index = this.indexOf(data);
11957         
11958         var newIndex = index + type;
11959         
11960         this.remove(data);
11961         
11962         this.insert(newIndex, data);
11963         
11964     }
11965 });/*
11966  * Based on:
11967  * Ext JS Library 1.1.1
11968  * Copyright(c) 2006-2007, Ext JS, LLC.
11969  *
11970  * Originally Released Under LGPL - original licence link has changed is not relivant.
11971  *
11972  * Fork - LGPL
11973  * <script type="text/javascript">
11974  */
11975
11976 /**
11977  * @class Roo.data.SimpleStore
11978  * @extends Roo.data.Store
11979  * Small helper class to make creating Stores from Array data easier.
11980  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11981  * @cfg {Array} fields An array of field definition objects, or field name strings.
11982  * @cfg {Array} data The multi-dimensional array of data
11983  * @constructor
11984  * @param {Object} config
11985  */
11986 Roo.data.SimpleStore = function(config){
11987     Roo.data.SimpleStore.superclass.constructor.call(this, {
11988         isLocal : true,
11989         reader: new Roo.data.ArrayReader({
11990                 id: config.id
11991             },
11992             Roo.data.Record.create(config.fields)
11993         ),
11994         proxy : new Roo.data.MemoryProxy(config.data)
11995     });
11996     this.load();
11997 };
11998 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11999  * Based on:
12000  * Ext JS Library 1.1.1
12001  * Copyright(c) 2006-2007, Ext JS, LLC.
12002  *
12003  * Originally Released Under LGPL - original licence link has changed is not relivant.
12004  *
12005  * Fork - LGPL
12006  * <script type="text/javascript">
12007  */
12008
12009 /**
12010 /**
12011  * @extends Roo.data.Store
12012  * @class Roo.data.JsonStore
12013  * Small helper class to make creating Stores for JSON data easier. <br/>
12014 <pre><code>
12015 var store = new Roo.data.JsonStore({
12016     url: 'get-images.php',
12017     root: 'images',
12018     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12019 });
12020 </code></pre>
12021  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12022  * JsonReader and HttpProxy (unless inline data is provided).</b>
12023  * @cfg {Array} fields An array of field definition objects, or field name strings.
12024  * @constructor
12025  * @param {Object} config
12026  */
12027 Roo.data.JsonStore = function(c){
12028     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12029         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12030         reader: new Roo.data.JsonReader(c, c.fields)
12031     }));
12032 };
12033 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12034  * Based on:
12035  * Ext JS Library 1.1.1
12036  * Copyright(c) 2006-2007, Ext JS, LLC.
12037  *
12038  * Originally Released Under LGPL - original licence link has changed is not relivant.
12039  *
12040  * Fork - LGPL
12041  * <script type="text/javascript">
12042  */
12043
12044  
12045 Roo.data.Field = function(config){
12046     if(typeof config == "string"){
12047         config = {name: config};
12048     }
12049     Roo.apply(this, config);
12050     
12051     if(!this.type){
12052         this.type = "auto";
12053     }
12054     
12055     var st = Roo.data.SortTypes;
12056     // named sortTypes are supported, here we look them up
12057     if(typeof this.sortType == "string"){
12058         this.sortType = st[this.sortType];
12059     }
12060     
12061     // set default sortType for strings and dates
12062     if(!this.sortType){
12063         switch(this.type){
12064             case "string":
12065                 this.sortType = st.asUCString;
12066                 break;
12067             case "date":
12068                 this.sortType = st.asDate;
12069                 break;
12070             default:
12071                 this.sortType = st.none;
12072         }
12073     }
12074
12075     // define once
12076     var stripRe = /[\$,%]/g;
12077
12078     // prebuilt conversion function for this field, instead of
12079     // switching every time we're reading a value
12080     if(!this.convert){
12081         var cv, dateFormat = this.dateFormat;
12082         switch(this.type){
12083             case "":
12084             case "auto":
12085             case undefined:
12086                 cv = function(v){ return v; };
12087                 break;
12088             case "string":
12089                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12090                 break;
12091             case "int":
12092                 cv = function(v){
12093                     return v !== undefined && v !== null && v !== '' ?
12094                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12095                     };
12096                 break;
12097             case "float":
12098                 cv = function(v){
12099                     return v !== undefined && v !== null && v !== '' ?
12100                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12101                     };
12102                 break;
12103             case "bool":
12104             case "boolean":
12105                 cv = function(v){ return v === true || v === "true" || v == 1; };
12106                 break;
12107             case "date":
12108                 cv = function(v){
12109                     if(!v){
12110                         return '';
12111                     }
12112                     if(v instanceof Date){
12113                         return v;
12114                     }
12115                     if(dateFormat){
12116                         if(dateFormat == "timestamp"){
12117                             return new Date(v*1000);
12118                         }
12119                         return Date.parseDate(v, dateFormat);
12120                     }
12121                     var parsed = Date.parse(v);
12122                     return parsed ? new Date(parsed) : null;
12123                 };
12124              break;
12125             
12126         }
12127         this.convert = cv;
12128     }
12129 };
12130
12131 Roo.data.Field.prototype = {
12132     dateFormat: null,
12133     defaultValue: "",
12134     mapping: null,
12135     sortType : null,
12136     sortDir : "ASC"
12137 };/*
12138  * Based on:
12139  * Ext JS Library 1.1.1
12140  * Copyright(c) 2006-2007, Ext JS, LLC.
12141  *
12142  * Originally Released Under LGPL - original licence link has changed is not relivant.
12143  *
12144  * Fork - LGPL
12145  * <script type="text/javascript">
12146  */
12147  
12148 // Base class for reading structured data from a data source.  This class is intended to be
12149 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12150
12151 /**
12152  * @class Roo.data.DataReader
12153  * Base class for reading structured data from a data source.  This class is intended to be
12154  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12155  */
12156
12157 Roo.data.DataReader = function(meta, recordType){
12158     
12159     this.meta = meta;
12160     
12161     this.recordType = recordType instanceof Array ? 
12162         Roo.data.Record.create(recordType) : recordType;
12163 };
12164
12165 Roo.data.DataReader.prototype = {
12166      /**
12167      * Create an empty record
12168      * @param {Object} data (optional) - overlay some values
12169      * @return {Roo.data.Record} record created.
12170      */
12171     newRow :  function(d) {
12172         var da =  {};
12173         this.recordType.prototype.fields.each(function(c) {
12174             switch( c.type) {
12175                 case 'int' : da[c.name] = 0; break;
12176                 case 'date' : da[c.name] = new Date(); break;
12177                 case 'float' : da[c.name] = 0.0; break;
12178                 case 'boolean' : da[c.name] = false; break;
12179                 default : da[c.name] = ""; break;
12180             }
12181             
12182         });
12183         return new this.recordType(Roo.apply(da, d));
12184     }
12185     
12186 };/*
12187  * Based on:
12188  * Ext JS Library 1.1.1
12189  * Copyright(c) 2006-2007, Ext JS, LLC.
12190  *
12191  * Originally Released Under LGPL - original licence link has changed is not relivant.
12192  *
12193  * Fork - LGPL
12194  * <script type="text/javascript">
12195  */
12196
12197 /**
12198  * @class Roo.data.DataProxy
12199  * @extends Roo.data.Observable
12200  * This class is an abstract base class for implementations which provide retrieval of
12201  * unformatted data objects.<br>
12202  * <p>
12203  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12204  * (of the appropriate type which knows how to parse the data object) to provide a block of
12205  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12206  * <p>
12207  * Custom implementations must implement the load method as described in
12208  * {@link Roo.data.HttpProxy#load}.
12209  */
12210 Roo.data.DataProxy = function(){
12211     this.addEvents({
12212         /**
12213          * @event beforeload
12214          * Fires before a network request is made to retrieve a data object.
12215          * @param {Object} This DataProxy object.
12216          * @param {Object} params The params parameter to the load function.
12217          */
12218         beforeload : true,
12219         /**
12220          * @event load
12221          * Fires before the load method's callback is called.
12222          * @param {Object} This DataProxy object.
12223          * @param {Object} o The data object.
12224          * @param {Object} arg The callback argument object passed to the load function.
12225          */
12226         load : true,
12227         /**
12228          * @event loadexception
12229          * Fires if an Exception occurs during data retrieval.
12230          * @param {Object} This DataProxy object.
12231          * @param {Object} o The data object.
12232          * @param {Object} arg The callback argument object passed to the load function.
12233          * @param {Object} e The Exception.
12234          */
12235         loadexception : true
12236     });
12237     Roo.data.DataProxy.superclass.constructor.call(this);
12238 };
12239
12240 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12241
12242     /**
12243      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12244      */
12245 /*
12246  * Based on:
12247  * Ext JS Library 1.1.1
12248  * Copyright(c) 2006-2007, Ext JS, LLC.
12249  *
12250  * Originally Released Under LGPL - original licence link has changed is not relivant.
12251  *
12252  * Fork - LGPL
12253  * <script type="text/javascript">
12254  */
12255 /**
12256  * @class Roo.data.MemoryProxy
12257  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12258  * to the Reader when its load method is called.
12259  * @constructor
12260  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12261  */
12262 Roo.data.MemoryProxy = function(data){
12263     if (data.data) {
12264         data = data.data;
12265     }
12266     Roo.data.MemoryProxy.superclass.constructor.call(this);
12267     this.data = data;
12268 };
12269
12270 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12271     
12272     /**
12273      * Load data from the requested source (in this case an in-memory
12274      * data object passed to the constructor), read the data object into
12275      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12276      * process that block using the passed callback.
12277      * @param {Object} params This parameter is not used by the MemoryProxy class.
12278      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12279      * object into a block of Roo.data.Records.
12280      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12281      * The function must be passed <ul>
12282      * <li>The Record block object</li>
12283      * <li>The "arg" argument from the load function</li>
12284      * <li>A boolean success indicator</li>
12285      * </ul>
12286      * @param {Object} scope The scope in which to call the callback
12287      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12288      */
12289     load : function(params, reader, callback, scope, arg){
12290         params = params || {};
12291         var result;
12292         try {
12293             result = reader.readRecords(this.data);
12294         }catch(e){
12295             this.fireEvent("loadexception", this, arg, null, e);
12296             callback.call(scope, null, arg, false);
12297             return;
12298         }
12299         callback.call(scope, result, arg, true);
12300     },
12301     
12302     // private
12303     update : function(params, records){
12304         
12305     }
12306 });/*
12307  * Based on:
12308  * Ext JS Library 1.1.1
12309  * Copyright(c) 2006-2007, Ext JS, LLC.
12310  *
12311  * Originally Released Under LGPL - original licence link has changed is not relivant.
12312  *
12313  * Fork - LGPL
12314  * <script type="text/javascript">
12315  */
12316 /**
12317  * @class Roo.data.HttpProxy
12318  * @extends Roo.data.DataProxy
12319  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12320  * configured to reference a certain URL.<br><br>
12321  * <p>
12322  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12323  * from which the running page was served.<br><br>
12324  * <p>
12325  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12326  * <p>
12327  * Be aware that to enable the browser to parse an XML document, the server must set
12328  * the Content-Type header in the HTTP response to "text/xml".
12329  * @constructor
12330  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12331  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12332  * will be used to make the request.
12333  */
12334 Roo.data.HttpProxy = function(conn){
12335     Roo.data.HttpProxy.superclass.constructor.call(this);
12336     // is conn a conn config or a real conn?
12337     this.conn = conn;
12338     this.useAjax = !conn || !conn.events;
12339   
12340 };
12341
12342 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12343     // thse are take from connection...
12344     
12345     /**
12346      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12347      */
12348     /**
12349      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12350      * extra parameters to each request made by this object. (defaults to undefined)
12351      */
12352     /**
12353      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12354      *  to each request made by this object. (defaults to undefined)
12355      */
12356     /**
12357      * @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)
12358      */
12359     /**
12360      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12361      */
12362      /**
12363      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12364      * @type Boolean
12365      */
12366   
12367
12368     /**
12369      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12370      * @type Boolean
12371      */
12372     /**
12373      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12374      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12375      * a finer-grained basis than the DataProxy events.
12376      */
12377     getConnection : function(){
12378         return this.useAjax ? Roo.Ajax : this.conn;
12379     },
12380
12381     /**
12382      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12383      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12384      * process that block using the passed callback.
12385      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12386      * for the request to the remote server.
12387      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12388      * object into a block of Roo.data.Records.
12389      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12390      * The function must be passed <ul>
12391      * <li>The Record block object</li>
12392      * <li>The "arg" argument from the load function</li>
12393      * <li>A boolean success indicator</li>
12394      * </ul>
12395      * @param {Object} scope The scope in which to call the callback
12396      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12397      */
12398     load : function(params, reader, callback, scope, arg){
12399         if(this.fireEvent("beforeload", this, params) !== false){
12400             var  o = {
12401                 params : params || {},
12402                 request: {
12403                     callback : callback,
12404                     scope : scope,
12405                     arg : arg
12406                 },
12407                 reader: reader,
12408                 callback : this.loadResponse,
12409                 scope: this
12410             };
12411             if(this.useAjax){
12412                 Roo.applyIf(o, this.conn);
12413                 if(this.activeRequest){
12414                     Roo.Ajax.abort(this.activeRequest);
12415                 }
12416                 this.activeRequest = Roo.Ajax.request(o);
12417             }else{
12418                 this.conn.request(o);
12419             }
12420         }else{
12421             callback.call(scope||this, null, arg, false);
12422         }
12423     },
12424
12425     // private
12426     loadResponse : function(o, success, response){
12427         delete this.activeRequest;
12428         if(!success){
12429             this.fireEvent("loadexception", this, o, response);
12430             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12431             return;
12432         }
12433         var result;
12434         try {
12435             result = o.reader.read(response);
12436         }catch(e){
12437             this.fireEvent("loadexception", this, o, response, e);
12438             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12439             return;
12440         }
12441         
12442         this.fireEvent("load", this, o, o.request.arg);
12443         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12444     },
12445
12446     // private
12447     update : function(dataSet){
12448
12449     },
12450
12451     // private
12452     updateResponse : function(dataSet){
12453
12454     }
12455 });/*
12456  * Based on:
12457  * Ext JS Library 1.1.1
12458  * Copyright(c) 2006-2007, Ext JS, LLC.
12459  *
12460  * Originally Released Under LGPL - original licence link has changed is not relivant.
12461  *
12462  * Fork - LGPL
12463  * <script type="text/javascript">
12464  */
12465
12466 /**
12467  * @class Roo.data.ScriptTagProxy
12468  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12469  * other than the originating domain of the running page.<br><br>
12470  * <p>
12471  * <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
12472  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12473  * <p>
12474  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12475  * source code that is used as the source inside a &lt;script> tag.<br><br>
12476  * <p>
12477  * In order for the browser to process the returned data, the server must wrap the data object
12478  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12479  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12480  * depending on whether the callback name was passed:
12481  * <p>
12482  * <pre><code>
12483 boolean scriptTag = false;
12484 String cb = request.getParameter("callback");
12485 if (cb != null) {
12486     scriptTag = true;
12487     response.setContentType("text/javascript");
12488 } else {
12489     response.setContentType("application/x-json");
12490 }
12491 Writer out = response.getWriter();
12492 if (scriptTag) {
12493     out.write(cb + "(");
12494 }
12495 out.print(dataBlock.toJsonString());
12496 if (scriptTag) {
12497     out.write(");");
12498 }
12499 </pre></code>
12500  *
12501  * @constructor
12502  * @param {Object} config A configuration object.
12503  */
12504 Roo.data.ScriptTagProxy = function(config){
12505     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12506     Roo.apply(this, config);
12507     this.head = document.getElementsByTagName("head")[0];
12508 };
12509
12510 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12511
12512 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12513     /**
12514      * @cfg {String} url The URL from which to request the data object.
12515      */
12516     /**
12517      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12518      */
12519     timeout : 30000,
12520     /**
12521      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12522      * the server the name of the callback function set up by the load call to process the returned data object.
12523      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12524      * javascript output which calls this named function passing the data object as its only parameter.
12525      */
12526     callbackParam : "callback",
12527     /**
12528      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12529      * name to the request.
12530      */
12531     nocache : true,
12532
12533     /**
12534      * Load data from the configured URL, read the data object into
12535      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12536      * process that block using the passed callback.
12537      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12538      * for the request to the remote server.
12539      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12540      * object into a block of Roo.data.Records.
12541      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12542      * The function must be passed <ul>
12543      * <li>The Record block object</li>
12544      * <li>The "arg" argument from the load function</li>
12545      * <li>A boolean success indicator</li>
12546      * </ul>
12547      * @param {Object} scope The scope in which to call the callback
12548      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12549      */
12550     load : function(params, reader, callback, scope, arg){
12551         if(this.fireEvent("beforeload", this, params) !== false){
12552
12553             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12554
12555             var url = this.url;
12556             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12557             if(this.nocache){
12558                 url += "&_dc=" + (new Date().getTime());
12559             }
12560             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12561             var trans = {
12562                 id : transId,
12563                 cb : "stcCallback"+transId,
12564                 scriptId : "stcScript"+transId,
12565                 params : params,
12566                 arg : arg,
12567                 url : url,
12568                 callback : callback,
12569                 scope : scope,
12570                 reader : reader
12571             };
12572             var conn = this;
12573
12574             window[trans.cb] = function(o){
12575                 conn.handleResponse(o, trans);
12576             };
12577
12578             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12579
12580             if(this.autoAbort !== false){
12581                 this.abort();
12582             }
12583
12584             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12585
12586             var script = document.createElement("script");
12587             script.setAttribute("src", url);
12588             script.setAttribute("type", "text/javascript");
12589             script.setAttribute("id", trans.scriptId);
12590             this.head.appendChild(script);
12591
12592             this.trans = trans;
12593         }else{
12594             callback.call(scope||this, null, arg, false);
12595         }
12596     },
12597
12598     // private
12599     isLoading : function(){
12600         return this.trans ? true : false;
12601     },
12602
12603     /**
12604      * Abort the current server request.
12605      */
12606     abort : function(){
12607         if(this.isLoading()){
12608             this.destroyTrans(this.trans);
12609         }
12610     },
12611
12612     // private
12613     destroyTrans : function(trans, isLoaded){
12614         this.head.removeChild(document.getElementById(trans.scriptId));
12615         clearTimeout(trans.timeoutId);
12616         if(isLoaded){
12617             window[trans.cb] = undefined;
12618             try{
12619                 delete window[trans.cb];
12620             }catch(e){}
12621         }else{
12622             // if hasn't been loaded, wait for load to remove it to prevent script error
12623             window[trans.cb] = function(){
12624                 window[trans.cb] = undefined;
12625                 try{
12626                     delete window[trans.cb];
12627                 }catch(e){}
12628             };
12629         }
12630     },
12631
12632     // private
12633     handleResponse : function(o, trans){
12634         this.trans = false;
12635         this.destroyTrans(trans, true);
12636         var result;
12637         try {
12638             result = trans.reader.readRecords(o);
12639         }catch(e){
12640             this.fireEvent("loadexception", this, o, trans.arg, e);
12641             trans.callback.call(trans.scope||window, null, trans.arg, false);
12642             return;
12643         }
12644         this.fireEvent("load", this, o, trans.arg);
12645         trans.callback.call(trans.scope||window, result, trans.arg, true);
12646     },
12647
12648     // private
12649     handleFailure : function(trans){
12650         this.trans = false;
12651         this.destroyTrans(trans, false);
12652         this.fireEvent("loadexception", this, null, trans.arg);
12653         trans.callback.call(trans.scope||window, null, trans.arg, false);
12654     }
12655 });/*
12656  * Based on:
12657  * Ext JS Library 1.1.1
12658  * Copyright(c) 2006-2007, Ext JS, LLC.
12659  *
12660  * Originally Released Under LGPL - original licence link has changed is not relivant.
12661  *
12662  * Fork - LGPL
12663  * <script type="text/javascript">
12664  */
12665
12666 /**
12667  * @class Roo.data.JsonReader
12668  * @extends Roo.data.DataReader
12669  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12670  * based on mappings in a provided Roo.data.Record constructor.
12671  * 
12672  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12673  * in the reply previously. 
12674  * 
12675  * <p>
12676  * Example code:
12677  * <pre><code>
12678 var RecordDef = Roo.data.Record.create([
12679     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12680     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12681 ]);
12682 var myReader = new Roo.data.JsonReader({
12683     totalProperty: "results",    // The property which contains the total dataset size (optional)
12684     root: "rows",                // The property which contains an Array of row objects
12685     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12686 }, RecordDef);
12687 </code></pre>
12688  * <p>
12689  * This would consume a JSON file like this:
12690  * <pre><code>
12691 { 'results': 2, 'rows': [
12692     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12693     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12694 }
12695 </code></pre>
12696  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12697  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12698  * paged from the remote server.
12699  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12700  * @cfg {String} root name of the property which contains the Array of row objects.
12701  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12702  * @cfg {Array} fields Array of field definition objects
12703  * @constructor
12704  * Create a new JsonReader
12705  * @param {Object} meta Metadata configuration options
12706  * @param {Object} recordType Either an Array of field definition objects,
12707  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12708  */
12709 Roo.data.JsonReader = function(meta, recordType){
12710     
12711     meta = meta || {};
12712     // set some defaults:
12713     Roo.applyIf(meta, {
12714         totalProperty: 'total',
12715         successProperty : 'success',
12716         root : 'data',
12717         id : 'id'
12718     });
12719     
12720     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12721 };
12722 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12723     
12724     /**
12725      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12726      * Used by Store query builder to append _requestMeta to params.
12727      * 
12728      */
12729     metaFromRemote : false,
12730     /**
12731      * This method is only used by a DataProxy which has retrieved data from a remote server.
12732      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12733      * @return {Object} data A data block which is used by an Roo.data.Store object as
12734      * a cache of Roo.data.Records.
12735      */
12736     read : function(response){
12737         var json = response.responseText;
12738        
12739         var o = /* eval:var:o */ eval("("+json+")");
12740         if(!o) {
12741             throw {message: "JsonReader.read: Json object not found"};
12742         }
12743         
12744         if(o.metaData){
12745             
12746             delete this.ef;
12747             this.metaFromRemote = true;
12748             this.meta = o.metaData;
12749             this.recordType = Roo.data.Record.create(o.metaData.fields);
12750             this.onMetaChange(this.meta, this.recordType, o);
12751         }
12752         return this.readRecords(o);
12753     },
12754
12755     // private function a store will implement
12756     onMetaChange : function(meta, recordType, o){
12757
12758     },
12759
12760     /**
12761          * @ignore
12762          */
12763     simpleAccess: function(obj, subsc) {
12764         return obj[subsc];
12765     },
12766
12767         /**
12768          * @ignore
12769          */
12770     getJsonAccessor: function(){
12771         var re = /[\[\.]/;
12772         return function(expr) {
12773             try {
12774                 return(re.test(expr))
12775                     ? new Function("obj", "return obj." + expr)
12776                     : function(obj){
12777                         return obj[expr];
12778                     };
12779             } catch(e){}
12780             return Roo.emptyFn;
12781         };
12782     }(),
12783
12784     /**
12785      * Create a data block containing Roo.data.Records from an XML document.
12786      * @param {Object} o An object which contains an Array of row objects in the property specified
12787      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12788      * which contains the total size of the dataset.
12789      * @return {Object} data A data block which is used by an Roo.data.Store object as
12790      * a cache of Roo.data.Records.
12791      */
12792     readRecords : function(o){
12793         /**
12794          * After any data loads, the raw JSON data is available for further custom processing.
12795          * @type Object
12796          */
12797         this.o = o;
12798         var s = this.meta, Record = this.recordType,
12799             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12800
12801 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12802         if (!this.ef) {
12803             if(s.totalProperty) {
12804                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12805                 }
12806                 if(s.successProperty) {
12807                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12808                 }
12809                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12810                 if (s.id) {
12811                         var g = this.getJsonAccessor(s.id);
12812                         this.getId = function(rec) {
12813                                 var r = g(rec);  
12814                                 return (r === undefined || r === "") ? null : r;
12815                         };
12816                 } else {
12817                         this.getId = function(){return null;};
12818                 }
12819             this.ef = [];
12820             for(var jj = 0; jj < fl; jj++){
12821                 f = fi[jj];
12822                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12823                 this.ef[jj] = this.getJsonAccessor(map);
12824             }
12825         }
12826
12827         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12828         if(s.totalProperty){
12829             var vt = parseInt(this.getTotal(o), 10);
12830             if(!isNaN(vt)){
12831                 totalRecords = vt;
12832             }
12833         }
12834         if(s.successProperty){
12835             var vs = this.getSuccess(o);
12836             if(vs === false || vs === 'false'){
12837                 success = false;
12838             }
12839         }
12840         var records = [];
12841         for(var i = 0; i < c; i++){
12842                 var n = root[i];
12843             var values = {};
12844             var id = this.getId(n);
12845             for(var j = 0; j < fl; j++){
12846                 f = fi[j];
12847             var v = this.ef[j](n);
12848             if (!f.convert) {
12849                 Roo.log('missing convert for ' + f.name);
12850                 Roo.log(f);
12851                 continue;
12852             }
12853             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12854             }
12855             var record = new Record(values, id);
12856             record.json = n;
12857             records[i] = record;
12858         }
12859         return {
12860             raw : o,
12861             success : success,
12862             records : records,
12863             totalRecords : totalRecords
12864         };
12865     }
12866 });/*
12867  * Based on:
12868  * Ext JS Library 1.1.1
12869  * Copyright(c) 2006-2007, Ext JS, LLC.
12870  *
12871  * Originally Released Under LGPL - original licence link has changed is not relivant.
12872  *
12873  * Fork - LGPL
12874  * <script type="text/javascript">
12875  */
12876
12877 /**
12878  * @class Roo.data.ArrayReader
12879  * @extends Roo.data.DataReader
12880  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12881  * Each element of that Array represents a row of data fields. The
12882  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12883  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12884  * <p>
12885  * Example code:.
12886  * <pre><code>
12887 var RecordDef = Roo.data.Record.create([
12888     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12889     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12890 ]);
12891 var myReader = new Roo.data.ArrayReader({
12892     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12893 }, RecordDef);
12894 </code></pre>
12895  * <p>
12896  * This would consume an Array like this:
12897  * <pre><code>
12898 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12899   </code></pre>
12900  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12901  * @constructor
12902  * Create a new JsonReader
12903  * @param {Object} meta Metadata configuration options.
12904  * @param {Object} recordType Either an Array of field definition objects
12905  * as specified to {@link Roo.data.Record#create},
12906  * or an {@link Roo.data.Record} object
12907  * created using {@link Roo.data.Record#create}.
12908  */
12909 Roo.data.ArrayReader = function(meta, recordType){
12910     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12911 };
12912
12913 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12914     /**
12915      * Create a data block containing Roo.data.Records from an XML document.
12916      * @param {Object} o An Array of row objects which represents the dataset.
12917      * @return {Object} data A data block which is used by an Roo.data.Store object as
12918      * a cache of Roo.data.Records.
12919      */
12920     readRecords : function(o){
12921         var sid = this.meta ? this.meta.id : null;
12922         var recordType = this.recordType, fields = recordType.prototype.fields;
12923         var records = [];
12924         var root = o;
12925             for(var i = 0; i < root.length; i++){
12926                     var n = root[i];
12927                 var values = {};
12928                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12929                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12930                 var f = fields.items[j];
12931                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12932                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12933                 v = f.convert(v);
12934                 values[f.name] = v;
12935             }
12936                 var record = new recordType(values, id);
12937                 record.json = n;
12938                 records[records.length] = record;
12939             }
12940             return {
12941                 records : records,
12942                 totalRecords : records.length
12943             };
12944     }
12945 });/*
12946  * - LGPL
12947  * * 
12948  */
12949
12950 /**
12951  * @class Roo.bootstrap.ComboBox
12952  * @extends Roo.bootstrap.TriggerField
12953  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12954  * @cfg {Boolean} append (true|false) default false
12955  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12956  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12957  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12958  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12959  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12960  * @cfg {Boolean} animate default true
12961  * @cfg {Boolean} emptyResultText only for touch device
12962  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12963  * @cfg {String} emptyTitle default ''
12964  * @constructor
12965  * Create a new ComboBox.
12966  * @param {Object} config Configuration options
12967  */
12968 Roo.bootstrap.ComboBox = function(config){
12969     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12970     this.addEvents({
12971         /**
12972          * @event expand
12973          * Fires when the dropdown list is expanded
12974         * @param {Roo.bootstrap.ComboBox} combo This combo box
12975         */
12976         'expand' : true,
12977         /**
12978          * @event collapse
12979          * Fires when the dropdown list is collapsed
12980         * @param {Roo.bootstrap.ComboBox} combo This combo box
12981         */
12982         'collapse' : true,
12983         /**
12984          * @event beforeselect
12985          * Fires before a list item is selected. Return false to cancel the selection.
12986         * @param {Roo.bootstrap.ComboBox} combo This combo box
12987         * @param {Roo.data.Record} record The data record returned from the underlying store
12988         * @param {Number} index The index of the selected item in the dropdown list
12989         */
12990         'beforeselect' : true,
12991         /**
12992          * @event select
12993          * Fires when a list item is selected
12994         * @param {Roo.bootstrap.ComboBox} combo This combo box
12995         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12996         * @param {Number} index The index of the selected item in the dropdown list
12997         */
12998         'select' : true,
12999         /**
13000          * @event beforequery
13001          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13002          * The event object passed has these properties:
13003         * @param {Roo.bootstrap.ComboBox} combo This combo box
13004         * @param {String} query The query
13005         * @param {Boolean} forceAll true to force "all" query
13006         * @param {Boolean} cancel true to cancel the query
13007         * @param {Object} e The query event object
13008         */
13009         'beforequery': true,
13010          /**
13011          * @event add
13012          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13013         * @param {Roo.bootstrap.ComboBox} combo This combo box
13014         */
13015         'add' : true,
13016         /**
13017          * @event edit
13018          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13019         * @param {Roo.bootstrap.ComboBox} combo This combo box
13020         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13021         */
13022         'edit' : true,
13023         /**
13024          * @event remove
13025          * Fires when the remove value from the combobox array
13026         * @param {Roo.bootstrap.ComboBox} combo This combo box
13027         */
13028         'remove' : true,
13029         /**
13030          * @event afterremove
13031          * Fires when the remove value from the combobox array
13032         * @param {Roo.bootstrap.ComboBox} combo This combo box
13033         */
13034         'afterremove' : true,
13035         /**
13036          * @event specialfilter
13037          * Fires when specialfilter
13038             * @param {Roo.bootstrap.ComboBox} combo This combo box
13039             */
13040         'specialfilter' : true,
13041         /**
13042          * @event tick
13043          * Fires when tick the element
13044             * @param {Roo.bootstrap.ComboBox} combo This combo box
13045             */
13046         'tick' : true,
13047         /**
13048          * @event touchviewdisplay
13049          * Fires when touch view require special display (default is using displayField)
13050             * @param {Roo.bootstrap.ComboBox} combo This combo box
13051             * @param {Object} cfg set html .
13052             */
13053         'touchviewdisplay' : true
13054         
13055     });
13056     
13057     this.item = [];
13058     this.tickItems = [];
13059     
13060     this.selectedIndex = -1;
13061     if(this.mode == 'local'){
13062         if(config.queryDelay === undefined){
13063             this.queryDelay = 10;
13064         }
13065         if(config.minChars === undefined){
13066             this.minChars = 0;
13067         }
13068     }
13069 };
13070
13071 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13072      
13073     /**
13074      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13075      * rendering into an Roo.Editor, defaults to false)
13076      */
13077     /**
13078      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13079      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13080      */
13081     /**
13082      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13083      */
13084     /**
13085      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13086      * the dropdown list (defaults to undefined, with no header element)
13087      */
13088
13089      /**
13090      * @cfg {String/Roo.Template} tpl The template to use to render the output
13091      */
13092      
13093      /**
13094      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13095      */
13096     listWidth: undefined,
13097     /**
13098      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13099      * mode = 'remote' or 'text' if mode = 'local')
13100      */
13101     displayField: undefined,
13102     
13103     /**
13104      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13105      * mode = 'remote' or 'value' if mode = 'local'). 
13106      * Note: use of a valueField requires the user make a selection
13107      * in order for a value to be mapped.
13108      */
13109     valueField: undefined,
13110     /**
13111      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13112      */
13113     modalTitle : '',
13114     
13115     /**
13116      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13117      * field's data value (defaults to the underlying DOM element's name)
13118      */
13119     hiddenName: undefined,
13120     /**
13121      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13122      */
13123     listClass: '',
13124     /**
13125      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13126      */
13127     selectedClass: 'active',
13128     
13129     /**
13130      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13131      */
13132     shadow:'sides',
13133     /**
13134      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13135      * anchor positions (defaults to 'tl-bl')
13136      */
13137     listAlign: 'tl-bl?',
13138     /**
13139      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13140      */
13141     maxHeight: 300,
13142     /**
13143      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13144      * query specified by the allQuery config option (defaults to 'query')
13145      */
13146     triggerAction: 'query',
13147     /**
13148      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13149      * (defaults to 4, does not apply if editable = false)
13150      */
13151     minChars : 4,
13152     /**
13153      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13154      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13155      */
13156     typeAhead: false,
13157     /**
13158      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13159      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13160      */
13161     queryDelay: 500,
13162     /**
13163      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13164      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13165      */
13166     pageSize: 0,
13167     /**
13168      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13169      * when editable = true (defaults to false)
13170      */
13171     selectOnFocus:false,
13172     /**
13173      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13174      */
13175     queryParam: 'query',
13176     /**
13177      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13178      * when mode = 'remote' (defaults to 'Loading...')
13179      */
13180     loadingText: 'Loading...',
13181     /**
13182      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13183      */
13184     resizable: false,
13185     /**
13186      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13187      */
13188     handleHeight : 8,
13189     /**
13190      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13191      * traditional select (defaults to true)
13192      */
13193     editable: true,
13194     /**
13195      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13196      */
13197     allQuery: '',
13198     /**
13199      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13200      */
13201     mode: 'remote',
13202     /**
13203      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13204      * listWidth has a higher value)
13205      */
13206     minListWidth : 70,
13207     /**
13208      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13209      * allow the user to set arbitrary text into the field (defaults to false)
13210      */
13211     forceSelection:false,
13212     /**
13213      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13214      * if typeAhead = true (defaults to 250)
13215      */
13216     typeAheadDelay : 250,
13217     /**
13218      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13219      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13220      */
13221     valueNotFoundText : undefined,
13222     /**
13223      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13224      */
13225     blockFocus : false,
13226     
13227     /**
13228      * @cfg {Boolean} disableClear Disable showing of clear button.
13229      */
13230     disableClear : false,
13231     /**
13232      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13233      */
13234     alwaysQuery : false,
13235     
13236     /**
13237      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13238      */
13239     multiple : false,
13240     
13241     /**
13242      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13243      */
13244     invalidClass : "has-warning",
13245     
13246     /**
13247      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13248      */
13249     validClass : "has-success",
13250     
13251     /**
13252      * @cfg {Boolean} specialFilter (true|false) special filter default false
13253      */
13254     specialFilter : false,
13255     
13256     /**
13257      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13258      */
13259     mobileTouchView : true,
13260     
13261     /**
13262      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13263      */
13264     useNativeIOS : false,
13265     
13266     /**
13267      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13268      */
13269     mobile_restrict_height : false,
13270     
13271     ios_options : false,
13272     
13273     //private
13274     addicon : false,
13275     editicon: false,
13276     
13277     page: 0,
13278     hasQuery: false,
13279     append: false,
13280     loadNext: false,
13281     autoFocus : true,
13282     tickable : false,
13283     btnPosition : 'right',
13284     triggerList : true,
13285     showToggleBtn : true,
13286     animate : true,
13287     emptyResultText: 'Empty',
13288     triggerText : 'Select',
13289     emptyTitle : '',
13290     
13291     // element that contains real text value.. (when hidden is used..)
13292     
13293     getAutoCreate : function()
13294     {   
13295         var cfg = false;
13296         //render
13297         /*
13298          * Render classic select for iso
13299          */
13300         
13301         if(Roo.isIOS && this.useNativeIOS){
13302             cfg = this.getAutoCreateNativeIOS();
13303             return cfg;
13304         }
13305         
13306         /*
13307          * Touch Devices
13308          */
13309         
13310         if(Roo.isTouch && this.mobileTouchView){
13311             cfg = this.getAutoCreateTouchView();
13312             return cfg;;
13313         }
13314         
13315         /*
13316          *  Normal ComboBox
13317          */
13318         if(!this.tickable){
13319             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13320             return cfg;
13321         }
13322         
13323         /*
13324          *  ComboBox with tickable selections
13325          */
13326              
13327         var align = this.labelAlign || this.parentLabelAlign();
13328         
13329         cfg = {
13330             cls : 'form-group roo-combobox-tickable' //input-group
13331         };
13332         
13333         var btn_text_select = '';
13334         var btn_text_done = '';
13335         var btn_text_cancel = '';
13336         
13337         if (this.btn_text_show) {
13338             btn_text_select = 'Select';
13339             btn_text_done = 'Done';
13340             btn_text_cancel = 'Cancel'; 
13341         }
13342         
13343         var buttons = {
13344             tag : 'div',
13345             cls : 'tickable-buttons',
13346             cn : [
13347                 {
13348                     tag : 'button',
13349                     type : 'button',
13350                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13351                     //html : this.triggerText
13352                     html: btn_text_select
13353                 },
13354                 {
13355                     tag : 'button',
13356                     type : 'button',
13357                     name : 'ok',
13358                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13359                     //html : 'Done'
13360                     html: btn_text_done
13361                 },
13362                 {
13363                     tag : 'button',
13364                     type : 'button',
13365                     name : 'cancel',
13366                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13367                     //html : 'Cancel'
13368                     html: btn_text_cancel
13369                 }
13370             ]
13371         };
13372         
13373         if(this.editable){
13374             buttons.cn.unshift({
13375                 tag: 'input',
13376                 cls: 'roo-select2-search-field-input'
13377             });
13378         }
13379         
13380         var _this = this;
13381         
13382         Roo.each(buttons.cn, function(c){
13383             if (_this.size) {
13384                 c.cls += ' btn-' + _this.size;
13385             }
13386
13387             if (_this.disabled) {
13388                 c.disabled = true;
13389             }
13390         });
13391         
13392         var box = {
13393             tag: 'div',
13394             style : 'display: contents',
13395             cn: [
13396                 {
13397                     tag: 'input',
13398                     type : 'hidden',
13399                     cls: 'form-hidden-field'
13400                 },
13401                 {
13402                     tag: 'ul',
13403                     cls: 'roo-select2-choices',
13404                     cn:[
13405                         {
13406                             tag: 'li',
13407                             cls: 'roo-select2-search-field',
13408                             cn: [
13409                                 buttons
13410                             ]
13411                         }
13412                     ]
13413                 }
13414             ]
13415         };
13416         
13417         var combobox = {
13418             cls: 'roo-select2-container input-group roo-select2-container-multi',
13419             cn: [
13420                 
13421                 box
13422 //                {
13423 //                    tag: 'ul',
13424 //                    cls: 'typeahead typeahead-long dropdown-menu',
13425 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13426 //                }
13427             ]
13428         };
13429         
13430         if(this.hasFeedback && !this.allowBlank){
13431             
13432             var feedback = {
13433                 tag: 'span',
13434                 cls: 'glyphicon form-control-feedback'
13435             };
13436
13437             combobox.cn.push(feedback);
13438         }
13439         
13440         var indicator = {
13441             tag : 'i',
13442             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13443             tooltip : 'This field is required'
13444         };
13445         if (Roo.bootstrap.version == 4) {
13446             indicator = {
13447                 tag : 'i',
13448                 style : 'display:none'
13449             };
13450         }
13451         if (align ==='left' && this.fieldLabel.length) {
13452             
13453             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13454             
13455             cfg.cn = [
13456                 indicator,
13457                 {
13458                     tag: 'label',
13459                     'for' :  id,
13460                     cls : 'control-label col-form-label',
13461                     html : this.fieldLabel
13462
13463                 },
13464                 {
13465                     cls : "", 
13466                     cn: [
13467                         combobox
13468                     ]
13469                 }
13470
13471             ];
13472             
13473             var labelCfg = cfg.cn[1];
13474             var contentCfg = cfg.cn[2];
13475             
13476
13477             if(this.indicatorpos == 'right'){
13478                 
13479                 cfg.cn = [
13480                     {
13481                         tag: 'label',
13482                         'for' :  id,
13483                         cls : 'control-label col-form-label',
13484                         cn : [
13485                             {
13486                                 tag : 'span',
13487                                 html : this.fieldLabel
13488                             },
13489                             indicator
13490                         ]
13491                     },
13492                     {
13493                         cls : "",
13494                         cn: [
13495                             combobox
13496                         ]
13497                     }
13498
13499                 ];
13500                 
13501                 
13502                 
13503                 labelCfg = cfg.cn[0];
13504                 contentCfg = cfg.cn[1];
13505             
13506             }
13507             
13508             if(this.labelWidth > 12){
13509                 labelCfg.style = "width: " + this.labelWidth + 'px';
13510             }
13511             
13512             if(this.labelWidth < 13 && this.labelmd == 0){
13513                 this.labelmd = this.labelWidth;
13514             }
13515             
13516             if(this.labellg > 0){
13517                 labelCfg.cls += ' col-lg-' + this.labellg;
13518                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13519             }
13520             
13521             if(this.labelmd > 0){
13522                 labelCfg.cls += ' col-md-' + this.labelmd;
13523                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13524             }
13525             
13526             if(this.labelsm > 0){
13527                 labelCfg.cls += ' col-sm-' + this.labelsm;
13528                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13529             }
13530             
13531             if(this.labelxs > 0){
13532                 labelCfg.cls += ' col-xs-' + this.labelxs;
13533                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13534             }
13535                 
13536                 
13537         } else if ( this.fieldLabel.length) {
13538 //                Roo.log(" label");
13539                  cfg.cn = [
13540                    indicator,
13541                     {
13542                         tag: 'label',
13543                         //cls : 'input-group-addon',
13544                         html : this.fieldLabel
13545                     },
13546                     combobox
13547                 ];
13548                 
13549                 if(this.indicatorpos == 'right'){
13550                     cfg.cn = [
13551                         {
13552                             tag: 'label',
13553                             //cls : 'input-group-addon',
13554                             html : this.fieldLabel
13555                         },
13556                         indicator,
13557                         combobox
13558                     ];
13559                     
13560                 }
13561
13562         } else {
13563             
13564 //                Roo.log(" no label && no align");
13565                 cfg = combobox
13566                      
13567                 
13568         }
13569          
13570         var settings=this;
13571         ['xs','sm','md','lg'].map(function(size){
13572             if (settings[size]) {
13573                 cfg.cls += ' col-' + size + '-' + settings[size];
13574             }
13575         });
13576         
13577         return cfg;
13578         
13579     },
13580     
13581     _initEventsCalled : false,
13582     
13583     // private
13584     initEvents: function()
13585     {   
13586         if (this._initEventsCalled) { // as we call render... prevent looping...
13587             return;
13588         }
13589         this._initEventsCalled = true;
13590         
13591         if (!this.store) {
13592             throw "can not find store for combo";
13593         }
13594         
13595         this.indicator = this.indicatorEl();
13596         
13597         this.store = Roo.factory(this.store, Roo.data);
13598         this.store.parent = this;
13599         
13600         // if we are building from html. then this element is so complex, that we can not really
13601         // use the rendered HTML.
13602         // so we have to trash and replace the previous code.
13603         if (Roo.XComponent.build_from_html) {
13604             // remove this element....
13605             var e = this.el.dom, k=0;
13606             while (e ) { e = e.previousSibling;  ++k;}
13607
13608             this.el.remove();
13609             
13610             this.el=false;
13611             this.rendered = false;
13612             
13613             this.render(this.parent().getChildContainer(true), k);
13614         }
13615         
13616         if(Roo.isIOS && this.useNativeIOS){
13617             this.initIOSView();
13618             return;
13619         }
13620         
13621         /*
13622          * Touch Devices
13623          */
13624         
13625         if(Roo.isTouch && this.mobileTouchView){
13626             this.initTouchView();
13627             return;
13628         }
13629         
13630         if(this.tickable){
13631             this.initTickableEvents();
13632             return;
13633         }
13634         
13635         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13636         
13637         if(this.hiddenName){
13638             
13639             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13640             
13641             this.hiddenField.dom.value =
13642                 this.hiddenValue !== undefined ? this.hiddenValue :
13643                 this.value !== undefined ? this.value : '';
13644
13645             // prevent input submission
13646             this.el.dom.removeAttribute('name');
13647             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13648              
13649              
13650         }
13651         //if(Roo.isGecko){
13652         //    this.el.dom.setAttribute('autocomplete', 'off');
13653         //}
13654         
13655         var cls = 'x-combo-list';
13656         
13657         //this.list = new Roo.Layer({
13658         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13659         //});
13660         
13661         var _this = this;
13662         
13663         (function(){
13664             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13665             _this.list.setWidth(lw);
13666         }).defer(100);
13667         
13668         this.list.on('mouseover', this.onViewOver, this);
13669         this.list.on('mousemove', this.onViewMove, this);
13670         this.list.on('scroll', this.onViewScroll, this);
13671         
13672         /*
13673         this.list.swallowEvent('mousewheel');
13674         this.assetHeight = 0;
13675
13676         if(this.title){
13677             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13678             this.assetHeight += this.header.getHeight();
13679         }
13680
13681         this.innerList = this.list.createChild({cls:cls+'-inner'});
13682         this.innerList.on('mouseover', this.onViewOver, this);
13683         this.innerList.on('mousemove', this.onViewMove, this);
13684         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13685         
13686         if(this.allowBlank && !this.pageSize && !this.disableClear){
13687             this.footer = this.list.createChild({cls:cls+'-ft'});
13688             this.pageTb = new Roo.Toolbar(this.footer);
13689            
13690         }
13691         if(this.pageSize){
13692             this.footer = this.list.createChild({cls:cls+'-ft'});
13693             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13694                     {pageSize: this.pageSize});
13695             
13696         }
13697         
13698         if (this.pageTb && this.allowBlank && !this.disableClear) {
13699             var _this = this;
13700             this.pageTb.add(new Roo.Toolbar.Fill(), {
13701                 cls: 'x-btn-icon x-btn-clear',
13702                 text: '&#160;',
13703                 handler: function()
13704                 {
13705                     _this.collapse();
13706                     _this.clearValue();
13707                     _this.onSelect(false, -1);
13708                 }
13709             });
13710         }
13711         if (this.footer) {
13712             this.assetHeight += this.footer.getHeight();
13713         }
13714         */
13715             
13716         if(!this.tpl){
13717             this.tpl = Roo.bootstrap.version == 4 ?
13718                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13719                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13720         }
13721
13722         this.view = new Roo.View(this.list, this.tpl, {
13723             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13724         });
13725         //this.view.wrapEl.setDisplayed(false);
13726         this.view.on('click', this.onViewClick, this);
13727         
13728         
13729         this.store.on('beforeload', this.onBeforeLoad, this);
13730         this.store.on('load', this.onLoad, this);
13731         this.store.on('loadexception', this.onLoadException, this);
13732         /*
13733         if(this.resizable){
13734             this.resizer = new Roo.Resizable(this.list,  {
13735                pinned:true, handles:'se'
13736             });
13737             this.resizer.on('resize', function(r, w, h){
13738                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13739                 this.listWidth = w;
13740                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13741                 this.restrictHeight();
13742             }, this);
13743             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13744         }
13745         */
13746         if(!this.editable){
13747             this.editable = true;
13748             this.setEditable(false);
13749         }
13750         
13751         /*
13752         
13753         if (typeof(this.events.add.listeners) != 'undefined') {
13754             
13755             this.addicon = this.wrap.createChild(
13756                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13757        
13758             this.addicon.on('click', function(e) {
13759                 this.fireEvent('add', this);
13760             }, this);
13761         }
13762         if (typeof(this.events.edit.listeners) != 'undefined') {
13763             
13764             this.editicon = this.wrap.createChild(
13765                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13766             if (this.addicon) {
13767                 this.editicon.setStyle('margin-left', '40px');
13768             }
13769             this.editicon.on('click', function(e) {
13770                 
13771                 // we fire even  if inothing is selected..
13772                 this.fireEvent('edit', this, this.lastData );
13773                 
13774             }, this);
13775         }
13776         */
13777         
13778         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13779             "up" : function(e){
13780                 this.inKeyMode = true;
13781                 this.selectPrev();
13782             },
13783
13784             "down" : function(e){
13785                 if(!this.isExpanded()){
13786                     this.onTriggerClick();
13787                 }else{
13788                     this.inKeyMode = true;
13789                     this.selectNext();
13790                 }
13791             },
13792
13793             "enter" : function(e){
13794 //                this.onViewClick();
13795                 //return true;
13796                 this.collapse();
13797                 
13798                 if(this.fireEvent("specialkey", this, e)){
13799                     this.onViewClick(false);
13800                 }
13801                 
13802                 return true;
13803             },
13804
13805             "esc" : function(e){
13806                 this.collapse();
13807             },
13808
13809             "tab" : function(e){
13810                 this.collapse();
13811                 
13812                 if(this.fireEvent("specialkey", this, e)){
13813                     this.onViewClick(false);
13814                 }
13815                 
13816                 return true;
13817             },
13818
13819             scope : this,
13820
13821             doRelay : function(foo, bar, hname){
13822                 if(hname == 'down' || this.scope.isExpanded()){
13823                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13824                 }
13825                 return true;
13826             },
13827
13828             forceKeyDown: true
13829         });
13830         
13831         
13832         this.queryDelay = Math.max(this.queryDelay || 10,
13833                 this.mode == 'local' ? 10 : 250);
13834         
13835         
13836         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13837         
13838         if(this.typeAhead){
13839             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13840         }
13841         if(this.editable !== false){
13842             this.inputEl().on("keyup", this.onKeyUp, this);
13843         }
13844         if(this.forceSelection){
13845             this.inputEl().on('blur', this.doForce, this);
13846         }
13847         
13848         if(this.multiple){
13849             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13850             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13851         }
13852     },
13853     
13854     initTickableEvents: function()
13855     {   
13856         this.createList();
13857         
13858         if(this.hiddenName){
13859             
13860             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13861             
13862             this.hiddenField.dom.value =
13863                 this.hiddenValue !== undefined ? this.hiddenValue :
13864                 this.value !== undefined ? this.value : '';
13865
13866             // prevent input submission
13867             this.el.dom.removeAttribute('name');
13868             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13869              
13870              
13871         }
13872         
13873 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13874         
13875         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13876         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13877         if(this.triggerList){
13878             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13879         }
13880          
13881         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13882         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13883         
13884         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13885         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13886         
13887         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13888         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13889         
13890         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13891         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13892         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13893         
13894         this.okBtn.hide();
13895         this.cancelBtn.hide();
13896         
13897         var _this = this;
13898         
13899         (function(){
13900             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13901             _this.list.setWidth(lw);
13902         }).defer(100);
13903         
13904         this.list.on('mouseover', this.onViewOver, this);
13905         this.list.on('mousemove', this.onViewMove, this);
13906         
13907         this.list.on('scroll', this.onViewScroll, this);
13908         
13909         if(!this.tpl){
13910             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13911                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13912         }
13913
13914         this.view = new Roo.View(this.list, this.tpl, {
13915             singleSelect:true,
13916             tickable:true,
13917             parent:this,
13918             store: this.store,
13919             selectedClass: this.selectedClass
13920         });
13921         
13922         //this.view.wrapEl.setDisplayed(false);
13923         this.view.on('click', this.onViewClick, this);
13924         
13925         
13926         
13927         this.store.on('beforeload', this.onBeforeLoad, this);
13928         this.store.on('load', this.onLoad, this);
13929         this.store.on('loadexception', this.onLoadException, this);
13930         
13931         if(this.editable){
13932             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13933                 "up" : function(e){
13934                     this.inKeyMode = true;
13935                     this.selectPrev();
13936                 },
13937
13938                 "down" : function(e){
13939                     this.inKeyMode = true;
13940                     this.selectNext();
13941                 },
13942
13943                 "enter" : function(e){
13944                     if(this.fireEvent("specialkey", this, e)){
13945                         this.onViewClick(false);
13946                     }
13947                     
13948                     return true;
13949                 },
13950
13951                 "esc" : function(e){
13952                     this.onTickableFooterButtonClick(e, false, false);
13953                 },
13954
13955                 "tab" : function(e){
13956                     this.fireEvent("specialkey", this, e);
13957                     
13958                     this.onTickableFooterButtonClick(e, false, false);
13959                     
13960                     return true;
13961                 },
13962
13963                 scope : this,
13964
13965                 doRelay : function(e, fn, key){
13966                     if(this.scope.isExpanded()){
13967                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13968                     }
13969                     return true;
13970                 },
13971
13972                 forceKeyDown: true
13973             });
13974         }
13975         
13976         this.queryDelay = Math.max(this.queryDelay || 10,
13977                 this.mode == 'local' ? 10 : 250);
13978         
13979         
13980         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13981         
13982         if(this.typeAhead){
13983             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13984         }
13985         
13986         if(this.editable !== false){
13987             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13988         }
13989         
13990         this.indicator = this.indicatorEl();
13991         
13992         if(this.indicator){
13993             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13994             this.indicator.hide();
13995         }
13996         
13997     },
13998
13999     onDestroy : function(){
14000         if(this.view){
14001             this.view.setStore(null);
14002             this.view.el.removeAllListeners();
14003             this.view.el.remove();
14004             this.view.purgeListeners();
14005         }
14006         if(this.list){
14007             this.list.dom.innerHTML  = '';
14008         }
14009         
14010         if(this.store){
14011             this.store.un('beforeload', this.onBeforeLoad, this);
14012             this.store.un('load', this.onLoad, this);
14013             this.store.un('loadexception', this.onLoadException, this);
14014         }
14015         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14016     },
14017
14018     // private
14019     fireKey : function(e){
14020         if(e.isNavKeyPress() && !this.list.isVisible()){
14021             this.fireEvent("specialkey", this, e);
14022         }
14023     },
14024
14025     // private
14026     onResize: function(w, h){
14027 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14028 //        
14029 //        if(typeof w != 'number'){
14030 //            // we do not handle it!?!?
14031 //            return;
14032 //        }
14033 //        var tw = this.trigger.getWidth();
14034 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14035 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14036 //        var x = w - tw;
14037 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14038 //            
14039 //        //this.trigger.setStyle('left', x+'px');
14040 //        
14041 //        if(this.list && this.listWidth === undefined){
14042 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14043 //            this.list.setWidth(lw);
14044 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14045 //        }
14046         
14047     
14048         
14049     },
14050
14051     /**
14052      * Allow or prevent the user from directly editing the field text.  If false is passed,
14053      * the user will only be able to select from the items defined in the dropdown list.  This method
14054      * is the runtime equivalent of setting the 'editable' config option at config time.
14055      * @param {Boolean} value True to allow the user to directly edit the field text
14056      */
14057     setEditable : function(value){
14058         if(value == this.editable){
14059             return;
14060         }
14061         this.editable = value;
14062         if(!value){
14063             this.inputEl().dom.setAttribute('readOnly', true);
14064             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14065             this.inputEl().addClass('x-combo-noedit');
14066         }else{
14067             this.inputEl().dom.setAttribute('readOnly', false);
14068             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14069             this.inputEl().removeClass('x-combo-noedit');
14070         }
14071     },
14072
14073     // private
14074     
14075     onBeforeLoad : function(combo,opts){
14076         if(!this.hasFocus){
14077             return;
14078         }
14079          if (!opts.add) {
14080             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14081          }
14082         this.restrictHeight();
14083         this.selectedIndex = -1;
14084     },
14085
14086     // private
14087     onLoad : function(){
14088         
14089         this.hasQuery = false;
14090         
14091         if(!this.hasFocus){
14092             return;
14093         }
14094         
14095         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14096             this.loading.hide();
14097         }
14098         
14099         if(this.store.getCount() > 0){
14100             
14101             this.expand();
14102             this.restrictHeight();
14103             if(this.lastQuery == this.allQuery){
14104                 if(this.editable && !this.tickable){
14105                     this.inputEl().dom.select();
14106                 }
14107                 
14108                 if(
14109                     !this.selectByValue(this.value, true) &&
14110                     this.autoFocus && 
14111                     (
14112                         !this.store.lastOptions ||
14113                         typeof(this.store.lastOptions.add) == 'undefined' || 
14114                         this.store.lastOptions.add != true
14115                     )
14116                 ){
14117                     this.select(0, true);
14118                 }
14119             }else{
14120                 if(this.autoFocus){
14121                     this.selectNext();
14122                 }
14123                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14124                     this.taTask.delay(this.typeAheadDelay);
14125                 }
14126             }
14127         }else{
14128             this.onEmptyResults();
14129         }
14130         
14131         //this.el.focus();
14132     },
14133     // private
14134     onLoadException : function()
14135     {
14136         this.hasQuery = false;
14137         
14138         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14139             this.loading.hide();
14140         }
14141         
14142         if(this.tickable && this.editable){
14143             return;
14144         }
14145         
14146         this.collapse();
14147         // only causes errors at present
14148         //Roo.log(this.store.reader.jsonData);
14149         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14150             // fixme
14151             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14152         //}
14153         
14154         
14155     },
14156     // private
14157     onTypeAhead : function(){
14158         if(this.store.getCount() > 0){
14159             var r = this.store.getAt(0);
14160             var newValue = r.data[this.displayField];
14161             var len = newValue.length;
14162             var selStart = this.getRawValue().length;
14163             
14164             if(selStart != len){
14165                 this.setRawValue(newValue);
14166                 this.selectText(selStart, newValue.length);
14167             }
14168         }
14169     },
14170
14171     // private
14172     onSelect : function(record, index){
14173         
14174         if(this.fireEvent('beforeselect', this, record, index) !== false){
14175         
14176             this.setFromData(index > -1 ? record.data : false);
14177             
14178             this.collapse();
14179             this.fireEvent('select', this, record, index);
14180         }
14181     },
14182
14183     /**
14184      * Returns the currently selected field value or empty string if no value is set.
14185      * @return {String} value The selected value
14186      */
14187     getValue : function()
14188     {
14189         if(Roo.isIOS && this.useNativeIOS){
14190             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14191         }
14192         
14193         if(this.multiple){
14194             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14195         }
14196         
14197         if(this.valueField){
14198             return typeof this.value != 'undefined' ? this.value : '';
14199         }else{
14200             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14201         }
14202     },
14203     
14204     getRawValue : function()
14205     {
14206         if(Roo.isIOS && this.useNativeIOS){
14207             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14208         }
14209         
14210         var v = this.inputEl().getValue();
14211         
14212         return v;
14213     },
14214
14215     /**
14216      * Clears any text/value currently set in the field
14217      */
14218     clearValue : function(){
14219         
14220         if(this.hiddenField){
14221             this.hiddenField.dom.value = '';
14222         }
14223         this.value = '';
14224         this.setRawValue('');
14225         this.lastSelectionText = '';
14226         this.lastData = false;
14227         
14228         var close = this.closeTriggerEl();
14229         
14230         if(close){
14231             close.hide();
14232         }
14233         
14234         this.validate();
14235         
14236     },
14237
14238     /**
14239      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14240      * will be displayed in the field.  If the value does not match the data value of an existing item,
14241      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14242      * Otherwise the field will be blank (although the value will still be set).
14243      * @param {String} value The value to match
14244      */
14245     setValue : function(v)
14246     {
14247         if(Roo.isIOS && this.useNativeIOS){
14248             this.setIOSValue(v);
14249             return;
14250         }
14251         
14252         if(this.multiple){
14253             this.syncValue();
14254             return;
14255         }
14256         
14257         var text = v;
14258         if(this.valueField){
14259             var r = this.findRecord(this.valueField, v);
14260             if(r){
14261                 text = r.data[this.displayField];
14262             }else if(this.valueNotFoundText !== undefined){
14263                 text = this.valueNotFoundText;
14264             }
14265         }
14266         this.lastSelectionText = text;
14267         if(this.hiddenField){
14268             this.hiddenField.dom.value = v;
14269         }
14270         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14271         this.value = v;
14272         
14273         var close = this.closeTriggerEl();
14274         
14275         if(close){
14276             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14277         }
14278         
14279         this.validate();
14280     },
14281     /**
14282      * @property {Object} the last set data for the element
14283      */
14284     
14285     lastData : false,
14286     /**
14287      * Sets the value of the field based on a object which is related to the record format for the store.
14288      * @param {Object} value the value to set as. or false on reset?
14289      */
14290     setFromData : function(o){
14291         
14292         if(this.multiple){
14293             this.addItem(o);
14294             return;
14295         }
14296             
14297         var dv = ''; // display value
14298         var vv = ''; // value value..
14299         this.lastData = o;
14300         if (this.displayField) {
14301             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14302         } else {
14303             // this is an error condition!!!
14304             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14305         }
14306         
14307         if(this.valueField){
14308             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14309         }
14310         
14311         var close = this.closeTriggerEl();
14312         
14313         if(close){
14314             if(dv.length || vv * 1 > 0){
14315                 close.show() ;
14316                 this.blockFocus=true;
14317             } else {
14318                 close.hide();
14319             }             
14320         }
14321         
14322         if(this.hiddenField){
14323             this.hiddenField.dom.value = vv;
14324             
14325             this.lastSelectionText = dv;
14326             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14327             this.value = vv;
14328             return;
14329         }
14330         // no hidden field.. - we store the value in 'value', but still display
14331         // display field!!!!
14332         this.lastSelectionText = dv;
14333         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14334         this.value = vv;
14335         
14336         
14337         
14338     },
14339     // private
14340     reset : function(){
14341         // overridden so that last data is reset..
14342         
14343         if(this.multiple){
14344             this.clearItem();
14345             return;
14346         }
14347         
14348         this.setValue(this.originalValue);
14349         //this.clearInvalid();
14350         this.lastData = false;
14351         if (this.view) {
14352             this.view.clearSelections();
14353         }
14354         
14355         this.validate();
14356     },
14357     // private
14358     findRecord : function(prop, value){
14359         var record;
14360         if(this.store.getCount() > 0){
14361             this.store.each(function(r){
14362                 if(r.data[prop] == value){
14363                     record = r;
14364                     return false;
14365                 }
14366                 return true;
14367             });
14368         }
14369         return record;
14370     },
14371     
14372     getName: function()
14373     {
14374         // returns hidden if it's set..
14375         if (!this.rendered) {return ''};
14376         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14377         
14378     },
14379     // private
14380     onViewMove : function(e, t){
14381         this.inKeyMode = false;
14382     },
14383
14384     // private
14385     onViewOver : function(e, t){
14386         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14387             return;
14388         }
14389         var item = this.view.findItemFromChild(t);
14390         
14391         if(item){
14392             var index = this.view.indexOf(item);
14393             this.select(index, false);
14394         }
14395     },
14396
14397     // private
14398     onViewClick : function(view, doFocus, el, e)
14399     {
14400         var index = this.view.getSelectedIndexes()[0];
14401         
14402         var r = this.store.getAt(index);
14403         
14404         if(this.tickable){
14405             
14406             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14407                 return;
14408             }
14409             
14410             var rm = false;
14411             var _this = this;
14412             
14413             Roo.each(this.tickItems, function(v,k){
14414                 
14415                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14416                     Roo.log(v);
14417                     _this.tickItems.splice(k, 1);
14418                     
14419                     if(typeof(e) == 'undefined' && view == false){
14420                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14421                     }
14422                     
14423                     rm = true;
14424                     return;
14425                 }
14426             });
14427             
14428             if(rm){
14429                 return;
14430             }
14431             
14432             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14433                 this.tickItems.push(r.data);
14434             }
14435             
14436             if(typeof(e) == 'undefined' && view == false){
14437                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14438             }
14439                     
14440             return;
14441         }
14442         
14443         if(r){
14444             this.onSelect(r, index);
14445         }
14446         if(doFocus !== false && !this.blockFocus){
14447             this.inputEl().focus();
14448         }
14449     },
14450
14451     // private
14452     restrictHeight : function(){
14453         //this.innerList.dom.style.height = '';
14454         //var inner = this.innerList.dom;
14455         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14456         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14457         //this.list.beginUpdate();
14458         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14459         this.list.alignTo(this.inputEl(), this.listAlign);
14460         this.list.alignTo(this.inputEl(), this.listAlign);
14461         //this.list.endUpdate();
14462     },
14463
14464     // private
14465     onEmptyResults : function(){
14466         
14467         if(this.tickable && this.editable){
14468             this.hasFocus = false;
14469             this.restrictHeight();
14470             return;
14471         }
14472         
14473         this.collapse();
14474     },
14475
14476     /**
14477      * Returns true if the dropdown list is expanded, else false.
14478      */
14479     isExpanded : function(){
14480         return this.list.isVisible();
14481     },
14482
14483     /**
14484      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14485      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14486      * @param {String} value The data value of the item to select
14487      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14488      * selected item if it is not currently in view (defaults to true)
14489      * @return {Boolean} True if the value matched an item in the list, else false
14490      */
14491     selectByValue : function(v, scrollIntoView){
14492         if(v !== undefined && v !== null){
14493             var r = this.findRecord(this.valueField || this.displayField, v);
14494             if(r){
14495                 this.select(this.store.indexOf(r), scrollIntoView);
14496                 return true;
14497             }
14498         }
14499         return false;
14500     },
14501
14502     /**
14503      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14504      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14505      * @param {Number} index The zero-based index of the list item to select
14506      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14507      * selected item if it is not currently in view (defaults to true)
14508      */
14509     select : function(index, scrollIntoView){
14510         this.selectedIndex = index;
14511         this.view.select(index);
14512         if(scrollIntoView !== false){
14513             var el = this.view.getNode(index);
14514             /*
14515              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14516              */
14517             if(el){
14518                 this.list.scrollChildIntoView(el, false);
14519             }
14520         }
14521     },
14522
14523     // private
14524     selectNext : function(){
14525         var ct = this.store.getCount();
14526         if(ct > 0){
14527             if(this.selectedIndex == -1){
14528                 this.select(0);
14529             }else if(this.selectedIndex < ct-1){
14530                 this.select(this.selectedIndex+1);
14531             }
14532         }
14533     },
14534
14535     // private
14536     selectPrev : function(){
14537         var ct = this.store.getCount();
14538         if(ct > 0){
14539             if(this.selectedIndex == -1){
14540                 this.select(0);
14541             }else if(this.selectedIndex != 0){
14542                 this.select(this.selectedIndex-1);
14543             }
14544         }
14545     },
14546
14547     // private
14548     onKeyUp : function(e){
14549         if(this.editable !== false && !e.isSpecialKey()){
14550             this.lastKey = e.getKey();
14551             this.dqTask.delay(this.queryDelay);
14552         }
14553     },
14554
14555     // private
14556     validateBlur : function(){
14557         return !this.list || !this.list.isVisible();   
14558     },
14559
14560     // private
14561     initQuery : function(){
14562         
14563         var v = this.getRawValue();
14564         
14565         if(this.tickable && this.editable){
14566             v = this.tickableInputEl().getValue();
14567         }
14568         
14569         this.doQuery(v);
14570     },
14571
14572     // private
14573     doForce : function(){
14574         if(this.inputEl().dom.value.length > 0){
14575             this.inputEl().dom.value =
14576                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14577              
14578         }
14579     },
14580
14581     /**
14582      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14583      * query allowing the query action to be canceled if needed.
14584      * @param {String} query The SQL query to execute
14585      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14586      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14587      * saved in the current store (defaults to false)
14588      */
14589     doQuery : function(q, forceAll){
14590         
14591         if(q === undefined || q === null){
14592             q = '';
14593         }
14594         var qe = {
14595             query: q,
14596             forceAll: forceAll,
14597             combo: this,
14598             cancel:false
14599         };
14600         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14601             return false;
14602         }
14603         q = qe.query;
14604         
14605         forceAll = qe.forceAll;
14606         if(forceAll === true || (q.length >= this.minChars)){
14607             
14608             this.hasQuery = true;
14609             
14610             if(this.lastQuery != q || this.alwaysQuery){
14611                 this.lastQuery = q;
14612                 if(this.mode == 'local'){
14613                     this.selectedIndex = -1;
14614                     if(forceAll){
14615                         this.store.clearFilter();
14616                     }else{
14617                         
14618                         if(this.specialFilter){
14619                             this.fireEvent('specialfilter', this);
14620                             this.onLoad();
14621                             return;
14622                         }
14623                         
14624                         this.store.filter(this.displayField, q);
14625                     }
14626                     
14627                     this.store.fireEvent("datachanged", this.store);
14628                     
14629                     this.onLoad();
14630                     
14631                     
14632                 }else{
14633                     
14634                     this.store.baseParams[this.queryParam] = q;
14635                     
14636                     var options = {params : this.getParams(q)};
14637                     
14638                     if(this.loadNext){
14639                         options.add = true;
14640                         options.params.start = this.page * this.pageSize;
14641                     }
14642                     
14643                     this.store.load(options);
14644                     
14645                     /*
14646                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14647                      *  we should expand the list on onLoad
14648                      *  so command out it
14649                      */
14650 //                    this.expand();
14651                 }
14652             }else{
14653                 this.selectedIndex = -1;
14654                 this.onLoad();   
14655             }
14656         }
14657         
14658         this.loadNext = false;
14659     },
14660     
14661     // private
14662     getParams : function(q){
14663         var p = {};
14664         //p[this.queryParam] = q;
14665         
14666         if(this.pageSize){
14667             p.start = 0;
14668             p.limit = this.pageSize;
14669         }
14670         return p;
14671     },
14672
14673     /**
14674      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14675      */
14676     collapse : function(){
14677         if(!this.isExpanded()){
14678             return;
14679         }
14680         
14681         this.list.hide();
14682         
14683         this.hasFocus = false;
14684         
14685         if(this.tickable){
14686             this.okBtn.hide();
14687             this.cancelBtn.hide();
14688             this.trigger.show();
14689             
14690             if(this.editable){
14691                 this.tickableInputEl().dom.value = '';
14692                 this.tickableInputEl().blur();
14693             }
14694             
14695         }
14696         
14697         Roo.get(document).un('mousedown', this.collapseIf, this);
14698         Roo.get(document).un('mousewheel', this.collapseIf, this);
14699         if (!this.editable) {
14700             Roo.get(document).un('keydown', this.listKeyPress, this);
14701         }
14702         this.fireEvent('collapse', this);
14703         
14704         this.validate();
14705     },
14706
14707     // private
14708     collapseIf : function(e){
14709         var in_combo  = e.within(this.el);
14710         var in_list =  e.within(this.list);
14711         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14712         
14713         if (in_combo || in_list || is_list) {
14714             //e.stopPropagation();
14715             return;
14716         }
14717         
14718         if(this.tickable){
14719             this.onTickableFooterButtonClick(e, false, false);
14720         }
14721
14722         this.collapse();
14723         
14724     },
14725
14726     /**
14727      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14728      */
14729     expand : function(){
14730        
14731         if(this.isExpanded() || !this.hasFocus){
14732             return;
14733         }
14734         
14735         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14736         this.list.setWidth(lw);
14737         
14738         Roo.log('expand');
14739         
14740         this.list.show();
14741         
14742         this.restrictHeight();
14743         
14744         if(this.tickable){
14745             
14746             this.tickItems = Roo.apply([], this.item);
14747             
14748             this.okBtn.show();
14749             this.cancelBtn.show();
14750             this.trigger.hide();
14751             
14752             if(this.editable){
14753                 this.tickableInputEl().focus();
14754             }
14755             
14756         }
14757         
14758         Roo.get(document).on('mousedown', this.collapseIf, this);
14759         Roo.get(document).on('mousewheel', this.collapseIf, this);
14760         if (!this.editable) {
14761             Roo.get(document).on('keydown', this.listKeyPress, this);
14762         }
14763         
14764         this.fireEvent('expand', this);
14765     },
14766
14767     // private
14768     // Implements the default empty TriggerField.onTriggerClick function
14769     onTriggerClick : function(e)
14770     {
14771         Roo.log('trigger click');
14772         
14773         if(this.disabled || !this.triggerList){
14774             return;
14775         }
14776         
14777         this.page = 0;
14778         this.loadNext = false;
14779         
14780         if(this.isExpanded()){
14781             this.collapse();
14782             if (!this.blockFocus) {
14783                 this.inputEl().focus();
14784             }
14785             
14786         }else {
14787             this.hasFocus = true;
14788             if(this.triggerAction == 'all') {
14789                 this.doQuery(this.allQuery, true);
14790             } else {
14791                 this.doQuery(this.getRawValue());
14792             }
14793             if (!this.blockFocus) {
14794                 this.inputEl().focus();
14795             }
14796         }
14797     },
14798     
14799     onTickableTriggerClick : function(e)
14800     {
14801         if(this.disabled){
14802             return;
14803         }
14804         
14805         this.page = 0;
14806         this.loadNext = false;
14807         this.hasFocus = true;
14808         
14809         if(this.triggerAction == 'all') {
14810             this.doQuery(this.allQuery, true);
14811         } else {
14812             this.doQuery(this.getRawValue());
14813         }
14814     },
14815     
14816     onSearchFieldClick : function(e)
14817     {
14818         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14819             this.onTickableFooterButtonClick(e, false, false);
14820             return;
14821         }
14822         
14823         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14824             return;
14825         }
14826         
14827         this.page = 0;
14828         this.loadNext = false;
14829         this.hasFocus = true;
14830         
14831         if(this.triggerAction == 'all') {
14832             this.doQuery(this.allQuery, true);
14833         } else {
14834             this.doQuery(this.getRawValue());
14835         }
14836     },
14837     
14838     listKeyPress : function(e)
14839     {
14840         //Roo.log('listkeypress');
14841         // scroll to first matching element based on key pres..
14842         if (e.isSpecialKey()) {
14843             return false;
14844         }
14845         var k = String.fromCharCode(e.getKey()).toUpperCase();
14846         //Roo.log(k);
14847         var match  = false;
14848         var csel = this.view.getSelectedNodes();
14849         var cselitem = false;
14850         if (csel.length) {
14851             var ix = this.view.indexOf(csel[0]);
14852             cselitem  = this.store.getAt(ix);
14853             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14854                 cselitem = false;
14855             }
14856             
14857         }
14858         
14859         this.store.each(function(v) { 
14860             if (cselitem) {
14861                 // start at existing selection.
14862                 if (cselitem.id == v.id) {
14863                     cselitem = false;
14864                 }
14865                 return true;
14866             }
14867                 
14868             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14869                 match = this.store.indexOf(v);
14870                 return false;
14871             }
14872             return true;
14873         }, this);
14874         
14875         if (match === false) {
14876             return true; // no more action?
14877         }
14878         // scroll to?
14879         this.view.select(match);
14880         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14881         sn.scrollIntoView(sn.dom.parentNode, false);
14882     },
14883     
14884     onViewScroll : function(e, t){
14885         
14886         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){
14887             return;
14888         }
14889         
14890         this.hasQuery = true;
14891         
14892         this.loading = this.list.select('.loading', true).first();
14893         
14894         if(this.loading === null){
14895             this.list.createChild({
14896                 tag: 'div',
14897                 cls: 'loading roo-select2-more-results roo-select2-active',
14898                 html: 'Loading more results...'
14899             });
14900             
14901             this.loading = this.list.select('.loading', true).first();
14902             
14903             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14904             
14905             this.loading.hide();
14906         }
14907         
14908         this.loading.show();
14909         
14910         var _combo = this;
14911         
14912         this.page++;
14913         this.loadNext = true;
14914         
14915         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14916         
14917         return;
14918     },
14919     
14920     addItem : function(o)
14921     {   
14922         var dv = ''; // display value
14923         
14924         if (this.displayField) {
14925             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14926         } else {
14927             // this is an error condition!!!
14928             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14929         }
14930         
14931         if(!dv.length){
14932             return;
14933         }
14934         
14935         var choice = this.choices.createChild({
14936             tag: 'li',
14937             cls: 'roo-select2-search-choice',
14938             cn: [
14939                 {
14940                     tag: 'div',
14941                     html: dv
14942                 },
14943                 {
14944                     tag: 'a',
14945                     href: '#',
14946                     cls: 'roo-select2-search-choice-close fa fa-times',
14947                     tabindex: '-1'
14948                 }
14949             ]
14950             
14951         }, this.searchField);
14952         
14953         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14954         
14955         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14956         
14957         this.item.push(o);
14958         
14959         this.lastData = o;
14960         
14961         this.syncValue();
14962         
14963         this.inputEl().dom.value = '';
14964         
14965         this.validate();
14966     },
14967     
14968     onRemoveItem : function(e, _self, o)
14969     {
14970         e.preventDefault();
14971         
14972         this.lastItem = Roo.apply([], this.item);
14973         
14974         var index = this.item.indexOf(o.data) * 1;
14975         
14976         if( index < 0){
14977             Roo.log('not this item?!');
14978             return;
14979         }
14980         
14981         this.item.splice(index, 1);
14982         o.item.remove();
14983         
14984         this.syncValue();
14985         
14986         this.fireEvent('remove', this, e);
14987         
14988         this.validate();
14989         
14990     },
14991     
14992     syncValue : function()
14993     {
14994         if(!this.item.length){
14995             this.clearValue();
14996             return;
14997         }
14998             
14999         var value = [];
15000         var _this = this;
15001         Roo.each(this.item, function(i){
15002             if(_this.valueField){
15003                 value.push(i[_this.valueField]);
15004                 return;
15005             }
15006
15007             value.push(i);
15008         });
15009
15010         this.value = value.join(',');
15011
15012         if(this.hiddenField){
15013             this.hiddenField.dom.value = this.value;
15014         }
15015         
15016         this.store.fireEvent("datachanged", this.store);
15017         
15018         this.validate();
15019     },
15020     
15021     clearItem : function()
15022     {
15023         if(!this.multiple){
15024             return;
15025         }
15026         
15027         this.item = [];
15028         
15029         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15030            c.remove();
15031         });
15032         
15033         this.syncValue();
15034         
15035         this.validate();
15036         
15037         if(this.tickable && !Roo.isTouch){
15038             this.view.refresh();
15039         }
15040     },
15041     
15042     inputEl: function ()
15043     {
15044         if(Roo.isIOS && this.useNativeIOS){
15045             return this.el.select('select.roo-ios-select', true).first();
15046         }
15047         
15048         if(Roo.isTouch && this.mobileTouchView){
15049             return this.el.select('input.form-control',true).first();
15050         }
15051         
15052         if(this.tickable){
15053             return this.searchField;
15054         }
15055         
15056         return this.el.select('input.form-control',true).first();
15057     },
15058     
15059     onTickableFooterButtonClick : function(e, btn, el)
15060     {
15061         e.preventDefault();
15062         
15063         this.lastItem = Roo.apply([], this.item);
15064         
15065         if(btn && btn.name == 'cancel'){
15066             this.tickItems = Roo.apply([], this.item);
15067             this.collapse();
15068             return;
15069         }
15070         
15071         this.clearItem();
15072         
15073         var _this = this;
15074         
15075         Roo.each(this.tickItems, function(o){
15076             _this.addItem(o);
15077         });
15078         
15079         this.collapse();
15080         
15081     },
15082     
15083     validate : function()
15084     {
15085         if(this.getVisibilityEl().hasClass('hidden')){
15086             return true;
15087         }
15088         
15089         var v = this.getRawValue();
15090         
15091         if(this.multiple){
15092             v = this.getValue();
15093         }
15094         
15095         if(this.disabled || this.allowBlank || v.length){
15096             this.markValid();
15097             return true;
15098         }
15099         
15100         this.markInvalid();
15101         return false;
15102     },
15103     
15104     tickableInputEl : function()
15105     {
15106         if(!this.tickable || !this.editable){
15107             return this.inputEl();
15108         }
15109         
15110         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15111     },
15112     
15113     
15114     getAutoCreateTouchView : function()
15115     {
15116         var id = Roo.id();
15117         
15118         var cfg = {
15119             cls: 'form-group' //input-group
15120         };
15121         
15122         var input =  {
15123             tag: 'input',
15124             id : id,
15125             type : this.inputType,
15126             cls : 'form-control x-combo-noedit',
15127             autocomplete: 'new-password',
15128             placeholder : this.placeholder || '',
15129             readonly : true
15130         };
15131         
15132         if (this.name) {
15133             input.name = this.name;
15134         }
15135         
15136         if (this.size) {
15137             input.cls += ' input-' + this.size;
15138         }
15139         
15140         if (this.disabled) {
15141             input.disabled = true;
15142         }
15143         
15144         var inputblock = {
15145             cls : '',
15146             cn : [
15147                 input
15148             ]
15149         };
15150         
15151         if(this.before){
15152             inputblock.cls += ' input-group';
15153             
15154             inputblock.cn.unshift({
15155                 tag :'span',
15156                 cls : 'input-group-addon input-group-prepend input-group-text',
15157                 html : this.before
15158             });
15159         }
15160         
15161         if(this.removable && !this.multiple){
15162             inputblock.cls += ' roo-removable';
15163             
15164             inputblock.cn.push({
15165                 tag: 'button',
15166                 html : 'x',
15167                 cls : 'roo-combo-removable-btn close'
15168             });
15169         }
15170
15171         if(this.hasFeedback && !this.allowBlank){
15172             
15173             inputblock.cls += ' has-feedback';
15174             
15175             inputblock.cn.push({
15176                 tag: 'span',
15177                 cls: 'glyphicon form-control-feedback'
15178             });
15179             
15180         }
15181         
15182         if (this.after) {
15183             
15184             inputblock.cls += (this.before) ? '' : ' input-group';
15185             
15186             inputblock.cn.push({
15187                 tag :'span',
15188                 cls : 'input-group-addon input-group-append input-group-text',
15189                 html : this.after
15190             });
15191         }
15192
15193         
15194         var ibwrap = inputblock;
15195         
15196         if(this.multiple){
15197             ibwrap = {
15198                 tag: 'ul',
15199                 cls: 'roo-select2-choices',
15200                 cn:[
15201                     {
15202                         tag: 'li',
15203                         cls: 'roo-select2-search-field',
15204                         cn: [
15205
15206                             inputblock
15207                         ]
15208                     }
15209                 ]
15210             };
15211         
15212             
15213         }
15214         
15215         var combobox = {
15216             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15217             cn: [
15218                 {
15219                     tag: 'input',
15220                     type : 'hidden',
15221                     cls: 'form-hidden-field'
15222                 },
15223                 ibwrap
15224             ]
15225         };
15226         
15227         if(!this.multiple && this.showToggleBtn){
15228             
15229             var caret = {
15230                         tag: 'span',
15231                         cls: 'caret'
15232             };
15233             
15234             if (this.caret != false) {
15235                 caret = {
15236                      tag: 'i',
15237                      cls: 'fa fa-' + this.caret
15238                 };
15239                 
15240             }
15241             
15242             combobox.cn.push({
15243                 tag :'span',
15244                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15245                 cn : [
15246                     caret,
15247                     {
15248                         tag: 'span',
15249                         cls: 'combobox-clear',
15250                         cn  : [
15251                             {
15252                                 tag : 'i',
15253                                 cls: 'icon-remove'
15254                             }
15255                         ]
15256                     }
15257                 ]
15258
15259             })
15260         }
15261         
15262         if(this.multiple){
15263             combobox.cls += ' roo-select2-container-multi';
15264         }
15265         
15266         var align = this.labelAlign || this.parentLabelAlign();
15267         
15268         if (align ==='left' && this.fieldLabel.length) {
15269
15270             cfg.cn = [
15271                 {
15272                    tag : 'i',
15273                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15274                    tooltip : 'This field is required'
15275                 },
15276                 {
15277                     tag: 'label',
15278                     cls : 'control-label col-form-label',
15279                     html : this.fieldLabel
15280
15281                 },
15282                 {
15283                     cls : '', 
15284                     cn: [
15285                         combobox
15286                     ]
15287                 }
15288             ];
15289             
15290             var labelCfg = cfg.cn[1];
15291             var contentCfg = cfg.cn[2];
15292             
15293
15294             if(this.indicatorpos == 'right'){
15295                 cfg.cn = [
15296                     {
15297                         tag: 'label',
15298                         'for' :  id,
15299                         cls : 'control-label col-form-label',
15300                         cn : [
15301                             {
15302                                 tag : 'span',
15303                                 html : this.fieldLabel
15304                             },
15305                             {
15306                                 tag : 'i',
15307                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15308                                 tooltip : 'This field is required'
15309                             }
15310                         ]
15311                     },
15312                     {
15313                         cls : "",
15314                         cn: [
15315                             combobox
15316                         ]
15317                     }
15318
15319                 ];
15320                 
15321                 labelCfg = cfg.cn[0];
15322                 contentCfg = cfg.cn[1];
15323             }
15324             
15325            
15326             
15327             if(this.labelWidth > 12){
15328                 labelCfg.style = "width: " + this.labelWidth + 'px';
15329             }
15330             
15331             if(this.labelWidth < 13 && this.labelmd == 0){
15332                 this.labelmd = this.labelWidth;
15333             }
15334             
15335             if(this.labellg > 0){
15336                 labelCfg.cls += ' col-lg-' + this.labellg;
15337                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15338             }
15339             
15340             if(this.labelmd > 0){
15341                 labelCfg.cls += ' col-md-' + this.labelmd;
15342                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15343             }
15344             
15345             if(this.labelsm > 0){
15346                 labelCfg.cls += ' col-sm-' + this.labelsm;
15347                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15348             }
15349             
15350             if(this.labelxs > 0){
15351                 labelCfg.cls += ' col-xs-' + this.labelxs;
15352                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15353             }
15354                 
15355                 
15356         } else if ( this.fieldLabel.length) {
15357             cfg.cn = [
15358                 {
15359                    tag : 'i',
15360                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15361                    tooltip : 'This field is required'
15362                 },
15363                 {
15364                     tag: 'label',
15365                     cls : 'control-label',
15366                     html : this.fieldLabel
15367
15368                 },
15369                 {
15370                     cls : '', 
15371                     cn: [
15372                         combobox
15373                     ]
15374                 }
15375             ];
15376             
15377             if(this.indicatorpos == 'right'){
15378                 cfg.cn = [
15379                     {
15380                         tag: 'label',
15381                         cls : 'control-label',
15382                         html : this.fieldLabel,
15383                         cn : [
15384                             {
15385                                tag : 'i',
15386                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15387                                tooltip : 'This field is required'
15388                             }
15389                         ]
15390                     },
15391                     {
15392                         cls : '', 
15393                         cn: [
15394                             combobox
15395                         ]
15396                     }
15397                 ];
15398             }
15399         } else {
15400             cfg.cn = combobox;    
15401         }
15402         
15403         
15404         var settings = this;
15405         
15406         ['xs','sm','md','lg'].map(function(size){
15407             if (settings[size]) {
15408                 cfg.cls += ' col-' + size + '-' + settings[size];
15409             }
15410         });
15411         
15412         return cfg;
15413     },
15414     
15415     initTouchView : function()
15416     {
15417         this.renderTouchView();
15418         
15419         this.touchViewEl.on('scroll', function(){
15420             this.el.dom.scrollTop = 0;
15421         }, this);
15422         
15423         this.originalValue = this.getValue();
15424         
15425         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15426         
15427         this.inputEl().on("click", this.showTouchView, this);
15428         if (this.triggerEl) {
15429             this.triggerEl.on("click", this.showTouchView, this);
15430         }
15431         
15432         
15433         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15434         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15435         
15436         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15437         
15438         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15439         this.store.on('load', this.onTouchViewLoad, this);
15440         this.store.on('loadexception', this.onTouchViewLoadException, this);
15441         
15442         if(this.hiddenName){
15443             
15444             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15445             
15446             this.hiddenField.dom.value =
15447                 this.hiddenValue !== undefined ? this.hiddenValue :
15448                 this.value !== undefined ? this.value : '';
15449         
15450             this.el.dom.removeAttribute('name');
15451             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15452         }
15453         
15454         if(this.multiple){
15455             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15456             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15457         }
15458         
15459         if(this.removable && !this.multiple){
15460             var close = this.closeTriggerEl();
15461             if(close){
15462                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15463                 close.on('click', this.removeBtnClick, this, close);
15464             }
15465         }
15466         /*
15467          * fix the bug in Safari iOS8
15468          */
15469         this.inputEl().on("focus", function(e){
15470             document.activeElement.blur();
15471         }, this);
15472         
15473         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15474         
15475         return;
15476         
15477         
15478     },
15479     
15480     renderTouchView : function()
15481     {
15482         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15483         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15484         
15485         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15486         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15487         
15488         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15489         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15490         this.touchViewBodyEl.setStyle('overflow', 'auto');
15491         
15492         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15493         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15494         
15495         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15496         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15497         
15498     },
15499     
15500     showTouchView : function()
15501     {
15502         if(this.disabled){
15503             return;
15504         }
15505         
15506         this.touchViewHeaderEl.hide();
15507
15508         if(this.modalTitle.length){
15509             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15510             this.touchViewHeaderEl.show();
15511         }
15512
15513         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15514         this.touchViewEl.show();
15515
15516         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15517         
15518         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15519         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15520
15521         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15522
15523         if(this.modalTitle.length){
15524             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15525         }
15526         
15527         this.touchViewBodyEl.setHeight(bodyHeight);
15528
15529         if(this.animate){
15530             var _this = this;
15531             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15532         }else{
15533             this.touchViewEl.addClass('in');
15534         }
15535         
15536         if(this._touchViewMask){
15537             Roo.get(document.body).addClass("x-body-masked");
15538             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15539             this._touchViewMask.setStyle('z-index', 10000);
15540             this._touchViewMask.addClass('show');
15541         }
15542         
15543         this.doTouchViewQuery();
15544         
15545     },
15546     
15547     hideTouchView : function()
15548     {
15549         this.touchViewEl.removeClass('in');
15550
15551         if(this.animate){
15552             var _this = this;
15553             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15554         }else{
15555             this.touchViewEl.setStyle('display', 'none');
15556         }
15557         
15558         if(this._touchViewMask){
15559             this._touchViewMask.removeClass('show');
15560             Roo.get(document.body).removeClass("x-body-masked");
15561         }
15562     },
15563     
15564     setTouchViewValue : function()
15565     {
15566         if(this.multiple){
15567             this.clearItem();
15568         
15569             var _this = this;
15570
15571             Roo.each(this.tickItems, function(o){
15572                 this.addItem(o);
15573             }, this);
15574         }
15575         
15576         this.hideTouchView();
15577     },
15578     
15579     doTouchViewQuery : function()
15580     {
15581         var qe = {
15582             query: '',
15583             forceAll: true,
15584             combo: this,
15585             cancel:false
15586         };
15587         
15588         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15589             return false;
15590         }
15591         
15592         if(!this.alwaysQuery || this.mode == 'local'){
15593             this.onTouchViewLoad();
15594             return;
15595         }
15596         
15597         this.store.load();
15598     },
15599     
15600     onTouchViewBeforeLoad : function(combo,opts)
15601     {
15602         return;
15603     },
15604
15605     // private
15606     onTouchViewLoad : function()
15607     {
15608         if(this.store.getCount() < 1){
15609             this.onTouchViewEmptyResults();
15610             return;
15611         }
15612         
15613         this.clearTouchView();
15614         
15615         var rawValue = this.getRawValue();
15616         
15617         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15618         
15619         this.tickItems = [];
15620         
15621         this.store.data.each(function(d, rowIndex){
15622             var row = this.touchViewListGroup.createChild(template);
15623             
15624             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15625                 row.addClass(d.data.cls);
15626             }
15627             
15628             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15629                 var cfg = {
15630                     data : d.data,
15631                     html : d.data[this.displayField]
15632                 };
15633                 
15634                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15635                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15636                 }
15637             }
15638             row.removeClass('selected');
15639             if(!this.multiple && this.valueField &&
15640                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15641             {
15642                 // radio buttons..
15643                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15644                 row.addClass('selected');
15645             }
15646             
15647             if(this.multiple && this.valueField &&
15648                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15649             {
15650                 
15651                 // checkboxes...
15652                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15653                 this.tickItems.push(d.data);
15654             }
15655             
15656             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15657             
15658         }, this);
15659         
15660         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15661         
15662         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15663
15664         if(this.modalTitle.length){
15665             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15666         }
15667
15668         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15669         
15670         if(this.mobile_restrict_height && listHeight < bodyHeight){
15671             this.touchViewBodyEl.setHeight(listHeight);
15672         }
15673         
15674         var _this = this;
15675         
15676         if(firstChecked && listHeight > bodyHeight){
15677             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15678         }
15679         
15680     },
15681     
15682     onTouchViewLoadException : function()
15683     {
15684         this.hideTouchView();
15685     },
15686     
15687     onTouchViewEmptyResults : function()
15688     {
15689         this.clearTouchView();
15690         
15691         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15692         
15693         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15694         
15695     },
15696     
15697     clearTouchView : function()
15698     {
15699         this.touchViewListGroup.dom.innerHTML = '';
15700     },
15701     
15702     onTouchViewClick : function(e, el, o)
15703     {
15704         e.preventDefault();
15705         
15706         var row = o.row;
15707         var rowIndex = o.rowIndex;
15708         
15709         var r = this.store.getAt(rowIndex);
15710         
15711         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15712             
15713             if(!this.multiple){
15714                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15715                     c.dom.removeAttribute('checked');
15716                 }, this);
15717
15718                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15719
15720                 this.setFromData(r.data);
15721
15722                 var close = this.closeTriggerEl();
15723
15724                 if(close){
15725                     close.show();
15726                 }
15727
15728                 this.hideTouchView();
15729
15730                 this.fireEvent('select', this, r, rowIndex);
15731
15732                 return;
15733             }
15734
15735             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15736                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15737                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15738                 return;
15739             }
15740
15741             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15742             this.addItem(r.data);
15743             this.tickItems.push(r.data);
15744         }
15745     },
15746     
15747     getAutoCreateNativeIOS : function()
15748     {
15749         var cfg = {
15750             cls: 'form-group' //input-group,
15751         };
15752         
15753         var combobox =  {
15754             tag: 'select',
15755             cls : 'roo-ios-select'
15756         };
15757         
15758         if (this.name) {
15759             combobox.name = this.name;
15760         }
15761         
15762         if (this.disabled) {
15763             combobox.disabled = true;
15764         }
15765         
15766         var settings = this;
15767         
15768         ['xs','sm','md','lg'].map(function(size){
15769             if (settings[size]) {
15770                 cfg.cls += ' col-' + size + '-' + settings[size];
15771             }
15772         });
15773         
15774         cfg.cn = combobox;
15775         
15776         return cfg;
15777         
15778     },
15779     
15780     initIOSView : function()
15781     {
15782         this.store.on('load', this.onIOSViewLoad, this);
15783         
15784         return;
15785     },
15786     
15787     onIOSViewLoad : function()
15788     {
15789         if(this.store.getCount() < 1){
15790             return;
15791         }
15792         
15793         this.clearIOSView();
15794         
15795         if(this.allowBlank) {
15796             
15797             var default_text = '-- SELECT --';
15798             
15799             if(this.placeholder.length){
15800                 default_text = this.placeholder;
15801             }
15802             
15803             if(this.emptyTitle.length){
15804                 default_text += ' - ' + this.emptyTitle + ' -';
15805             }
15806             
15807             var opt = this.inputEl().createChild({
15808                 tag: 'option',
15809                 value : 0,
15810                 html : default_text
15811             });
15812             
15813             var o = {};
15814             o[this.valueField] = 0;
15815             o[this.displayField] = default_text;
15816             
15817             this.ios_options.push({
15818                 data : o,
15819                 el : opt
15820             });
15821             
15822         }
15823         
15824         this.store.data.each(function(d, rowIndex){
15825             
15826             var html = '';
15827             
15828             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15829                 html = d.data[this.displayField];
15830             }
15831             
15832             var value = '';
15833             
15834             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15835                 value = d.data[this.valueField];
15836             }
15837             
15838             var option = {
15839                 tag: 'option',
15840                 value : value,
15841                 html : html
15842             };
15843             
15844             if(this.value == d.data[this.valueField]){
15845                 option['selected'] = true;
15846             }
15847             
15848             var opt = this.inputEl().createChild(option);
15849             
15850             this.ios_options.push({
15851                 data : d.data,
15852                 el : opt
15853             });
15854             
15855         }, this);
15856         
15857         this.inputEl().on('change', function(){
15858            this.fireEvent('select', this);
15859         }, this);
15860         
15861     },
15862     
15863     clearIOSView: function()
15864     {
15865         this.inputEl().dom.innerHTML = '';
15866         
15867         this.ios_options = [];
15868     },
15869     
15870     setIOSValue: function(v)
15871     {
15872         this.value = v;
15873         
15874         if(!this.ios_options){
15875             return;
15876         }
15877         
15878         Roo.each(this.ios_options, function(opts){
15879            
15880            opts.el.dom.removeAttribute('selected');
15881            
15882            if(opts.data[this.valueField] != v){
15883                return;
15884            }
15885            
15886            opts.el.dom.setAttribute('selected', true);
15887            
15888         }, this);
15889     }
15890
15891     /** 
15892     * @cfg {Boolean} grow 
15893     * @hide 
15894     */
15895     /** 
15896     * @cfg {Number} growMin 
15897     * @hide 
15898     */
15899     /** 
15900     * @cfg {Number} growMax 
15901     * @hide 
15902     */
15903     /**
15904      * @hide
15905      * @method autoSize
15906      */
15907 });
15908
15909 Roo.apply(Roo.bootstrap.ComboBox,  {
15910     
15911     header : {
15912         tag: 'div',
15913         cls: 'modal-header',
15914         cn: [
15915             {
15916                 tag: 'h4',
15917                 cls: 'modal-title'
15918             }
15919         ]
15920     },
15921     
15922     body : {
15923         tag: 'div',
15924         cls: 'modal-body',
15925         cn: [
15926             {
15927                 tag: 'ul',
15928                 cls: 'list-group'
15929             }
15930         ]
15931     },
15932     
15933     listItemRadio : {
15934         tag: 'li',
15935         cls: 'list-group-item',
15936         cn: [
15937             {
15938                 tag: 'span',
15939                 cls: 'roo-combobox-list-group-item-value'
15940             },
15941             {
15942                 tag: 'div',
15943                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15944                 cn: [
15945                     {
15946                         tag: 'input',
15947                         type: 'radio'
15948                     },
15949                     {
15950                         tag: 'label'
15951                     }
15952                 ]
15953             }
15954         ]
15955     },
15956     
15957     listItemCheckbox : {
15958         tag: 'li',
15959         cls: 'list-group-item',
15960         cn: [
15961             {
15962                 tag: 'span',
15963                 cls: 'roo-combobox-list-group-item-value'
15964             },
15965             {
15966                 tag: 'div',
15967                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15968                 cn: [
15969                     {
15970                         tag: 'input',
15971                         type: 'checkbox'
15972                     },
15973                     {
15974                         tag: 'label'
15975                     }
15976                 ]
15977             }
15978         ]
15979     },
15980     
15981     emptyResult : {
15982         tag: 'div',
15983         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15984     },
15985     
15986     footer : {
15987         tag: 'div',
15988         cls: 'modal-footer',
15989         cn: [
15990             {
15991                 tag: 'div',
15992                 cls: 'row',
15993                 cn: [
15994                     {
15995                         tag: 'div',
15996                         cls: 'col-xs-6 text-left',
15997                         cn: {
15998                             tag: 'button',
15999                             cls: 'btn btn-danger roo-touch-view-cancel',
16000                             html: 'Cancel'
16001                         }
16002                     },
16003                     {
16004                         tag: 'div',
16005                         cls: 'col-xs-6 text-right',
16006                         cn: {
16007                             tag: 'button',
16008                             cls: 'btn btn-success roo-touch-view-ok',
16009                             html: 'OK'
16010                         }
16011                     }
16012                 ]
16013             }
16014         ]
16015         
16016     }
16017 });
16018
16019 Roo.apply(Roo.bootstrap.ComboBox,  {
16020     
16021     touchViewTemplate : {
16022         tag: 'div',
16023         cls: 'modal fade roo-combobox-touch-view',
16024         cn: [
16025             {
16026                 tag: 'div',
16027                 cls: 'modal-dialog',
16028                 style : 'position:fixed', // we have to fix position....
16029                 cn: [
16030                     {
16031                         tag: 'div',
16032                         cls: 'modal-content',
16033                         cn: [
16034                             Roo.bootstrap.ComboBox.header,
16035                             Roo.bootstrap.ComboBox.body,
16036                             Roo.bootstrap.ComboBox.footer
16037                         ]
16038                     }
16039                 ]
16040             }
16041         ]
16042     }
16043 });/*
16044  * Based on:
16045  * Ext JS Library 1.1.1
16046  * Copyright(c) 2006-2007, Ext JS, LLC.
16047  *
16048  * Originally Released Under LGPL - original licence link has changed is not relivant.
16049  *
16050  * Fork - LGPL
16051  * <script type="text/javascript">
16052  */
16053
16054 /**
16055  * @class Roo.View
16056  * @extends Roo.util.Observable
16057  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16058  * This class also supports single and multi selection modes. <br>
16059  * Create a data model bound view:
16060  <pre><code>
16061  var store = new Roo.data.Store(...);
16062
16063  var view = new Roo.View({
16064     el : "my-element",
16065     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16066  
16067     singleSelect: true,
16068     selectedClass: "ydataview-selected",
16069     store: store
16070  });
16071
16072  // listen for node click?
16073  view.on("click", function(vw, index, node, e){
16074  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16075  });
16076
16077  // load XML data
16078  dataModel.load("foobar.xml");
16079  </code></pre>
16080  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16081  * <br><br>
16082  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16083  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16084  * 
16085  * Note: old style constructor is still suported (container, template, config)
16086  * 
16087  * @constructor
16088  * Create a new View
16089  * @param {Object} config The config object
16090  * 
16091  */
16092 Roo.View = function(config, depreciated_tpl, depreciated_config){
16093     
16094     this.parent = false;
16095     
16096     if (typeof(depreciated_tpl) == 'undefined') {
16097         // new way.. - universal constructor.
16098         Roo.apply(this, config);
16099         this.el  = Roo.get(this.el);
16100     } else {
16101         // old format..
16102         this.el  = Roo.get(config);
16103         this.tpl = depreciated_tpl;
16104         Roo.apply(this, depreciated_config);
16105     }
16106     this.wrapEl  = this.el.wrap().wrap();
16107     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16108     
16109     
16110     if(typeof(this.tpl) == "string"){
16111         this.tpl = new Roo.Template(this.tpl);
16112     } else {
16113         // support xtype ctors..
16114         this.tpl = new Roo.factory(this.tpl, Roo);
16115     }
16116     
16117     
16118     this.tpl.compile();
16119     
16120     /** @private */
16121     this.addEvents({
16122         /**
16123          * @event beforeclick
16124          * Fires before a click is processed. Returns false to cancel the default action.
16125          * @param {Roo.View} this
16126          * @param {Number} index The index of the target node
16127          * @param {HTMLElement} node The target node
16128          * @param {Roo.EventObject} e The raw event object
16129          */
16130             "beforeclick" : true,
16131         /**
16132          * @event click
16133          * Fires when a template node is clicked.
16134          * @param {Roo.View} this
16135          * @param {Number} index The index of the target node
16136          * @param {HTMLElement} node The target node
16137          * @param {Roo.EventObject} e The raw event object
16138          */
16139             "click" : true,
16140         /**
16141          * @event dblclick
16142          * Fires when a template node is double clicked.
16143          * @param {Roo.View} this
16144          * @param {Number} index The index of the target node
16145          * @param {HTMLElement} node The target node
16146          * @param {Roo.EventObject} e The raw event object
16147          */
16148             "dblclick" : true,
16149         /**
16150          * @event contextmenu
16151          * Fires when a template node is right clicked.
16152          * @param {Roo.View} this
16153          * @param {Number} index The index of the target node
16154          * @param {HTMLElement} node The target node
16155          * @param {Roo.EventObject} e The raw event object
16156          */
16157             "contextmenu" : true,
16158         /**
16159          * @event selectionchange
16160          * Fires when the selected nodes change.
16161          * @param {Roo.View} this
16162          * @param {Array} selections Array of the selected nodes
16163          */
16164             "selectionchange" : true,
16165     
16166         /**
16167          * @event beforeselect
16168          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16169          * @param {Roo.View} this
16170          * @param {HTMLElement} node The node to be selected
16171          * @param {Array} selections Array of currently selected nodes
16172          */
16173             "beforeselect" : true,
16174         /**
16175          * @event preparedata
16176          * Fires on every row to render, to allow you to change the data.
16177          * @param {Roo.View} this
16178          * @param {Object} data to be rendered (change this)
16179          */
16180           "preparedata" : true
16181           
16182           
16183         });
16184
16185
16186
16187     this.el.on({
16188         "click": this.onClick,
16189         "dblclick": this.onDblClick,
16190         "contextmenu": this.onContextMenu,
16191         scope:this
16192     });
16193
16194     this.selections = [];
16195     this.nodes = [];
16196     this.cmp = new Roo.CompositeElementLite([]);
16197     if(this.store){
16198         this.store = Roo.factory(this.store, Roo.data);
16199         this.setStore(this.store, true);
16200     }
16201     
16202     if ( this.footer && this.footer.xtype) {
16203            
16204          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16205         
16206         this.footer.dataSource = this.store;
16207         this.footer.container = fctr;
16208         this.footer = Roo.factory(this.footer, Roo);
16209         fctr.insertFirst(this.el);
16210         
16211         // this is a bit insane - as the paging toolbar seems to detach the el..
16212 //        dom.parentNode.parentNode.parentNode
16213          // they get detached?
16214     }
16215     
16216     
16217     Roo.View.superclass.constructor.call(this);
16218     
16219     
16220 };
16221
16222 Roo.extend(Roo.View, Roo.util.Observable, {
16223     
16224      /**
16225      * @cfg {Roo.data.Store} store Data store to load data from.
16226      */
16227     store : false,
16228     
16229     /**
16230      * @cfg {String|Roo.Element} el The container element.
16231      */
16232     el : '',
16233     
16234     /**
16235      * @cfg {String|Roo.Template} tpl The template used by this View 
16236      */
16237     tpl : false,
16238     /**
16239      * @cfg {String} dataName the named area of the template to use as the data area
16240      *                          Works with domtemplates roo-name="name"
16241      */
16242     dataName: false,
16243     /**
16244      * @cfg {String} selectedClass The css class to add to selected nodes
16245      */
16246     selectedClass : "x-view-selected",
16247      /**
16248      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16249      */
16250     emptyText : "",
16251     
16252     /**
16253      * @cfg {String} text to display on mask (default Loading)
16254      */
16255     mask : false,
16256     /**
16257      * @cfg {Boolean} multiSelect Allow multiple selection
16258      */
16259     multiSelect : false,
16260     /**
16261      * @cfg {Boolean} singleSelect Allow single selection
16262      */
16263     singleSelect:  false,
16264     
16265     /**
16266      * @cfg {Boolean} toggleSelect - selecting 
16267      */
16268     toggleSelect : false,
16269     
16270     /**
16271      * @cfg {Boolean} tickable - selecting 
16272      */
16273     tickable : false,
16274     
16275     /**
16276      * Returns the element this view is bound to.
16277      * @return {Roo.Element}
16278      */
16279     getEl : function(){
16280         return this.wrapEl;
16281     },
16282     
16283     
16284
16285     /**
16286      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16287      */
16288     refresh : function(){
16289         //Roo.log('refresh');
16290         var t = this.tpl;
16291         
16292         // if we are using something like 'domtemplate', then
16293         // the what gets used is:
16294         // t.applySubtemplate(NAME, data, wrapping data..)
16295         // the outer template then get' applied with
16296         //     the store 'extra data'
16297         // and the body get's added to the
16298         //      roo-name="data" node?
16299         //      <span class='roo-tpl-{name}'></span> ?????
16300         
16301         
16302         
16303         this.clearSelections();
16304         this.el.update("");
16305         var html = [];
16306         var records = this.store.getRange();
16307         if(records.length < 1) {
16308             
16309             // is this valid??  = should it render a template??
16310             
16311             this.el.update(this.emptyText);
16312             return;
16313         }
16314         var el = this.el;
16315         if (this.dataName) {
16316             this.el.update(t.apply(this.store.meta)); //????
16317             el = this.el.child('.roo-tpl-' + this.dataName);
16318         }
16319         
16320         for(var i = 0, len = records.length; i < len; i++){
16321             var data = this.prepareData(records[i].data, i, records[i]);
16322             this.fireEvent("preparedata", this, data, i, records[i]);
16323             
16324             var d = Roo.apply({}, data);
16325             
16326             if(this.tickable){
16327                 Roo.apply(d, {'roo-id' : Roo.id()});
16328                 
16329                 var _this = this;
16330             
16331                 Roo.each(this.parent.item, function(item){
16332                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16333                         return;
16334                     }
16335                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16336                 });
16337             }
16338             
16339             html[html.length] = Roo.util.Format.trim(
16340                 this.dataName ?
16341                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16342                     t.apply(d)
16343             );
16344         }
16345         
16346         
16347         
16348         el.update(html.join(""));
16349         this.nodes = el.dom.childNodes;
16350         this.updateIndexes(0);
16351     },
16352     
16353
16354     /**
16355      * Function to override to reformat the data that is sent to
16356      * the template for each node.
16357      * DEPRICATED - use the preparedata event handler.
16358      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16359      * a JSON object for an UpdateManager bound view).
16360      */
16361     prepareData : function(data, index, record)
16362     {
16363         this.fireEvent("preparedata", this, data, index, record);
16364         return data;
16365     },
16366
16367     onUpdate : function(ds, record){
16368         // Roo.log('on update');   
16369         this.clearSelections();
16370         var index = this.store.indexOf(record);
16371         var n = this.nodes[index];
16372         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16373         n.parentNode.removeChild(n);
16374         this.updateIndexes(index, index);
16375     },
16376
16377     
16378     
16379 // --------- FIXME     
16380     onAdd : function(ds, records, index)
16381     {
16382         //Roo.log(['on Add', ds, records, index] );        
16383         this.clearSelections();
16384         if(this.nodes.length == 0){
16385             this.refresh();
16386             return;
16387         }
16388         var n = this.nodes[index];
16389         for(var i = 0, len = records.length; i < len; i++){
16390             var d = this.prepareData(records[i].data, i, records[i]);
16391             if(n){
16392                 this.tpl.insertBefore(n, d);
16393             }else{
16394                 
16395                 this.tpl.append(this.el, d);
16396             }
16397         }
16398         this.updateIndexes(index);
16399     },
16400
16401     onRemove : function(ds, record, index){
16402        // Roo.log('onRemove');
16403         this.clearSelections();
16404         var el = this.dataName  ?
16405             this.el.child('.roo-tpl-' + this.dataName) :
16406             this.el; 
16407         
16408         el.dom.removeChild(this.nodes[index]);
16409         this.updateIndexes(index);
16410     },
16411
16412     /**
16413      * Refresh an individual node.
16414      * @param {Number} index
16415      */
16416     refreshNode : function(index){
16417         this.onUpdate(this.store, this.store.getAt(index));
16418     },
16419
16420     updateIndexes : function(startIndex, endIndex){
16421         var ns = this.nodes;
16422         startIndex = startIndex || 0;
16423         endIndex = endIndex || ns.length - 1;
16424         for(var i = startIndex; i <= endIndex; i++){
16425             ns[i].nodeIndex = i;
16426         }
16427     },
16428
16429     /**
16430      * Changes the data store this view uses and refresh the view.
16431      * @param {Store} store
16432      */
16433     setStore : function(store, initial){
16434         if(!initial && this.store){
16435             this.store.un("datachanged", this.refresh);
16436             this.store.un("add", this.onAdd);
16437             this.store.un("remove", this.onRemove);
16438             this.store.un("update", this.onUpdate);
16439             this.store.un("clear", this.refresh);
16440             this.store.un("beforeload", this.onBeforeLoad);
16441             this.store.un("load", this.onLoad);
16442             this.store.un("loadexception", this.onLoad);
16443         }
16444         if(store){
16445           
16446             store.on("datachanged", this.refresh, this);
16447             store.on("add", this.onAdd, this);
16448             store.on("remove", this.onRemove, this);
16449             store.on("update", this.onUpdate, this);
16450             store.on("clear", this.refresh, this);
16451             store.on("beforeload", this.onBeforeLoad, this);
16452             store.on("load", this.onLoad, this);
16453             store.on("loadexception", this.onLoad, this);
16454         }
16455         
16456         if(store){
16457             this.refresh();
16458         }
16459     },
16460     /**
16461      * onbeforeLoad - masks the loading area.
16462      *
16463      */
16464     onBeforeLoad : function(store,opts)
16465     {
16466          //Roo.log('onBeforeLoad');   
16467         if (!opts.add) {
16468             this.el.update("");
16469         }
16470         this.el.mask(this.mask ? this.mask : "Loading" ); 
16471     },
16472     onLoad : function ()
16473     {
16474         this.el.unmask();
16475     },
16476     
16477
16478     /**
16479      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16480      * @param {HTMLElement} node
16481      * @return {HTMLElement} The template node
16482      */
16483     findItemFromChild : function(node){
16484         var el = this.dataName  ?
16485             this.el.child('.roo-tpl-' + this.dataName,true) :
16486             this.el.dom; 
16487         
16488         if(!node || node.parentNode == el){
16489                     return node;
16490             }
16491             var p = node.parentNode;
16492             while(p && p != el){
16493             if(p.parentNode == el){
16494                 return p;
16495             }
16496             p = p.parentNode;
16497         }
16498             return null;
16499     },
16500
16501     /** @ignore */
16502     onClick : function(e){
16503         var item = this.findItemFromChild(e.getTarget());
16504         if(item){
16505             var index = this.indexOf(item);
16506             if(this.onItemClick(item, index, e) !== false){
16507                 this.fireEvent("click", this, index, item, e);
16508             }
16509         }else{
16510             this.clearSelections();
16511         }
16512     },
16513
16514     /** @ignore */
16515     onContextMenu : function(e){
16516         var item = this.findItemFromChild(e.getTarget());
16517         if(item){
16518             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16519         }
16520     },
16521
16522     /** @ignore */
16523     onDblClick : function(e){
16524         var item = this.findItemFromChild(e.getTarget());
16525         if(item){
16526             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16527         }
16528     },
16529
16530     onItemClick : function(item, index, e)
16531     {
16532         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16533             return false;
16534         }
16535         if (this.toggleSelect) {
16536             var m = this.isSelected(item) ? 'unselect' : 'select';
16537             //Roo.log(m);
16538             var _t = this;
16539             _t[m](item, true, false);
16540             return true;
16541         }
16542         if(this.multiSelect || this.singleSelect){
16543             if(this.multiSelect && e.shiftKey && this.lastSelection){
16544                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16545             }else{
16546                 this.select(item, this.multiSelect && e.ctrlKey);
16547                 this.lastSelection = item;
16548             }
16549             
16550             if(!this.tickable){
16551                 e.preventDefault();
16552             }
16553             
16554         }
16555         return true;
16556     },
16557
16558     /**
16559      * Get the number of selected nodes.
16560      * @return {Number}
16561      */
16562     getSelectionCount : function(){
16563         return this.selections.length;
16564     },
16565
16566     /**
16567      * Get the currently selected nodes.
16568      * @return {Array} An array of HTMLElements
16569      */
16570     getSelectedNodes : function(){
16571         return this.selections;
16572     },
16573
16574     /**
16575      * Get the indexes of the selected nodes.
16576      * @return {Array}
16577      */
16578     getSelectedIndexes : function(){
16579         var indexes = [], s = this.selections;
16580         for(var i = 0, len = s.length; i < len; i++){
16581             indexes.push(s[i].nodeIndex);
16582         }
16583         return indexes;
16584     },
16585
16586     /**
16587      * Clear all selections
16588      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16589      */
16590     clearSelections : function(suppressEvent){
16591         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16592             this.cmp.elements = this.selections;
16593             this.cmp.removeClass(this.selectedClass);
16594             this.selections = [];
16595             if(!suppressEvent){
16596                 this.fireEvent("selectionchange", this, this.selections);
16597             }
16598         }
16599     },
16600
16601     /**
16602      * Returns true if the passed node is selected
16603      * @param {HTMLElement/Number} node The node or node index
16604      * @return {Boolean}
16605      */
16606     isSelected : function(node){
16607         var s = this.selections;
16608         if(s.length < 1){
16609             return false;
16610         }
16611         node = this.getNode(node);
16612         return s.indexOf(node) !== -1;
16613     },
16614
16615     /**
16616      * Selects nodes.
16617      * @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
16618      * @param {Boolean} keepExisting (optional) true to keep existing selections
16619      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16620      */
16621     select : function(nodeInfo, keepExisting, suppressEvent){
16622         if(nodeInfo instanceof Array){
16623             if(!keepExisting){
16624                 this.clearSelections(true);
16625             }
16626             for(var i = 0, len = nodeInfo.length; i < len; i++){
16627                 this.select(nodeInfo[i], true, true);
16628             }
16629             return;
16630         } 
16631         var node = this.getNode(nodeInfo);
16632         if(!node || this.isSelected(node)){
16633             return; // already selected.
16634         }
16635         if(!keepExisting){
16636             this.clearSelections(true);
16637         }
16638         
16639         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16640             Roo.fly(node).addClass(this.selectedClass);
16641             this.selections.push(node);
16642             if(!suppressEvent){
16643                 this.fireEvent("selectionchange", this, this.selections);
16644             }
16645         }
16646         
16647         
16648     },
16649       /**
16650      * Unselects nodes.
16651      * @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
16652      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16653      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16654      */
16655     unselect : function(nodeInfo, keepExisting, suppressEvent)
16656     {
16657         if(nodeInfo instanceof Array){
16658             Roo.each(this.selections, function(s) {
16659                 this.unselect(s, nodeInfo);
16660             }, this);
16661             return;
16662         }
16663         var node = this.getNode(nodeInfo);
16664         if(!node || !this.isSelected(node)){
16665             //Roo.log("not selected");
16666             return; // not selected.
16667         }
16668         // fireevent???
16669         var ns = [];
16670         Roo.each(this.selections, function(s) {
16671             if (s == node ) {
16672                 Roo.fly(node).removeClass(this.selectedClass);
16673
16674                 return;
16675             }
16676             ns.push(s);
16677         },this);
16678         
16679         this.selections= ns;
16680         this.fireEvent("selectionchange", this, this.selections);
16681     },
16682
16683     /**
16684      * Gets a template node.
16685      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16686      * @return {HTMLElement} The node or null if it wasn't found
16687      */
16688     getNode : function(nodeInfo){
16689         if(typeof nodeInfo == "string"){
16690             return document.getElementById(nodeInfo);
16691         }else if(typeof nodeInfo == "number"){
16692             return this.nodes[nodeInfo];
16693         }
16694         return nodeInfo;
16695     },
16696
16697     /**
16698      * Gets a range template nodes.
16699      * @param {Number} startIndex
16700      * @param {Number} endIndex
16701      * @return {Array} An array of nodes
16702      */
16703     getNodes : function(start, end){
16704         var ns = this.nodes;
16705         start = start || 0;
16706         end = typeof end == "undefined" ? ns.length - 1 : end;
16707         var nodes = [];
16708         if(start <= end){
16709             for(var i = start; i <= end; i++){
16710                 nodes.push(ns[i]);
16711             }
16712         } else{
16713             for(var i = start; i >= end; i--){
16714                 nodes.push(ns[i]);
16715             }
16716         }
16717         return nodes;
16718     },
16719
16720     /**
16721      * Finds the index of the passed node
16722      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16723      * @return {Number} The index of the node or -1
16724      */
16725     indexOf : function(node){
16726         node = this.getNode(node);
16727         if(typeof node.nodeIndex == "number"){
16728             return node.nodeIndex;
16729         }
16730         var ns = this.nodes;
16731         for(var i = 0, len = ns.length; i < len; i++){
16732             if(ns[i] == node){
16733                 return i;
16734             }
16735         }
16736         return -1;
16737     }
16738 });
16739 /*
16740  * - LGPL
16741  *
16742  * based on jquery fullcalendar
16743  * 
16744  */
16745
16746 Roo.bootstrap = Roo.bootstrap || {};
16747 /**
16748  * @class Roo.bootstrap.Calendar
16749  * @extends Roo.bootstrap.Component
16750  * Bootstrap Calendar class
16751  * @cfg {Boolean} loadMask (true|false) default false
16752  * @cfg {Object} header generate the user specific header of the calendar, default false
16753
16754  * @constructor
16755  * Create a new Container
16756  * @param {Object} config The config object
16757  */
16758
16759
16760
16761 Roo.bootstrap.Calendar = function(config){
16762     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16763      this.addEvents({
16764         /**
16765              * @event select
16766              * Fires when a date is selected
16767              * @param {DatePicker} this
16768              * @param {Date} date The selected date
16769              */
16770         'select': true,
16771         /**
16772              * @event monthchange
16773              * Fires when the displayed month changes 
16774              * @param {DatePicker} this
16775              * @param {Date} date The selected month
16776              */
16777         'monthchange': true,
16778         /**
16779              * @event evententer
16780              * Fires when mouse over an event
16781              * @param {Calendar} this
16782              * @param {event} Event
16783              */
16784         'evententer': true,
16785         /**
16786              * @event eventleave
16787              * Fires when the mouse leaves an
16788              * @param {Calendar} this
16789              * @param {event}
16790              */
16791         'eventleave': true,
16792         /**
16793              * @event eventclick
16794              * Fires when the mouse click an
16795              * @param {Calendar} this
16796              * @param {event}
16797              */
16798         'eventclick': true
16799         
16800     });
16801
16802 };
16803
16804 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16805     
16806      /**
16807      * @cfg {Number} startDay
16808      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16809      */
16810     startDay : 0,
16811     
16812     loadMask : false,
16813     
16814     header : false,
16815       
16816     getAutoCreate : function(){
16817         
16818         
16819         var fc_button = function(name, corner, style, content ) {
16820             return Roo.apply({},{
16821                 tag : 'span',
16822                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16823                          (corner.length ?
16824                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16825                             ''
16826                         ),
16827                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16828                 unselectable: 'on'
16829             });
16830         };
16831         
16832         var header = {};
16833         
16834         if(!this.header){
16835             header = {
16836                 tag : 'table',
16837                 cls : 'fc-header',
16838                 style : 'width:100%',
16839                 cn : [
16840                     {
16841                         tag: 'tr',
16842                         cn : [
16843                             {
16844                                 tag : 'td',
16845                                 cls : 'fc-header-left',
16846                                 cn : [
16847                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16848                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16849                                     { tag: 'span', cls: 'fc-header-space' },
16850                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16851
16852
16853                                 ]
16854                             },
16855
16856                             {
16857                                 tag : 'td',
16858                                 cls : 'fc-header-center',
16859                                 cn : [
16860                                     {
16861                                         tag: 'span',
16862                                         cls: 'fc-header-title',
16863                                         cn : {
16864                                             tag: 'H2',
16865                                             html : 'month / year'
16866                                         }
16867                                     }
16868
16869                                 ]
16870                             },
16871                             {
16872                                 tag : 'td',
16873                                 cls : 'fc-header-right',
16874                                 cn : [
16875                               /*      fc_button('month', 'left', '', 'month' ),
16876                                     fc_button('week', '', '', 'week' ),
16877                                     fc_button('day', 'right', '', 'day' )
16878                                 */    
16879
16880                                 ]
16881                             }
16882
16883                         ]
16884                     }
16885                 ]
16886             };
16887         }
16888         
16889         header = this.header;
16890         
16891        
16892         var cal_heads = function() {
16893             var ret = [];
16894             // fixme - handle this.
16895             
16896             for (var i =0; i < Date.dayNames.length; i++) {
16897                 var d = Date.dayNames[i];
16898                 ret.push({
16899                     tag: 'th',
16900                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16901                     html : d.substring(0,3)
16902                 });
16903                 
16904             }
16905             ret[0].cls += ' fc-first';
16906             ret[6].cls += ' fc-last';
16907             return ret;
16908         };
16909         var cal_cell = function(n) {
16910             return  {
16911                 tag: 'td',
16912                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16913                 cn : [
16914                     {
16915                         cn : [
16916                             {
16917                                 cls: 'fc-day-number',
16918                                 html: 'D'
16919                             },
16920                             {
16921                                 cls: 'fc-day-content',
16922                              
16923                                 cn : [
16924                                      {
16925                                         style: 'position: relative;' // height: 17px;
16926                                     }
16927                                 ]
16928                             }
16929                             
16930                             
16931                         ]
16932                     }
16933                 ]
16934                 
16935             }
16936         };
16937         var cal_rows = function() {
16938             
16939             var ret = [];
16940             for (var r = 0; r < 6; r++) {
16941                 var row= {
16942                     tag : 'tr',
16943                     cls : 'fc-week',
16944                     cn : []
16945                 };
16946                 
16947                 for (var i =0; i < Date.dayNames.length; i++) {
16948                     var d = Date.dayNames[i];
16949                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16950
16951                 }
16952                 row.cn[0].cls+=' fc-first';
16953                 row.cn[0].cn[0].style = 'min-height:90px';
16954                 row.cn[6].cls+=' fc-last';
16955                 ret.push(row);
16956                 
16957             }
16958             ret[0].cls += ' fc-first';
16959             ret[4].cls += ' fc-prev-last';
16960             ret[5].cls += ' fc-last';
16961             return ret;
16962             
16963         };
16964         
16965         var cal_table = {
16966             tag: 'table',
16967             cls: 'fc-border-separate',
16968             style : 'width:100%',
16969             cellspacing  : 0,
16970             cn : [
16971                 { 
16972                     tag: 'thead',
16973                     cn : [
16974                         { 
16975                             tag: 'tr',
16976                             cls : 'fc-first fc-last',
16977                             cn : cal_heads()
16978                         }
16979                     ]
16980                 },
16981                 { 
16982                     tag: 'tbody',
16983                     cn : cal_rows()
16984                 }
16985                   
16986             ]
16987         };
16988          
16989          var cfg = {
16990             cls : 'fc fc-ltr',
16991             cn : [
16992                 header,
16993                 {
16994                     cls : 'fc-content',
16995                     style : "position: relative;",
16996                     cn : [
16997                         {
16998                             cls : 'fc-view fc-view-month fc-grid',
16999                             style : 'position: relative',
17000                             unselectable : 'on',
17001                             cn : [
17002                                 {
17003                                     cls : 'fc-event-container',
17004                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17005                                 },
17006                                 cal_table
17007                             ]
17008                         }
17009                     ]
17010     
17011                 }
17012            ] 
17013             
17014         };
17015         
17016          
17017         
17018         return cfg;
17019     },
17020     
17021     
17022     initEvents : function()
17023     {
17024         if(!this.store){
17025             throw "can not find store for calendar";
17026         }
17027         
17028         var mark = {
17029             tag: "div",
17030             cls:"x-dlg-mask",
17031             style: "text-align:center",
17032             cn: [
17033                 {
17034                     tag: "div",
17035                     style: "background-color:white;width:50%;margin:250 auto",
17036                     cn: [
17037                         {
17038                             tag: "img",
17039                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17040                         },
17041                         {
17042                             tag: "span",
17043                             html: "Loading"
17044                         }
17045                         
17046                     ]
17047                 }
17048             ]
17049         };
17050         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17051         
17052         var size = this.el.select('.fc-content', true).first().getSize();
17053         this.maskEl.setSize(size.width, size.height);
17054         this.maskEl.enableDisplayMode("block");
17055         if(!this.loadMask){
17056             this.maskEl.hide();
17057         }
17058         
17059         this.store = Roo.factory(this.store, Roo.data);
17060         this.store.on('load', this.onLoad, this);
17061         this.store.on('beforeload', this.onBeforeLoad, this);
17062         
17063         this.resize();
17064         
17065         this.cells = this.el.select('.fc-day',true);
17066         //Roo.log(this.cells);
17067         this.textNodes = this.el.query('.fc-day-number');
17068         this.cells.addClassOnOver('fc-state-hover');
17069         
17070         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17071         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17072         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17073         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17074         
17075         this.on('monthchange', this.onMonthChange, this);
17076         
17077         this.update(new Date().clearTime());
17078     },
17079     
17080     resize : function() {
17081         var sz  = this.el.getSize();
17082         
17083         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17084         this.el.select('.fc-day-content div',true).setHeight(34);
17085     },
17086     
17087     
17088     // private
17089     showPrevMonth : function(e){
17090         this.update(this.activeDate.add("mo", -1));
17091     },
17092     showToday : function(e){
17093         this.update(new Date().clearTime());
17094     },
17095     // private
17096     showNextMonth : function(e){
17097         this.update(this.activeDate.add("mo", 1));
17098     },
17099
17100     // private
17101     showPrevYear : function(){
17102         this.update(this.activeDate.add("y", -1));
17103     },
17104
17105     // private
17106     showNextYear : function(){
17107         this.update(this.activeDate.add("y", 1));
17108     },
17109
17110     
17111    // private
17112     update : function(date)
17113     {
17114         var vd = this.activeDate;
17115         this.activeDate = date;
17116 //        if(vd && this.el){
17117 //            var t = date.getTime();
17118 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17119 //                Roo.log('using add remove');
17120 //                
17121 //                this.fireEvent('monthchange', this, date);
17122 //                
17123 //                this.cells.removeClass("fc-state-highlight");
17124 //                this.cells.each(function(c){
17125 //                   if(c.dateValue == t){
17126 //                       c.addClass("fc-state-highlight");
17127 //                       setTimeout(function(){
17128 //                            try{c.dom.firstChild.focus();}catch(e){}
17129 //                       }, 50);
17130 //                       return false;
17131 //                   }
17132 //                   return true;
17133 //                });
17134 //                return;
17135 //            }
17136 //        }
17137         
17138         var days = date.getDaysInMonth();
17139         
17140         var firstOfMonth = date.getFirstDateOfMonth();
17141         var startingPos = firstOfMonth.getDay()-this.startDay;
17142         
17143         if(startingPos < this.startDay){
17144             startingPos += 7;
17145         }
17146         
17147         var pm = date.add(Date.MONTH, -1);
17148         var prevStart = pm.getDaysInMonth()-startingPos;
17149 //        
17150         this.cells = this.el.select('.fc-day',true);
17151         this.textNodes = this.el.query('.fc-day-number');
17152         this.cells.addClassOnOver('fc-state-hover');
17153         
17154         var cells = this.cells.elements;
17155         var textEls = this.textNodes;
17156         
17157         Roo.each(cells, function(cell){
17158             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17159         });
17160         
17161         days += startingPos;
17162
17163         // convert everything to numbers so it's fast
17164         var day = 86400000;
17165         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17166         //Roo.log(d);
17167         //Roo.log(pm);
17168         //Roo.log(prevStart);
17169         
17170         var today = new Date().clearTime().getTime();
17171         var sel = date.clearTime().getTime();
17172         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17173         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17174         var ddMatch = this.disabledDatesRE;
17175         var ddText = this.disabledDatesText;
17176         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17177         var ddaysText = this.disabledDaysText;
17178         var format = this.format;
17179         
17180         var setCellClass = function(cal, cell){
17181             cell.row = 0;
17182             cell.events = [];
17183             cell.more = [];
17184             //Roo.log('set Cell Class');
17185             cell.title = "";
17186             var t = d.getTime();
17187             
17188             //Roo.log(d);
17189             
17190             cell.dateValue = t;
17191             if(t == today){
17192                 cell.className += " fc-today";
17193                 cell.className += " fc-state-highlight";
17194                 cell.title = cal.todayText;
17195             }
17196             if(t == sel){
17197                 // disable highlight in other month..
17198                 //cell.className += " fc-state-highlight";
17199                 
17200             }
17201             // disabling
17202             if(t < min) {
17203                 cell.className = " fc-state-disabled";
17204                 cell.title = cal.minText;
17205                 return;
17206             }
17207             if(t > max) {
17208                 cell.className = " fc-state-disabled";
17209                 cell.title = cal.maxText;
17210                 return;
17211             }
17212             if(ddays){
17213                 if(ddays.indexOf(d.getDay()) != -1){
17214                     cell.title = ddaysText;
17215                     cell.className = " fc-state-disabled";
17216                 }
17217             }
17218             if(ddMatch && format){
17219                 var fvalue = d.dateFormat(format);
17220                 if(ddMatch.test(fvalue)){
17221                     cell.title = ddText.replace("%0", fvalue);
17222                     cell.className = " fc-state-disabled";
17223                 }
17224             }
17225             
17226             if (!cell.initialClassName) {
17227                 cell.initialClassName = cell.dom.className;
17228             }
17229             
17230             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17231         };
17232
17233         var i = 0;
17234         
17235         for(; i < startingPos; i++) {
17236             textEls[i].innerHTML = (++prevStart);
17237             d.setDate(d.getDate()+1);
17238             
17239             cells[i].className = "fc-past fc-other-month";
17240             setCellClass(this, cells[i]);
17241         }
17242         
17243         var intDay = 0;
17244         
17245         for(; i < days; i++){
17246             intDay = i - startingPos + 1;
17247             textEls[i].innerHTML = (intDay);
17248             d.setDate(d.getDate()+1);
17249             
17250             cells[i].className = ''; // "x-date-active";
17251             setCellClass(this, cells[i]);
17252         }
17253         var extraDays = 0;
17254         
17255         for(; i < 42; i++) {
17256             textEls[i].innerHTML = (++extraDays);
17257             d.setDate(d.getDate()+1);
17258             
17259             cells[i].className = "fc-future fc-other-month";
17260             setCellClass(this, cells[i]);
17261         }
17262         
17263         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17264         
17265         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17266         
17267         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17268         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17269         
17270         if(totalRows != 6){
17271             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17272             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17273         }
17274         
17275         this.fireEvent('monthchange', this, date);
17276         
17277         
17278         /*
17279         if(!this.internalRender){
17280             var main = this.el.dom.firstChild;
17281             var w = main.offsetWidth;
17282             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17283             Roo.fly(main).setWidth(w);
17284             this.internalRender = true;
17285             // opera does not respect the auto grow header center column
17286             // then, after it gets a width opera refuses to recalculate
17287             // without a second pass
17288             if(Roo.isOpera && !this.secondPass){
17289                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17290                 this.secondPass = true;
17291                 this.update.defer(10, this, [date]);
17292             }
17293         }
17294         */
17295         
17296     },
17297     
17298     findCell : function(dt) {
17299         dt = dt.clearTime().getTime();
17300         var ret = false;
17301         this.cells.each(function(c){
17302             //Roo.log("check " +c.dateValue + '?=' + dt);
17303             if(c.dateValue == dt){
17304                 ret = c;
17305                 return false;
17306             }
17307             return true;
17308         });
17309         
17310         return ret;
17311     },
17312     
17313     findCells : function(ev) {
17314         var s = ev.start.clone().clearTime().getTime();
17315        // Roo.log(s);
17316         var e= ev.end.clone().clearTime().getTime();
17317        // Roo.log(e);
17318         var ret = [];
17319         this.cells.each(function(c){
17320              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17321             
17322             if(c.dateValue > e){
17323                 return ;
17324             }
17325             if(c.dateValue < s){
17326                 return ;
17327             }
17328             ret.push(c);
17329         });
17330         
17331         return ret;    
17332     },
17333     
17334 //    findBestRow: function(cells)
17335 //    {
17336 //        var ret = 0;
17337 //        
17338 //        for (var i =0 ; i < cells.length;i++) {
17339 //            ret  = Math.max(cells[i].rows || 0,ret);
17340 //        }
17341 //        return ret;
17342 //        
17343 //    },
17344     
17345     
17346     addItem : function(ev)
17347     {
17348         // look for vertical location slot in
17349         var cells = this.findCells(ev);
17350         
17351 //        ev.row = this.findBestRow(cells);
17352         
17353         // work out the location.
17354         
17355         var crow = false;
17356         var rows = [];
17357         for(var i =0; i < cells.length; i++) {
17358             
17359             cells[i].row = cells[0].row;
17360             
17361             if(i == 0){
17362                 cells[i].row = cells[i].row + 1;
17363             }
17364             
17365             if (!crow) {
17366                 crow = {
17367                     start : cells[i],
17368                     end :  cells[i]
17369                 };
17370                 continue;
17371             }
17372             if (crow.start.getY() == cells[i].getY()) {
17373                 // on same row.
17374                 crow.end = cells[i];
17375                 continue;
17376             }
17377             // different row.
17378             rows.push(crow);
17379             crow = {
17380                 start: cells[i],
17381                 end : cells[i]
17382             };
17383             
17384         }
17385         
17386         rows.push(crow);
17387         ev.els = [];
17388         ev.rows = rows;
17389         ev.cells = cells;
17390         
17391         cells[0].events.push(ev);
17392         
17393         this.calevents.push(ev);
17394     },
17395     
17396     clearEvents: function() {
17397         
17398         if(!this.calevents){
17399             return;
17400         }
17401         
17402         Roo.each(this.cells.elements, function(c){
17403             c.row = 0;
17404             c.events = [];
17405             c.more = [];
17406         });
17407         
17408         Roo.each(this.calevents, function(e) {
17409             Roo.each(e.els, function(el) {
17410                 el.un('mouseenter' ,this.onEventEnter, this);
17411                 el.un('mouseleave' ,this.onEventLeave, this);
17412                 el.remove();
17413             },this);
17414         },this);
17415         
17416         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17417             e.remove();
17418         });
17419         
17420     },
17421     
17422     renderEvents: function()
17423     {   
17424         var _this = this;
17425         
17426         this.cells.each(function(c) {
17427             
17428             if(c.row < 5){
17429                 return;
17430             }
17431             
17432             var ev = c.events;
17433             
17434             var r = 4;
17435             if(c.row != c.events.length){
17436                 r = 4 - (4 - (c.row - c.events.length));
17437             }
17438             
17439             c.events = ev.slice(0, r);
17440             c.more = ev.slice(r);
17441             
17442             if(c.more.length && c.more.length == 1){
17443                 c.events.push(c.more.pop());
17444             }
17445             
17446             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17447             
17448         });
17449             
17450         this.cells.each(function(c) {
17451             
17452             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17453             
17454             
17455             for (var e = 0; e < c.events.length; e++){
17456                 var ev = c.events[e];
17457                 var rows = ev.rows;
17458                 
17459                 for(var i = 0; i < rows.length; i++) {
17460                 
17461                     // how many rows should it span..
17462
17463                     var  cfg = {
17464                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17465                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17466
17467                         unselectable : "on",
17468                         cn : [
17469                             {
17470                                 cls: 'fc-event-inner',
17471                                 cn : [
17472     //                                {
17473     //                                  tag:'span',
17474     //                                  cls: 'fc-event-time',
17475     //                                  html : cells.length > 1 ? '' : ev.time
17476     //                                },
17477                                     {
17478                                       tag:'span',
17479                                       cls: 'fc-event-title',
17480                                       html : String.format('{0}', ev.title)
17481                                     }
17482
17483
17484                                 ]
17485                             },
17486                             {
17487                                 cls: 'ui-resizable-handle ui-resizable-e',
17488                                 html : '&nbsp;&nbsp;&nbsp'
17489                             }
17490
17491                         ]
17492                     };
17493
17494                     if (i == 0) {
17495                         cfg.cls += ' fc-event-start';
17496                     }
17497                     if ((i+1) == rows.length) {
17498                         cfg.cls += ' fc-event-end';
17499                     }
17500
17501                     var ctr = _this.el.select('.fc-event-container',true).first();
17502                     var cg = ctr.createChild(cfg);
17503
17504                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17505                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17506
17507                     var r = (c.more.length) ? 1 : 0;
17508                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17509                     cg.setWidth(ebox.right - sbox.x -2);
17510
17511                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17512                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17513                     cg.on('click', _this.onEventClick, _this, ev);
17514
17515                     ev.els.push(cg);
17516                     
17517                 }
17518                 
17519             }
17520             
17521             
17522             if(c.more.length){
17523                 var  cfg = {
17524                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17525                     style : 'position: absolute',
17526                     unselectable : "on",
17527                     cn : [
17528                         {
17529                             cls: 'fc-event-inner',
17530                             cn : [
17531                                 {
17532                                   tag:'span',
17533                                   cls: 'fc-event-title',
17534                                   html : 'More'
17535                                 }
17536
17537
17538                             ]
17539                         },
17540                         {
17541                             cls: 'ui-resizable-handle ui-resizable-e',
17542                             html : '&nbsp;&nbsp;&nbsp'
17543                         }
17544
17545                     ]
17546                 };
17547
17548                 var ctr = _this.el.select('.fc-event-container',true).first();
17549                 var cg = ctr.createChild(cfg);
17550
17551                 var sbox = c.select('.fc-day-content',true).first().getBox();
17552                 var ebox = c.select('.fc-day-content',true).first().getBox();
17553                 //Roo.log(cg);
17554                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17555                 cg.setWidth(ebox.right - sbox.x -2);
17556
17557                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17558                 
17559             }
17560             
17561         });
17562         
17563         
17564         
17565     },
17566     
17567     onEventEnter: function (e, el,event,d) {
17568         this.fireEvent('evententer', this, el, event);
17569     },
17570     
17571     onEventLeave: function (e, el,event,d) {
17572         this.fireEvent('eventleave', this, el, event);
17573     },
17574     
17575     onEventClick: function (e, el,event,d) {
17576         this.fireEvent('eventclick', this, el, event);
17577     },
17578     
17579     onMonthChange: function () {
17580         this.store.load();
17581     },
17582     
17583     onMoreEventClick: function(e, el, more)
17584     {
17585         var _this = this;
17586         
17587         this.calpopover.placement = 'right';
17588         this.calpopover.setTitle('More');
17589         
17590         this.calpopover.setContent('');
17591         
17592         var ctr = this.calpopover.el.select('.popover-content', true).first();
17593         
17594         Roo.each(more, function(m){
17595             var cfg = {
17596                 cls : 'fc-event-hori fc-event-draggable',
17597                 html : m.title
17598             };
17599             var cg = ctr.createChild(cfg);
17600             
17601             cg.on('click', _this.onEventClick, _this, m);
17602         });
17603         
17604         this.calpopover.show(el);
17605         
17606         
17607     },
17608     
17609     onLoad: function () 
17610     {   
17611         this.calevents = [];
17612         var cal = this;
17613         
17614         if(this.store.getCount() > 0){
17615             this.store.data.each(function(d){
17616                cal.addItem({
17617                     id : d.data.id,
17618                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17619                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17620                     time : d.data.start_time,
17621                     title : d.data.title,
17622                     description : d.data.description,
17623                     venue : d.data.venue
17624                 });
17625             });
17626         }
17627         
17628         this.renderEvents();
17629         
17630         if(this.calevents.length && this.loadMask){
17631             this.maskEl.hide();
17632         }
17633     },
17634     
17635     onBeforeLoad: function()
17636     {
17637         this.clearEvents();
17638         if(this.loadMask){
17639             this.maskEl.show();
17640         }
17641     }
17642 });
17643
17644  
17645  /*
17646  * - LGPL
17647  *
17648  * element
17649  * 
17650  */
17651
17652 /**
17653  * @class Roo.bootstrap.Popover
17654  * @extends Roo.bootstrap.Component
17655  * Bootstrap Popover class
17656  * @cfg {String} html contents of the popover   (or false to use children..)
17657  * @cfg {String} title of popover (or false to hide)
17658  * @cfg {String} placement how it is placed
17659  * @cfg {String} trigger click || hover (or false to trigger manually)
17660  * @cfg {String} over what (parent or false to trigger manually.)
17661  * @cfg {Number} delay - delay before showing
17662  
17663  * @constructor
17664  * Create a new Popover
17665  * @param {Object} config The config object
17666  */
17667
17668 Roo.bootstrap.Popover = function(config){
17669     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17670     
17671     this.addEvents({
17672         // raw events
17673          /**
17674          * @event show
17675          * After the popover show
17676          * 
17677          * @param {Roo.bootstrap.Popover} this
17678          */
17679         "show" : true,
17680         /**
17681          * @event hide
17682          * After the popover hide
17683          * 
17684          * @param {Roo.bootstrap.Popover} this
17685          */
17686         "hide" : true
17687     });
17688 };
17689
17690 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17691     
17692     title: 'Fill in a title',
17693     html: false,
17694     
17695     placement : 'right',
17696     trigger : 'hover', // hover
17697     
17698     delay : 0,
17699     
17700     over: 'parent',
17701     
17702     can_build_overlaid : false,
17703     
17704     getChildContainer : function()
17705     {
17706         return this.el.select('.popover-content',true).first();
17707     },
17708     
17709     getAutoCreate : function(){
17710          
17711         var cfg = {
17712            cls : 'popover roo-dynamic',
17713            style: 'display:block',
17714            cn : [
17715                 {
17716                     cls : 'arrow'
17717                 },
17718                 {
17719                     cls : 'popover-inner',
17720                     cn : [
17721                         {
17722                             tag: 'h3',
17723                             cls: 'popover-title popover-header',
17724                             html : this.title
17725                         },
17726                         {
17727                             cls : 'popover-content popover-body',
17728                             html : this.html
17729                         }
17730                     ]
17731                     
17732                 }
17733            ]
17734         };
17735         
17736         return cfg;
17737     },
17738     setTitle: function(str)
17739     {
17740         this.title = str;
17741         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17742     },
17743     setContent: function(str)
17744     {
17745         this.html = str;
17746         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17747     },
17748     // as it get's added to the bottom of the page.
17749     onRender : function(ct, position)
17750     {
17751         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17752         if(!this.el){
17753             var cfg = Roo.apply({},  this.getAutoCreate());
17754             cfg.id = Roo.id();
17755             
17756             if (this.cls) {
17757                 cfg.cls += ' ' + this.cls;
17758             }
17759             if (this.style) {
17760                 cfg.style = this.style;
17761             }
17762             //Roo.log("adding to ");
17763             this.el = Roo.get(document.body).createChild(cfg, position);
17764 //            Roo.log(this.el);
17765         }
17766         this.initEvents();
17767     },
17768     
17769     initEvents : function()
17770     {
17771         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17772         this.el.enableDisplayMode('block');
17773         this.el.hide();
17774         if (this.over === false) {
17775             return; 
17776         }
17777         if (this.triggers === false) {
17778             return;
17779         }
17780         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17781         var triggers = this.trigger ? this.trigger.split(' ') : [];
17782         Roo.each(triggers, function(trigger) {
17783         
17784             if (trigger == 'click') {
17785                 on_el.on('click', this.toggle, this);
17786             } else if (trigger != 'manual') {
17787                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17788                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17789       
17790                 on_el.on(eventIn  ,this.enter, this);
17791                 on_el.on(eventOut, this.leave, this);
17792             }
17793         }, this);
17794         
17795     },
17796     
17797     
17798     // private
17799     timeout : null,
17800     hoverState : null,
17801     
17802     toggle : function () {
17803         this.hoverState == 'in' ? this.leave() : this.enter();
17804     },
17805     
17806     enter : function () {
17807         
17808         clearTimeout(this.timeout);
17809     
17810         this.hoverState = 'in';
17811     
17812         if (!this.delay || !this.delay.show) {
17813             this.show();
17814             return;
17815         }
17816         var _t = this;
17817         this.timeout = setTimeout(function () {
17818             if (_t.hoverState == 'in') {
17819                 _t.show();
17820             }
17821         }, this.delay.show)
17822     },
17823     
17824     leave : function() {
17825         clearTimeout(this.timeout);
17826     
17827         this.hoverState = 'out';
17828     
17829         if (!this.delay || !this.delay.hide) {
17830             this.hide();
17831             return;
17832         }
17833         var _t = this;
17834         this.timeout = setTimeout(function () {
17835             if (_t.hoverState == 'out') {
17836                 _t.hide();
17837             }
17838         }, this.delay.hide)
17839     },
17840     
17841     show : function (on_el)
17842     {
17843         if (!on_el) {
17844             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17845         }
17846         
17847         // set content.
17848         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17849         if (this.html !== false) {
17850             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17851         }
17852         this.el.removeClass([
17853             'fade','top','bottom', 'left', 'right','in',
17854             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17855         ]);
17856         if (!this.title.length) {
17857             this.el.select('.popover-title',true).hide();
17858         }
17859         
17860         var placement = typeof this.placement == 'function' ?
17861             this.placement.call(this, this.el, on_el) :
17862             this.placement;
17863             
17864         var autoToken = /\s?auto?\s?/i;
17865         var autoPlace = autoToken.test(placement);
17866         if (autoPlace) {
17867             placement = placement.replace(autoToken, '') || 'top';
17868         }
17869         
17870         //this.el.detach()
17871         //this.el.setXY([0,0]);
17872         this.el.show();
17873         this.el.dom.style.display='block';
17874         this.el.addClass(placement);
17875         
17876         //this.el.appendTo(on_el);
17877         
17878         var p = this.getPosition();
17879         var box = this.el.getBox();
17880         
17881         if (autoPlace) {
17882             // fixme..
17883         }
17884         var align = Roo.bootstrap.Popover.alignment[placement];
17885         
17886 //        Roo.log(align);
17887         this.el.alignTo(on_el, align[0],align[1]);
17888         //var arrow = this.el.select('.arrow',true).first();
17889         //arrow.set(align[2], 
17890         
17891         this.el.addClass('in');
17892         
17893         
17894         if (this.el.hasClass('fade')) {
17895             // fade it?
17896         }
17897         
17898         this.hoverState = 'in';
17899         
17900         this.fireEvent('show', this);
17901         
17902     },
17903     hide : function()
17904     {
17905         this.el.setXY([0,0]);
17906         this.el.removeClass('in');
17907         this.el.hide();
17908         this.hoverState = null;
17909         
17910         this.fireEvent('hide', this);
17911     }
17912     
17913 });
17914
17915 Roo.bootstrap.Popover.alignment = {
17916     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17917     'right' : ['l-r', [10,0], 'left bs-popover-left'],
17918     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17919     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17920 };
17921
17922  /*
17923  * - LGPL
17924  *
17925  * Progress
17926  * 
17927  */
17928
17929 /**
17930  * @class Roo.bootstrap.Progress
17931  * @extends Roo.bootstrap.Component
17932  * Bootstrap Progress class
17933  * @cfg {Boolean} striped striped of the progress bar
17934  * @cfg {Boolean} active animated of the progress bar
17935  * 
17936  * 
17937  * @constructor
17938  * Create a new Progress
17939  * @param {Object} config The config object
17940  */
17941
17942 Roo.bootstrap.Progress = function(config){
17943     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17944 };
17945
17946 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17947     
17948     striped : false,
17949     active: false,
17950     
17951     getAutoCreate : function(){
17952         var cfg = {
17953             tag: 'div',
17954             cls: 'progress'
17955         };
17956         
17957         
17958         if(this.striped){
17959             cfg.cls += ' progress-striped';
17960         }
17961       
17962         if(this.active){
17963             cfg.cls += ' active';
17964         }
17965         
17966         
17967         return cfg;
17968     }
17969    
17970 });
17971
17972  
17973
17974  /*
17975  * - LGPL
17976  *
17977  * ProgressBar
17978  * 
17979  */
17980
17981 /**
17982  * @class Roo.bootstrap.ProgressBar
17983  * @extends Roo.bootstrap.Component
17984  * Bootstrap ProgressBar class
17985  * @cfg {Number} aria_valuenow aria-value now
17986  * @cfg {Number} aria_valuemin aria-value min
17987  * @cfg {Number} aria_valuemax aria-value max
17988  * @cfg {String} label label for the progress bar
17989  * @cfg {String} panel (success | info | warning | danger )
17990  * @cfg {String} role role of the progress bar
17991  * @cfg {String} sr_only text
17992  * 
17993  * 
17994  * @constructor
17995  * Create a new ProgressBar
17996  * @param {Object} config The config object
17997  */
17998
17999 Roo.bootstrap.ProgressBar = function(config){
18000     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18001 };
18002
18003 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18004     
18005     aria_valuenow : 0,
18006     aria_valuemin : 0,
18007     aria_valuemax : 100,
18008     label : false,
18009     panel : false,
18010     role : false,
18011     sr_only: false,
18012     
18013     getAutoCreate : function()
18014     {
18015         
18016         var cfg = {
18017             tag: 'div',
18018             cls: 'progress-bar',
18019             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18020         };
18021         
18022         if(this.sr_only){
18023             cfg.cn = {
18024                 tag: 'span',
18025                 cls: 'sr-only',
18026                 html: this.sr_only
18027             }
18028         }
18029         
18030         if(this.role){
18031             cfg.role = this.role;
18032         }
18033         
18034         if(this.aria_valuenow){
18035             cfg['aria-valuenow'] = this.aria_valuenow;
18036         }
18037         
18038         if(this.aria_valuemin){
18039             cfg['aria-valuemin'] = this.aria_valuemin;
18040         }
18041         
18042         if(this.aria_valuemax){
18043             cfg['aria-valuemax'] = this.aria_valuemax;
18044         }
18045         
18046         if(this.label && !this.sr_only){
18047             cfg.html = this.label;
18048         }
18049         
18050         if(this.panel){
18051             cfg.cls += ' progress-bar-' + this.panel;
18052         }
18053         
18054         return cfg;
18055     },
18056     
18057     update : function(aria_valuenow)
18058     {
18059         this.aria_valuenow = aria_valuenow;
18060         
18061         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18062     }
18063    
18064 });
18065
18066  
18067
18068  /*
18069  * - LGPL
18070  *
18071  * column
18072  * 
18073  */
18074
18075 /**
18076  * @class Roo.bootstrap.TabGroup
18077  * @extends Roo.bootstrap.Column
18078  * Bootstrap Column class
18079  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18080  * @cfg {Boolean} carousel true to make the group behave like a carousel
18081  * @cfg {Boolean} bullets show bullets for the panels
18082  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18083  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18084  * @cfg {Boolean} showarrow (true|false) show arrow default true
18085  * 
18086  * @constructor
18087  * Create a new TabGroup
18088  * @param {Object} config The config object
18089  */
18090
18091 Roo.bootstrap.TabGroup = function(config){
18092     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18093     if (!this.navId) {
18094         this.navId = Roo.id();
18095     }
18096     this.tabs = [];
18097     Roo.bootstrap.TabGroup.register(this);
18098     
18099 };
18100
18101 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18102     
18103     carousel : false,
18104     transition : false,
18105     bullets : 0,
18106     timer : 0,
18107     autoslide : false,
18108     slideFn : false,
18109     slideOnTouch : false,
18110     showarrow : true,
18111     
18112     getAutoCreate : function()
18113     {
18114         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18115         
18116         cfg.cls += ' tab-content';
18117         
18118         if (this.carousel) {
18119             cfg.cls += ' carousel slide';
18120             
18121             cfg.cn = [{
18122                cls : 'carousel-inner',
18123                cn : []
18124             }];
18125         
18126             if(this.bullets  && !Roo.isTouch){
18127                 
18128                 var bullets = {
18129                     cls : 'carousel-bullets',
18130                     cn : []
18131                 };
18132                
18133                 if(this.bullets_cls){
18134                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18135                 }
18136                 
18137                 bullets.cn.push({
18138                     cls : 'clear'
18139                 });
18140                 
18141                 cfg.cn[0].cn.push(bullets);
18142             }
18143             
18144             if(this.showarrow){
18145                 cfg.cn[0].cn.push({
18146                     tag : 'div',
18147                     class : 'carousel-arrow',
18148                     cn : [
18149                         {
18150                             tag : 'div',
18151                             class : 'carousel-prev',
18152                             cn : [
18153                                 {
18154                                     tag : 'i',
18155                                     class : 'fa fa-chevron-left'
18156                                 }
18157                             ]
18158                         },
18159                         {
18160                             tag : 'div',
18161                             class : 'carousel-next',
18162                             cn : [
18163                                 {
18164                                     tag : 'i',
18165                                     class : 'fa fa-chevron-right'
18166                                 }
18167                             ]
18168                         }
18169                     ]
18170                 });
18171             }
18172             
18173         }
18174         
18175         return cfg;
18176     },
18177     
18178     initEvents:  function()
18179     {
18180 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18181 //            this.el.on("touchstart", this.onTouchStart, this);
18182 //        }
18183         
18184         if(this.autoslide){
18185             var _this = this;
18186             
18187             this.slideFn = window.setInterval(function() {
18188                 _this.showPanelNext();
18189             }, this.timer);
18190         }
18191         
18192         if(this.showarrow){
18193             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18194             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18195         }
18196         
18197         
18198     },
18199     
18200 //    onTouchStart : function(e, el, o)
18201 //    {
18202 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18203 //            return;
18204 //        }
18205 //        
18206 //        this.showPanelNext();
18207 //    },
18208     
18209     
18210     getChildContainer : function()
18211     {
18212         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18213     },
18214     
18215     /**
18216     * register a Navigation item
18217     * @param {Roo.bootstrap.NavItem} the navitem to add
18218     */
18219     register : function(item)
18220     {
18221         this.tabs.push( item);
18222         item.navId = this.navId; // not really needed..
18223         this.addBullet();
18224     
18225     },
18226     
18227     getActivePanel : function()
18228     {
18229         var r = false;
18230         Roo.each(this.tabs, function(t) {
18231             if (t.active) {
18232                 r = t;
18233                 return false;
18234             }
18235             return null;
18236         });
18237         return r;
18238         
18239     },
18240     getPanelByName : function(n)
18241     {
18242         var r = false;
18243         Roo.each(this.tabs, function(t) {
18244             if (t.tabId == n) {
18245                 r = t;
18246                 return false;
18247             }
18248             return null;
18249         });
18250         return r;
18251     },
18252     indexOfPanel : function(p)
18253     {
18254         var r = false;
18255         Roo.each(this.tabs, function(t,i) {
18256             if (t.tabId == p.tabId) {
18257                 r = i;
18258                 return false;
18259             }
18260             return null;
18261         });
18262         return r;
18263     },
18264     /**
18265      * show a specific panel
18266      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18267      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18268      */
18269     showPanel : function (pan)
18270     {
18271         if(this.transition || typeof(pan) == 'undefined'){
18272             Roo.log("waiting for the transitionend");
18273             return;
18274         }
18275         
18276         if (typeof(pan) == 'number') {
18277             pan = this.tabs[pan];
18278         }
18279         
18280         if (typeof(pan) == 'string') {
18281             pan = this.getPanelByName(pan);
18282         }
18283         
18284         var cur = this.getActivePanel();
18285         
18286         if(!pan || !cur){
18287             Roo.log('pan or acitve pan is undefined');
18288             return false;
18289         }
18290         
18291         if (pan.tabId == this.getActivePanel().tabId) {
18292             return true;
18293         }
18294         
18295         if (false === cur.fireEvent('beforedeactivate')) {
18296             return false;
18297         }
18298         
18299         if(this.bullets > 0 && !Roo.isTouch){
18300             this.setActiveBullet(this.indexOfPanel(pan));
18301         }
18302         
18303         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18304             
18305             this.transition = true;
18306             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18307             var lr = dir == 'next' ? 'left' : 'right';
18308             pan.el.addClass(dir); // or prev
18309             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18310             cur.el.addClass(lr); // or right
18311             pan.el.addClass(lr);
18312             
18313             var _this = this;
18314             cur.el.on('transitionend', function() {
18315                 Roo.log("trans end?");
18316                 
18317                 pan.el.removeClass([lr,dir]);
18318                 pan.setActive(true);
18319                 
18320                 cur.el.removeClass([lr]);
18321                 cur.setActive(false);
18322                 
18323                 _this.transition = false;
18324                 
18325             }, this, { single:  true } );
18326             
18327             return true;
18328         }
18329         
18330         cur.setActive(false);
18331         pan.setActive(true);
18332         
18333         return true;
18334         
18335     },
18336     showPanelNext : function()
18337     {
18338         var i = this.indexOfPanel(this.getActivePanel());
18339         
18340         if (i >= this.tabs.length - 1 && !this.autoslide) {
18341             return;
18342         }
18343         
18344         if (i >= this.tabs.length - 1 && this.autoslide) {
18345             i = -1;
18346         }
18347         
18348         this.showPanel(this.tabs[i+1]);
18349     },
18350     
18351     showPanelPrev : function()
18352     {
18353         var i = this.indexOfPanel(this.getActivePanel());
18354         
18355         if (i  < 1 && !this.autoslide) {
18356             return;
18357         }
18358         
18359         if (i < 1 && this.autoslide) {
18360             i = this.tabs.length;
18361         }
18362         
18363         this.showPanel(this.tabs[i-1]);
18364     },
18365     
18366     
18367     addBullet: function()
18368     {
18369         if(!this.bullets || Roo.isTouch){
18370             return;
18371         }
18372         var ctr = this.el.select('.carousel-bullets',true).first();
18373         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18374         var bullet = ctr.createChild({
18375             cls : 'bullet bullet-' + i
18376         },ctr.dom.lastChild);
18377         
18378         
18379         var _this = this;
18380         
18381         bullet.on('click', (function(e, el, o, ii, t){
18382
18383             e.preventDefault();
18384
18385             this.showPanel(ii);
18386
18387             if(this.autoslide && this.slideFn){
18388                 clearInterval(this.slideFn);
18389                 this.slideFn = window.setInterval(function() {
18390                     _this.showPanelNext();
18391                 }, this.timer);
18392             }
18393
18394         }).createDelegate(this, [i, bullet], true));
18395                 
18396         
18397     },
18398      
18399     setActiveBullet : function(i)
18400     {
18401         if(Roo.isTouch){
18402             return;
18403         }
18404         
18405         Roo.each(this.el.select('.bullet', true).elements, function(el){
18406             el.removeClass('selected');
18407         });
18408
18409         var bullet = this.el.select('.bullet-' + i, true).first();
18410         
18411         if(!bullet){
18412             return;
18413         }
18414         
18415         bullet.addClass('selected');
18416     }
18417     
18418     
18419   
18420 });
18421
18422  
18423
18424  
18425  
18426 Roo.apply(Roo.bootstrap.TabGroup, {
18427     
18428     groups: {},
18429      /**
18430     * register a Navigation Group
18431     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18432     */
18433     register : function(navgrp)
18434     {
18435         this.groups[navgrp.navId] = navgrp;
18436         
18437     },
18438     /**
18439     * fetch a Navigation Group based on the navigation ID
18440     * if one does not exist , it will get created.
18441     * @param {string} the navgroup to add
18442     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18443     */
18444     get: function(navId) {
18445         if (typeof(this.groups[navId]) == 'undefined') {
18446             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18447         }
18448         return this.groups[navId] ;
18449     }
18450     
18451     
18452     
18453 });
18454
18455  /*
18456  * - LGPL
18457  *
18458  * TabPanel
18459  * 
18460  */
18461
18462 /**
18463  * @class Roo.bootstrap.TabPanel
18464  * @extends Roo.bootstrap.Component
18465  * Bootstrap TabPanel class
18466  * @cfg {Boolean} active panel active
18467  * @cfg {String} html panel content
18468  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18469  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18470  * @cfg {String} href click to link..
18471  * 
18472  * 
18473  * @constructor
18474  * Create a new TabPanel
18475  * @param {Object} config The config object
18476  */
18477
18478 Roo.bootstrap.TabPanel = function(config){
18479     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18480     this.addEvents({
18481         /**
18482              * @event changed
18483              * Fires when the active status changes
18484              * @param {Roo.bootstrap.TabPanel} this
18485              * @param {Boolean} state the new state
18486             
18487          */
18488         'changed': true,
18489         /**
18490              * @event beforedeactivate
18491              * Fires before a tab is de-activated - can be used to do validation on a form.
18492              * @param {Roo.bootstrap.TabPanel} this
18493              * @return {Boolean} false if there is an error
18494             
18495          */
18496         'beforedeactivate': true
18497      });
18498     
18499     this.tabId = this.tabId || Roo.id();
18500   
18501 };
18502
18503 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18504     
18505     active: false,
18506     html: false,
18507     tabId: false,
18508     navId : false,
18509     href : '',
18510     
18511     getAutoCreate : function(){
18512         var cfg = {
18513             tag: 'div',
18514             // item is needed for carousel - not sure if it has any effect otherwise
18515             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18516             html: this.html || ''
18517         };
18518         
18519         if(this.active){
18520             cfg.cls += ' active';
18521         }
18522         
18523         if(this.tabId){
18524             cfg.tabId = this.tabId;
18525         }
18526         
18527         
18528         return cfg;
18529     },
18530     
18531     initEvents:  function()
18532     {
18533         var p = this.parent();
18534         
18535         this.navId = this.navId || p.navId;
18536         
18537         if (typeof(this.navId) != 'undefined') {
18538             // not really needed.. but just in case.. parent should be a NavGroup.
18539             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18540             
18541             tg.register(this);
18542             
18543             var i = tg.tabs.length - 1;
18544             
18545             if(this.active && tg.bullets > 0 && i < tg.bullets){
18546                 tg.setActiveBullet(i);
18547             }
18548         }
18549         
18550         this.el.on('click', this.onClick, this);
18551         
18552         if(Roo.isTouch){
18553             this.el.on("touchstart", this.onTouchStart, this);
18554             this.el.on("touchmove", this.onTouchMove, this);
18555             this.el.on("touchend", this.onTouchEnd, this);
18556         }
18557         
18558     },
18559     
18560     onRender : function(ct, position)
18561     {
18562         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18563     },
18564     
18565     setActive : function(state)
18566     {
18567         Roo.log("panel - set active " + this.tabId + "=" + state);
18568         
18569         this.active = state;
18570         if (!state) {
18571             this.el.removeClass('active');
18572             
18573         } else  if (!this.el.hasClass('active')) {
18574             this.el.addClass('active');
18575         }
18576         
18577         this.fireEvent('changed', this, state);
18578     },
18579     
18580     onClick : function(e)
18581     {
18582         e.preventDefault();
18583         
18584         if(!this.href.length){
18585             return;
18586         }
18587         
18588         window.location.href = this.href;
18589     },
18590     
18591     startX : 0,
18592     startY : 0,
18593     endX : 0,
18594     endY : 0,
18595     swiping : false,
18596     
18597     onTouchStart : function(e)
18598     {
18599         this.swiping = false;
18600         
18601         this.startX = e.browserEvent.touches[0].clientX;
18602         this.startY = e.browserEvent.touches[0].clientY;
18603     },
18604     
18605     onTouchMove : function(e)
18606     {
18607         this.swiping = true;
18608         
18609         this.endX = e.browserEvent.touches[0].clientX;
18610         this.endY = e.browserEvent.touches[0].clientY;
18611     },
18612     
18613     onTouchEnd : function(e)
18614     {
18615         if(!this.swiping){
18616             this.onClick(e);
18617             return;
18618         }
18619         
18620         var tabGroup = this.parent();
18621         
18622         if(this.endX > this.startX){ // swiping right
18623             tabGroup.showPanelPrev();
18624             return;
18625         }
18626         
18627         if(this.startX > this.endX){ // swiping left
18628             tabGroup.showPanelNext();
18629             return;
18630         }
18631     }
18632     
18633     
18634 });
18635  
18636
18637  
18638
18639  /*
18640  * - LGPL
18641  *
18642  * DateField
18643  * 
18644  */
18645
18646 /**
18647  * @class Roo.bootstrap.DateField
18648  * @extends Roo.bootstrap.Input
18649  * Bootstrap DateField class
18650  * @cfg {Number} weekStart default 0
18651  * @cfg {String} viewMode default empty, (months|years)
18652  * @cfg {String} minViewMode default empty, (months|years)
18653  * @cfg {Number} startDate default -Infinity
18654  * @cfg {Number} endDate default Infinity
18655  * @cfg {Boolean} todayHighlight default false
18656  * @cfg {Boolean} todayBtn default false
18657  * @cfg {Boolean} calendarWeeks default false
18658  * @cfg {Object} daysOfWeekDisabled default empty
18659  * @cfg {Boolean} singleMode default false (true | false)
18660  * 
18661  * @cfg {Boolean} keyboardNavigation default true
18662  * @cfg {String} language default en
18663  * 
18664  * @constructor
18665  * Create a new DateField
18666  * @param {Object} config The config object
18667  */
18668
18669 Roo.bootstrap.DateField = function(config){
18670     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18671      this.addEvents({
18672             /**
18673              * @event show
18674              * Fires when this field show.
18675              * @param {Roo.bootstrap.DateField} this
18676              * @param {Mixed} date The date value
18677              */
18678             show : true,
18679             /**
18680              * @event show
18681              * Fires when this field hide.
18682              * @param {Roo.bootstrap.DateField} this
18683              * @param {Mixed} date The date value
18684              */
18685             hide : true,
18686             /**
18687              * @event select
18688              * Fires when select a date.
18689              * @param {Roo.bootstrap.DateField} this
18690              * @param {Mixed} date The date value
18691              */
18692             select : true,
18693             /**
18694              * @event beforeselect
18695              * Fires when before select a date.
18696              * @param {Roo.bootstrap.DateField} this
18697              * @param {Mixed} date The date value
18698              */
18699             beforeselect : true
18700         });
18701 };
18702
18703 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18704     
18705     /**
18706      * @cfg {String} format
18707      * The default date format string which can be overriden for localization support.  The format must be
18708      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18709      */
18710     format : "m/d/y",
18711     /**
18712      * @cfg {String} altFormats
18713      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18714      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18715      */
18716     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18717     
18718     weekStart : 0,
18719     
18720     viewMode : '',
18721     
18722     minViewMode : '',
18723     
18724     todayHighlight : false,
18725     
18726     todayBtn: false,
18727     
18728     language: 'en',
18729     
18730     keyboardNavigation: true,
18731     
18732     calendarWeeks: false,
18733     
18734     startDate: -Infinity,
18735     
18736     endDate: Infinity,
18737     
18738     daysOfWeekDisabled: [],
18739     
18740     _events: [],
18741     
18742     singleMode : false,
18743     
18744     UTCDate: function()
18745     {
18746         return new Date(Date.UTC.apply(Date, arguments));
18747     },
18748     
18749     UTCToday: function()
18750     {
18751         var today = new Date();
18752         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18753     },
18754     
18755     getDate: function() {
18756             var d = this.getUTCDate();
18757             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18758     },
18759     
18760     getUTCDate: function() {
18761             return this.date;
18762     },
18763     
18764     setDate: function(d) {
18765             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18766     },
18767     
18768     setUTCDate: function(d) {
18769             this.date = d;
18770             this.setValue(this.formatDate(this.date));
18771     },
18772         
18773     onRender: function(ct, position)
18774     {
18775         
18776         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18777         
18778         this.language = this.language || 'en';
18779         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18780         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18781         
18782         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18783         this.format = this.format || 'm/d/y';
18784         this.isInline = false;
18785         this.isInput = true;
18786         this.component = this.el.select('.add-on', true).first() || false;
18787         this.component = (this.component && this.component.length === 0) ? false : this.component;
18788         this.hasInput = this.component && this.inputEl().length;
18789         
18790         if (typeof(this.minViewMode === 'string')) {
18791             switch (this.minViewMode) {
18792                 case 'months':
18793                     this.minViewMode = 1;
18794                     break;
18795                 case 'years':
18796                     this.minViewMode = 2;
18797                     break;
18798                 default:
18799                     this.minViewMode = 0;
18800                     break;
18801             }
18802         }
18803         
18804         if (typeof(this.viewMode === 'string')) {
18805             switch (this.viewMode) {
18806                 case 'months':
18807                     this.viewMode = 1;
18808                     break;
18809                 case 'years':
18810                     this.viewMode = 2;
18811                     break;
18812                 default:
18813                     this.viewMode = 0;
18814                     break;
18815             }
18816         }
18817                 
18818         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18819         
18820 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18821         
18822         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18823         
18824         this.picker().on('mousedown', this.onMousedown, this);
18825         this.picker().on('click', this.onClick, this);
18826         
18827         this.picker().addClass('datepicker-dropdown');
18828         
18829         this.startViewMode = this.viewMode;
18830         
18831         if(this.singleMode){
18832             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18833                 v.setVisibilityMode(Roo.Element.DISPLAY);
18834                 v.hide();
18835             });
18836             
18837             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18838                 v.setStyle('width', '189px');
18839             });
18840         }
18841         
18842         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18843             if(!this.calendarWeeks){
18844                 v.remove();
18845                 return;
18846             }
18847             
18848             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18849             v.attr('colspan', function(i, val){
18850                 return parseInt(val) + 1;
18851             });
18852         });
18853                         
18854         
18855         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18856         
18857         this.setStartDate(this.startDate);
18858         this.setEndDate(this.endDate);
18859         
18860         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18861         
18862         this.fillDow();
18863         this.fillMonths();
18864         this.update();
18865         this.showMode();
18866         
18867         if(this.isInline) {
18868             this.showPopup();
18869         }
18870     },
18871     
18872     picker : function()
18873     {
18874         return this.pickerEl;
18875 //        return this.el.select('.datepicker', true).first();
18876     },
18877     
18878     fillDow: function()
18879     {
18880         var dowCnt = this.weekStart;
18881         
18882         var dow = {
18883             tag: 'tr',
18884             cn: [
18885                 
18886             ]
18887         };
18888         
18889         if(this.calendarWeeks){
18890             dow.cn.push({
18891                 tag: 'th',
18892                 cls: 'cw',
18893                 html: '&nbsp;'
18894             })
18895         }
18896         
18897         while (dowCnt < this.weekStart + 7) {
18898             dow.cn.push({
18899                 tag: 'th',
18900                 cls: 'dow',
18901                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18902             });
18903         }
18904         
18905         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18906     },
18907     
18908     fillMonths: function()
18909     {    
18910         var i = 0;
18911         var months = this.picker().select('>.datepicker-months td', true).first();
18912         
18913         months.dom.innerHTML = '';
18914         
18915         while (i < 12) {
18916             var month = {
18917                 tag: 'span',
18918                 cls: 'month',
18919                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18920             };
18921             
18922             months.createChild(month);
18923         }
18924         
18925     },
18926     
18927     update: function()
18928     {
18929         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;
18930         
18931         if (this.date < this.startDate) {
18932             this.viewDate = new Date(this.startDate);
18933         } else if (this.date > this.endDate) {
18934             this.viewDate = new Date(this.endDate);
18935         } else {
18936             this.viewDate = new Date(this.date);
18937         }
18938         
18939         this.fill();
18940     },
18941     
18942     fill: function() 
18943     {
18944         var d = new Date(this.viewDate),
18945                 year = d.getUTCFullYear(),
18946                 month = d.getUTCMonth(),
18947                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18948                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18949                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18950                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18951                 currentDate = this.date && this.date.valueOf(),
18952                 today = this.UTCToday();
18953         
18954         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18955         
18956 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18957         
18958 //        this.picker.select('>tfoot th.today').
18959 //                                              .text(dates[this.language].today)
18960 //                                              .toggle(this.todayBtn !== false);
18961     
18962         this.updateNavArrows();
18963         this.fillMonths();
18964                                                 
18965         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18966         
18967         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18968          
18969         prevMonth.setUTCDate(day);
18970         
18971         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18972         
18973         var nextMonth = new Date(prevMonth);
18974         
18975         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18976         
18977         nextMonth = nextMonth.valueOf();
18978         
18979         var fillMonths = false;
18980         
18981         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18982         
18983         while(prevMonth.valueOf() <= nextMonth) {
18984             var clsName = '';
18985             
18986             if (prevMonth.getUTCDay() === this.weekStart) {
18987                 if(fillMonths){
18988                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18989                 }
18990                     
18991                 fillMonths = {
18992                     tag: 'tr',
18993                     cn: []
18994                 };
18995                 
18996                 if(this.calendarWeeks){
18997                     // ISO 8601: First week contains first thursday.
18998                     // ISO also states week starts on Monday, but we can be more abstract here.
18999                     var
19000                     // Start of current week: based on weekstart/current date
19001                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19002                     // Thursday of this week
19003                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19004                     // First Thursday of year, year from thursday
19005                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19006                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19007                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19008                     
19009                     fillMonths.cn.push({
19010                         tag: 'td',
19011                         cls: 'cw',
19012                         html: calWeek
19013                     });
19014                 }
19015             }
19016             
19017             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19018                 clsName += ' old';
19019             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19020                 clsName += ' new';
19021             }
19022             if (this.todayHighlight &&
19023                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19024                 prevMonth.getUTCMonth() == today.getMonth() &&
19025                 prevMonth.getUTCDate() == today.getDate()) {
19026                 clsName += ' today';
19027             }
19028             
19029             if (currentDate && prevMonth.valueOf() === currentDate) {
19030                 clsName += ' active';
19031             }
19032             
19033             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19034                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19035                     clsName += ' disabled';
19036             }
19037             
19038             fillMonths.cn.push({
19039                 tag: 'td',
19040                 cls: 'day ' + clsName,
19041                 html: prevMonth.getDate()
19042             });
19043             
19044             prevMonth.setDate(prevMonth.getDate()+1);
19045         }
19046           
19047         var currentYear = this.date && this.date.getUTCFullYear();
19048         var currentMonth = this.date && this.date.getUTCMonth();
19049         
19050         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19051         
19052         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19053             v.removeClass('active');
19054             
19055             if(currentYear === year && k === currentMonth){
19056                 v.addClass('active');
19057             }
19058             
19059             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19060                 v.addClass('disabled');
19061             }
19062             
19063         });
19064         
19065         
19066         year = parseInt(year/10, 10) * 10;
19067         
19068         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19069         
19070         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19071         
19072         year -= 1;
19073         for (var i = -1; i < 11; i++) {
19074             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19075                 tag: 'span',
19076                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19077                 html: year
19078             });
19079             
19080             year += 1;
19081         }
19082     },
19083     
19084     showMode: function(dir) 
19085     {
19086         if (dir) {
19087             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19088         }
19089         
19090         Roo.each(this.picker().select('>div',true).elements, function(v){
19091             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19092             v.hide();
19093         });
19094         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19095     },
19096     
19097     place: function()
19098     {
19099         if(this.isInline) {
19100             return;
19101         }
19102         
19103         this.picker().removeClass(['bottom', 'top']);
19104         
19105         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19106             /*
19107              * place to the top of element!
19108              *
19109              */
19110             
19111             this.picker().addClass('top');
19112             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19113             
19114             return;
19115         }
19116         
19117         this.picker().addClass('bottom');
19118         
19119         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19120     },
19121     
19122     parseDate : function(value)
19123     {
19124         if(!value || value instanceof Date){
19125             return value;
19126         }
19127         var v = Date.parseDate(value, this.format);
19128         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19129             v = Date.parseDate(value, 'Y-m-d');
19130         }
19131         if(!v && this.altFormats){
19132             if(!this.altFormatsArray){
19133                 this.altFormatsArray = this.altFormats.split("|");
19134             }
19135             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19136                 v = Date.parseDate(value, this.altFormatsArray[i]);
19137             }
19138         }
19139         return v;
19140     },
19141     
19142     formatDate : function(date, fmt)
19143     {   
19144         return (!date || !(date instanceof Date)) ?
19145         date : date.dateFormat(fmt || this.format);
19146     },
19147     
19148     onFocus : function()
19149     {
19150         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19151         this.showPopup();
19152     },
19153     
19154     onBlur : function()
19155     {
19156         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19157         
19158         var d = this.inputEl().getValue();
19159         
19160         this.setValue(d);
19161                 
19162         this.hidePopup();
19163     },
19164     
19165     showPopup : function()
19166     {
19167         this.picker().show();
19168         this.update();
19169         this.place();
19170         
19171         this.fireEvent('showpopup', this, this.date);
19172     },
19173     
19174     hidePopup : function()
19175     {
19176         if(this.isInline) {
19177             return;
19178         }
19179         this.picker().hide();
19180         this.viewMode = this.startViewMode;
19181         this.showMode();
19182         
19183         this.fireEvent('hidepopup', this, this.date);
19184         
19185     },
19186     
19187     onMousedown: function(e)
19188     {
19189         e.stopPropagation();
19190         e.preventDefault();
19191     },
19192     
19193     keyup: function(e)
19194     {
19195         Roo.bootstrap.DateField.superclass.keyup.call(this);
19196         this.update();
19197     },
19198
19199     setValue: function(v)
19200     {
19201         if(this.fireEvent('beforeselect', this, v) !== false){
19202             var d = new Date(this.parseDate(v) ).clearTime();
19203         
19204             if(isNaN(d.getTime())){
19205                 this.date = this.viewDate = '';
19206                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19207                 return;
19208             }
19209
19210             v = this.formatDate(d);
19211
19212             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19213
19214             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19215
19216             this.update();
19217
19218             this.fireEvent('select', this, this.date);
19219         }
19220     },
19221     
19222     getValue: function()
19223     {
19224         return this.formatDate(this.date);
19225     },
19226     
19227     fireKey: function(e)
19228     {
19229         if (!this.picker().isVisible()){
19230             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19231                 this.showPopup();
19232             }
19233             return;
19234         }
19235         
19236         var dateChanged = false,
19237         dir, day, month,
19238         newDate, newViewDate;
19239         
19240         switch(e.keyCode){
19241             case 27: // escape
19242                 this.hidePopup();
19243                 e.preventDefault();
19244                 break;
19245             case 37: // left
19246             case 39: // right
19247                 if (!this.keyboardNavigation) {
19248                     break;
19249                 }
19250                 dir = e.keyCode == 37 ? -1 : 1;
19251                 
19252                 if (e.ctrlKey){
19253                     newDate = this.moveYear(this.date, dir);
19254                     newViewDate = this.moveYear(this.viewDate, dir);
19255                 } else if (e.shiftKey){
19256                     newDate = this.moveMonth(this.date, dir);
19257                     newViewDate = this.moveMonth(this.viewDate, dir);
19258                 } else {
19259                     newDate = new Date(this.date);
19260                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19261                     newViewDate = new Date(this.viewDate);
19262                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19263                 }
19264                 if (this.dateWithinRange(newDate)){
19265                     this.date = newDate;
19266                     this.viewDate = newViewDate;
19267                     this.setValue(this.formatDate(this.date));
19268 //                    this.update();
19269                     e.preventDefault();
19270                     dateChanged = true;
19271                 }
19272                 break;
19273             case 38: // up
19274             case 40: // down
19275                 if (!this.keyboardNavigation) {
19276                     break;
19277                 }
19278                 dir = e.keyCode == 38 ? -1 : 1;
19279                 if (e.ctrlKey){
19280                     newDate = this.moveYear(this.date, dir);
19281                     newViewDate = this.moveYear(this.viewDate, dir);
19282                 } else if (e.shiftKey){
19283                     newDate = this.moveMonth(this.date, dir);
19284                     newViewDate = this.moveMonth(this.viewDate, dir);
19285                 } else {
19286                     newDate = new Date(this.date);
19287                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19288                     newViewDate = new Date(this.viewDate);
19289                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19290                 }
19291                 if (this.dateWithinRange(newDate)){
19292                     this.date = newDate;
19293                     this.viewDate = newViewDate;
19294                     this.setValue(this.formatDate(this.date));
19295 //                    this.update();
19296                     e.preventDefault();
19297                     dateChanged = true;
19298                 }
19299                 break;
19300             case 13: // enter
19301                 this.setValue(this.formatDate(this.date));
19302                 this.hidePopup();
19303                 e.preventDefault();
19304                 break;
19305             case 9: // tab
19306                 this.setValue(this.formatDate(this.date));
19307                 this.hidePopup();
19308                 break;
19309             case 16: // shift
19310             case 17: // ctrl
19311             case 18: // alt
19312                 break;
19313             default :
19314                 this.hidePopup();
19315                 
19316         }
19317     },
19318     
19319     
19320     onClick: function(e) 
19321     {
19322         e.stopPropagation();
19323         e.preventDefault();
19324         
19325         var target = e.getTarget();
19326         
19327         if(target.nodeName.toLowerCase() === 'i'){
19328             target = Roo.get(target).dom.parentNode;
19329         }
19330         
19331         var nodeName = target.nodeName;
19332         var className = target.className;
19333         var html = target.innerHTML;
19334         //Roo.log(nodeName);
19335         
19336         switch(nodeName.toLowerCase()) {
19337             case 'th':
19338                 switch(className) {
19339                     case 'switch':
19340                         this.showMode(1);
19341                         break;
19342                     case 'prev':
19343                     case 'next':
19344                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19345                         switch(this.viewMode){
19346                                 case 0:
19347                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19348                                         break;
19349                                 case 1:
19350                                 case 2:
19351                                         this.viewDate = this.moveYear(this.viewDate, dir);
19352                                         break;
19353                         }
19354                         this.fill();
19355                         break;
19356                     case 'today':
19357                         var date = new Date();
19358                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19359 //                        this.fill()
19360                         this.setValue(this.formatDate(this.date));
19361                         
19362                         this.hidePopup();
19363                         break;
19364                 }
19365                 break;
19366             case 'span':
19367                 if (className.indexOf('disabled') < 0) {
19368                     this.viewDate.setUTCDate(1);
19369                     if (className.indexOf('month') > -1) {
19370                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19371                     } else {
19372                         var year = parseInt(html, 10) || 0;
19373                         this.viewDate.setUTCFullYear(year);
19374                         
19375                     }
19376                     
19377                     if(this.singleMode){
19378                         this.setValue(this.formatDate(this.viewDate));
19379                         this.hidePopup();
19380                         return;
19381                     }
19382                     
19383                     this.showMode(-1);
19384                     this.fill();
19385                 }
19386                 break;
19387                 
19388             case 'td':
19389                 //Roo.log(className);
19390                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19391                     var day = parseInt(html, 10) || 1;
19392                     var year = this.viewDate.getUTCFullYear(),
19393                         month = this.viewDate.getUTCMonth();
19394
19395                     if (className.indexOf('old') > -1) {
19396                         if(month === 0 ){
19397                             month = 11;
19398                             year -= 1;
19399                         }else{
19400                             month -= 1;
19401                         }
19402                     } else if (className.indexOf('new') > -1) {
19403                         if (month == 11) {
19404                             month = 0;
19405                             year += 1;
19406                         } else {
19407                             month += 1;
19408                         }
19409                     }
19410                     //Roo.log([year,month,day]);
19411                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19412                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19413 //                    this.fill();
19414                     //Roo.log(this.formatDate(this.date));
19415                     this.setValue(this.formatDate(this.date));
19416                     this.hidePopup();
19417                 }
19418                 break;
19419         }
19420     },
19421     
19422     setStartDate: function(startDate)
19423     {
19424         this.startDate = startDate || -Infinity;
19425         if (this.startDate !== -Infinity) {
19426             this.startDate = this.parseDate(this.startDate);
19427         }
19428         this.update();
19429         this.updateNavArrows();
19430     },
19431
19432     setEndDate: function(endDate)
19433     {
19434         this.endDate = endDate || Infinity;
19435         if (this.endDate !== Infinity) {
19436             this.endDate = this.parseDate(this.endDate);
19437         }
19438         this.update();
19439         this.updateNavArrows();
19440     },
19441     
19442     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19443     {
19444         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19445         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19446             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19447         }
19448         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19449             return parseInt(d, 10);
19450         });
19451         this.update();
19452         this.updateNavArrows();
19453     },
19454     
19455     updateNavArrows: function() 
19456     {
19457         if(this.singleMode){
19458             return;
19459         }
19460         
19461         var d = new Date(this.viewDate),
19462         year = d.getUTCFullYear(),
19463         month = d.getUTCMonth();
19464         
19465         Roo.each(this.picker().select('.prev', true).elements, function(v){
19466             v.show();
19467             switch (this.viewMode) {
19468                 case 0:
19469
19470                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19471                         v.hide();
19472                     }
19473                     break;
19474                 case 1:
19475                 case 2:
19476                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19477                         v.hide();
19478                     }
19479                     break;
19480             }
19481         });
19482         
19483         Roo.each(this.picker().select('.next', true).elements, function(v){
19484             v.show();
19485             switch (this.viewMode) {
19486                 case 0:
19487
19488                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19489                         v.hide();
19490                     }
19491                     break;
19492                 case 1:
19493                 case 2:
19494                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19495                         v.hide();
19496                     }
19497                     break;
19498             }
19499         })
19500     },
19501     
19502     moveMonth: function(date, dir)
19503     {
19504         if (!dir) {
19505             return date;
19506         }
19507         var new_date = new Date(date.valueOf()),
19508         day = new_date.getUTCDate(),
19509         month = new_date.getUTCMonth(),
19510         mag = Math.abs(dir),
19511         new_month, test;
19512         dir = dir > 0 ? 1 : -1;
19513         if (mag == 1){
19514             test = dir == -1
19515             // If going back one month, make sure month is not current month
19516             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19517             ? function(){
19518                 return new_date.getUTCMonth() == month;
19519             }
19520             // If going forward one month, make sure month is as expected
19521             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19522             : function(){
19523                 return new_date.getUTCMonth() != new_month;
19524             };
19525             new_month = month + dir;
19526             new_date.setUTCMonth(new_month);
19527             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19528             if (new_month < 0 || new_month > 11) {
19529                 new_month = (new_month + 12) % 12;
19530             }
19531         } else {
19532             // For magnitudes >1, move one month at a time...
19533             for (var i=0; i<mag; i++) {
19534                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19535                 new_date = this.moveMonth(new_date, dir);
19536             }
19537             // ...then reset the day, keeping it in the new month
19538             new_month = new_date.getUTCMonth();
19539             new_date.setUTCDate(day);
19540             test = function(){
19541                 return new_month != new_date.getUTCMonth();
19542             };
19543         }
19544         // Common date-resetting loop -- if date is beyond end of month, make it
19545         // end of month
19546         while (test()){
19547             new_date.setUTCDate(--day);
19548             new_date.setUTCMonth(new_month);
19549         }
19550         return new_date;
19551     },
19552
19553     moveYear: function(date, dir)
19554     {
19555         return this.moveMonth(date, dir*12);
19556     },
19557
19558     dateWithinRange: function(date)
19559     {
19560         return date >= this.startDate && date <= this.endDate;
19561     },
19562
19563     
19564     remove: function() 
19565     {
19566         this.picker().remove();
19567     },
19568     
19569     validateValue : function(value)
19570     {
19571         if(this.getVisibilityEl().hasClass('hidden')){
19572             return true;
19573         }
19574         
19575         if(value.length < 1)  {
19576             if(this.allowBlank){
19577                 return true;
19578             }
19579             return false;
19580         }
19581         
19582         if(value.length < this.minLength){
19583             return false;
19584         }
19585         if(value.length > this.maxLength){
19586             return false;
19587         }
19588         if(this.vtype){
19589             var vt = Roo.form.VTypes;
19590             if(!vt[this.vtype](value, this)){
19591                 return false;
19592             }
19593         }
19594         if(typeof this.validator == "function"){
19595             var msg = this.validator(value);
19596             if(msg !== true){
19597                 return false;
19598             }
19599         }
19600         
19601         if(this.regex && !this.regex.test(value)){
19602             return false;
19603         }
19604         
19605         if(typeof(this.parseDate(value)) == 'undefined'){
19606             return false;
19607         }
19608         
19609         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19610             return false;
19611         }      
19612         
19613         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19614             return false;
19615         } 
19616         
19617         
19618         return true;
19619     },
19620     
19621     reset : function()
19622     {
19623         this.date = this.viewDate = '';
19624         
19625         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19626     }
19627    
19628 });
19629
19630 Roo.apply(Roo.bootstrap.DateField,  {
19631     
19632     head : {
19633         tag: 'thead',
19634         cn: [
19635         {
19636             tag: 'tr',
19637             cn: [
19638             {
19639                 tag: 'th',
19640                 cls: 'prev',
19641                 html: '<i class="fa fa-arrow-left"/>'
19642             },
19643             {
19644                 tag: 'th',
19645                 cls: 'switch',
19646                 colspan: '5'
19647             },
19648             {
19649                 tag: 'th',
19650                 cls: 'next',
19651                 html: '<i class="fa fa-arrow-right"/>'
19652             }
19653
19654             ]
19655         }
19656         ]
19657     },
19658     
19659     content : {
19660         tag: 'tbody',
19661         cn: [
19662         {
19663             tag: 'tr',
19664             cn: [
19665             {
19666                 tag: 'td',
19667                 colspan: '7'
19668             }
19669             ]
19670         }
19671         ]
19672     },
19673     
19674     footer : {
19675         tag: 'tfoot',
19676         cn: [
19677         {
19678             tag: 'tr',
19679             cn: [
19680             {
19681                 tag: 'th',
19682                 colspan: '7',
19683                 cls: 'today'
19684             }
19685                     
19686             ]
19687         }
19688         ]
19689     },
19690     
19691     dates:{
19692         en: {
19693             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19694             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19695             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19696             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19697             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19698             today: "Today"
19699         }
19700     },
19701     
19702     modes: [
19703     {
19704         clsName: 'days',
19705         navFnc: 'Month',
19706         navStep: 1
19707     },
19708     {
19709         clsName: 'months',
19710         navFnc: 'FullYear',
19711         navStep: 1
19712     },
19713     {
19714         clsName: 'years',
19715         navFnc: 'FullYear',
19716         navStep: 10
19717     }]
19718 });
19719
19720 Roo.apply(Roo.bootstrap.DateField,  {
19721   
19722     template : {
19723         tag: 'div',
19724         cls: 'datepicker dropdown-menu roo-dynamic',
19725         cn: [
19726         {
19727             tag: 'div',
19728             cls: 'datepicker-days',
19729             cn: [
19730             {
19731                 tag: 'table',
19732                 cls: 'table-condensed',
19733                 cn:[
19734                 Roo.bootstrap.DateField.head,
19735                 {
19736                     tag: 'tbody'
19737                 },
19738                 Roo.bootstrap.DateField.footer
19739                 ]
19740             }
19741             ]
19742         },
19743         {
19744             tag: 'div',
19745             cls: 'datepicker-months',
19746             cn: [
19747             {
19748                 tag: 'table',
19749                 cls: 'table-condensed',
19750                 cn:[
19751                 Roo.bootstrap.DateField.head,
19752                 Roo.bootstrap.DateField.content,
19753                 Roo.bootstrap.DateField.footer
19754                 ]
19755             }
19756             ]
19757         },
19758         {
19759             tag: 'div',
19760             cls: 'datepicker-years',
19761             cn: [
19762             {
19763                 tag: 'table',
19764                 cls: 'table-condensed',
19765                 cn:[
19766                 Roo.bootstrap.DateField.head,
19767                 Roo.bootstrap.DateField.content,
19768                 Roo.bootstrap.DateField.footer
19769                 ]
19770             }
19771             ]
19772         }
19773         ]
19774     }
19775 });
19776
19777  
19778
19779  /*
19780  * - LGPL
19781  *
19782  * TimeField
19783  * 
19784  */
19785
19786 /**
19787  * @class Roo.bootstrap.TimeField
19788  * @extends Roo.bootstrap.Input
19789  * Bootstrap DateField class
19790  * 
19791  * 
19792  * @constructor
19793  * Create a new TimeField
19794  * @param {Object} config The config object
19795  */
19796
19797 Roo.bootstrap.TimeField = function(config){
19798     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19799     this.addEvents({
19800             /**
19801              * @event show
19802              * Fires when this field show.
19803              * @param {Roo.bootstrap.DateField} thisthis
19804              * @param {Mixed} date The date value
19805              */
19806             show : true,
19807             /**
19808              * @event show
19809              * Fires when this field hide.
19810              * @param {Roo.bootstrap.DateField} this
19811              * @param {Mixed} date The date value
19812              */
19813             hide : true,
19814             /**
19815              * @event select
19816              * Fires when select a date.
19817              * @param {Roo.bootstrap.DateField} this
19818              * @param {Mixed} date The date value
19819              */
19820             select : true
19821         });
19822 };
19823
19824 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19825     
19826     /**
19827      * @cfg {String} format
19828      * The default time format string which can be overriden for localization support.  The format must be
19829      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19830      */
19831     format : "H:i",
19832        
19833     onRender: function(ct, position)
19834     {
19835         
19836         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19837                 
19838         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19839         
19840         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19841         
19842         this.pop = this.picker().select('>.datepicker-time',true).first();
19843         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19844         
19845         this.picker().on('mousedown', this.onMousedown, this);
19846         this.picker().on('click', this.onClick, this);
19847         
19848         this.picker().addClass('datepicker-dropdown');
19849     
19850         this.fillTime();
19851         this.update();
19852             
19853         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19854         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19855         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19856         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19857         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19858         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19859
19860     },
19861     
19862     fireKey: function(e){
19863         if (!this.picker().isVisible()){
19864             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19865                 this.show();
19866             }
19867             return;
19868         }
19869
19870         e.preventDefault();
19871         
19872         switch(e.keyCode){
19873             case 27: // escape
19874                 this.hide();
19875                 break;
19876             case 37: // left
19877             case 39: // right
19878                 this.onTogglePeriod();
19879                 break;
19880             case 38: // up
19881                 this.onIncrementMinutes();
19882                 break;
19883             case 40: // down
19884                 this.onDecrementMinutes();
19885                 break;
19886             case 13: // enter
19887             case 9: // tab
19888                 this.setTime();
19889                 break;
19890         }
19891     },
19892     
19893     onClick: function(e) {
19894         e.stopPropagation();
19895         e.preventDefault();
19896     },
19897     
19898     picker : function()
19899     {
19900         return this.el.select('.datepicker', true).first();
19901     },
19902     
19903     fillTime: function()
19904     {    
19905         var time = this.pop.select('tbody', true).first();
19906         
19907         time.dom.innerHTML = '';
19908         
19909         time.createChild({
19910             tag: 'tr',
19911             cn: [
19912                 {
19913                     tag: 'td',
19914                     cn: [
19915                         {
19916                             tag: 'a',
19917                             href: '#',
19918                             cls: 'btn',
19919                             cn: [
19920                                 {
19921                                     tag: 'span',
19922                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19923                                 }
19924                             ]
19925                         } 
19926                     ]
19927                 },
19928                 {
19929                     tag: 'td',
19930                     cls: 'separator'
19931                 },
19932                 {
19933                     tag: 'td',
19934                     cn: [
19935                         {
19936                             tag: 'a',
19937                             href: '#',
19938                             cls: 'btn',
19939                             cn: [
19940                                 {
19941                                     tag: 'span',
19942                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19943                                 }
19944                             ]
19945                         }
19946                     ]
19947                 },
19948                 {
19949                     tag: 'td',
19950                     cls: 'separator'
19951                 }
19952             ]
19953         });
19954         
19955         time.createChild({
19956             tag: 'tr',
19957             cn: [
19958                 {
19959                     tag: 'td',
19960                     cn: [
19961                         {
19962                             tag: 'span',
19963                             cls: 'timepicker-hour',
19964                             html: '00'
19965                         }  
19966                     ]
19967                 },
19968                 {
19969                     tag: 'td',
19970                     cls: 'separator',
19971                     html: ':'
19972                 },
19973                 {
19974                     tag: 'td',
19975                     cn: [
19976                         {
19977                             tag: 'span',
19978                             cls: 'timepicker-minute',
19979                             html: '00'
19980                         }  
19981                     ]
19982                 },
19983                 {
19984                     tag: 'td',
19985                     cls: 'separator'
19986                 },
19987                 {
19988                     tag: 'td',
19989                     cn: [
19990                         {
19991                             tag: 'button',
19992                             type: 'button',
19993                             cls: 'btn btn-primary period',
19994                             html: 'AM'
19995                             
19996                         }
19997                     ]
19998                 }
19999             ]
20000         });
20001         
20002         time.createChild({
20003             tag: 'tr',
20004             cn: [
20005                 {
20006                     tag: 'td',
20007                     cn: [
20008                         {
20009                             tag: 'a',
20010                             href: '#',
20011                             cls: 'btn',
20012                             cn: [
20013                                 {
20014                                     tag: 'span',
20015                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20016                                 }
20017                             ]
20018                         }
20019                     ]
20020                 },
20021                 {
20022                     tag: 'td',
20023                     cls: 'separator'
20024                 },
20025                 {
20026                     tag: 'td',
20027                     cn: [
20028                         {
20029                             tag: 'a',
20030                             href: '#',
20031                             cls: 'btn',
20032                             cn: [
20033                                 {
20034                                     tag: 'span',
20035                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20036                                 }
20037                             ]
20038                         }
20039                     ]
20040                 },
20041                 {
20042                     tag: 'td',
20043                     cls: 'separator'
20044                 }
20045             ]
20046         });
20047         
20048     },
20049     
20050     update: function()
20051     {
20052         
20053         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20054         
20055         this.fill();
20056     },
20057     
20058     fill: function() 
20059     {
20060         var hours = this.time.getHours();
20061         var minutes = this.time.getMinutes();
20062         var period = 'AM';
20063         
20064         if(hours > 11){
20065             period = 'PM';
20066         }
20067         
20068         if(hours == 0){
20069             hours = 12;
20070         }
20071         
20072         
20073         if(hours > 12){
20074             hours = hours - 12;
20075         }
20076         
20077         if(hours < 10){
20078             hours = '0' + hours;
20079         }
20080         
20081         if(minutes < 10){
20082             minutes = '0' + minutes;
20083         }
20084         
20085         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20086         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20087         this.pop.select('button', true).first().dom.innerHTML = period;
20088         
20089     },
20090     
20091     place: function()
20092     {   
20093         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20094         
20095         var cls = ['bottom'];
20096         
20097         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20098             cls.pop();
20099             cls.push('top');
20100         }
20101         
20102         cls.push('right');
20103         
20104         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20105             cls.pop();
20106             cls.push('left');
20107         }
20108         
20109         this.picker().addClass(cls.join('-'));
20110         
20111         var _this = this;
20112         
20113         Roo.each(cls, function(c){
20114             if(c == 'bottom'){
20115                 _this.picker().setTop(_this.inputEl().getHeight());
20116                 return;
20117             }
20118             if(c == 'top'){
20119                 _this.picker().setTop(0 - _this.picker().getHeight());
20120                 return;
20121             }
20122             
20123             if(c == 'left'){
20124                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20125                 return;
20126             }
20127             if(c == 'right'){
20128                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20129                 return;
20130             }
20131         });
20132         
20133     },
20134   
20135     onFocus : function()
20136     {
20137         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20138         this.show();
20139     },
20140     
20141     onBlur : function()
20142     {
20143         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20144         this.hide();
20145     },
20146     
20147     show : function()
20148     {
20149         this.picker().show();
20150         this.pop.show();
20151         this.update();
20152         this.place();
20153         
20154         this.fireEvent('show', this, this.date);
20155     },
20156     
20157     hide : function()
20158     {
20159         this.picker().hide();
20160         this.pop.hide();
20161         
20162         this.fireEvent('hide', this, this.date);
20163     },
20164     
20165     setTime : function()
20166     {
20167         this.hide();
20168         this.setValue(this.time.format(this.format));
20169         
20170         this.fireEvent('select', this, this.date);
20171         
20172         
20173     },
20174     
20175     onMousedown: function(e){
20176         e.stopPropagation();
20177         e.preventDefault();
20178     },
20179     
20180     onIncrementHours: function()
20181     {
20182         Roo.log('onIncrementHours');
20183         this.time = this.time.add(Date.HOUR, 1);
20184         this.update();
20185         
20186     },
20187     
20188     onDecrementHours: function()
20189     {
20190         Roo.log('onDecrementHours');
20191         this.time = this.time.add(Date.HOUR, -1);
20192         this.update();
20193     },
20194     
20195     onIncrementMinutes: function()
20196     {
20197         Roo.log('onIncrementMinutes');
20198         this.time = this.time.add(Date.MINUTE, 1);
20199         this.update();
20200     },
20201     
20202     onDecrementMinutes: function()
20203     {
20204         Roo.log('onDecrementMinutes');
20205         this.time = this.time.add(Date.MINUTE, -1);
20206         this.update();
20207     },
20208     
20209     onTogglePeriod: function()
20210     {
20211         Roo.log('onTogglePeriod');
20212         this.time = this.time.add(Date.HOUR, 12);
20213         this.update();
20214     }
20215     
20216    
20217 });
20218
20219 Roo.apply(Roo.bootstrap.TimeField,  {
20220     
20221     content : {
20222         tag: 'tbody',
20223         cn: [
20224             {
20225                 tag: 'tr',
20226                 cn: [
20227                 {
20228                     tag: 'td',
20229                     colspan: '7'
20230                 }
20231                 ]
20232             }
20233         ]
20234     },
20235     
20236     footer : {
20237         tag: 'tfoot',
20238         cn: [
20239             {
20240                 tag: 'tr',
20241                 cn: [
20242                 {
20243                     tag: 'th',
20244                     colspan: '7',
20245                     cls: '',
20246                     cn: [
20247                         {
20248                             tag: 'button',
20249                             cls: 'btn btn-info ok',
20250                             html: 'OK'
20251                         }
20252                     ]
20253                 }
20254
20255                 ]
20256             }
20257         ]
20258     }
20259 });
20260
20261 Roo.apply(Roo.bootstrap.TimeField,  {
20262   
20263     template : {
20264         tag: 'div',
20265         cls: 'datepicker dropdown-menu',
20266         cn: [
20267             {
20268                 tag: 'div',
20269                 cls: 'datepicker-time',
20270                 cn: [
20271                 {
20272                     tag: 'table',
20273                     cls: 'table-condensed',
20274                     cn:[
20275                     Roo.bootstrap.TimeField.content,
20276                     Roo.bootstrap.TimeField.footer
20277                     ]
20278                 }
20279                 ]
20280             }
20281         ]
20282     }
20283 });
20284
20285  
20286
20287  /*
20288  * - LGPL
20289  *
20290  * MonthField
20291  * 
20292  */
20293
20294 /**
20295  * @class Roo.bootstrap.MonthField
20296  * @extends Roo.bootstrap.Input
20297  * Bootstrap MonthField class
20298  * 
20299  * @cfg {String} language default en
20300  * 
20301  * @constructor
20302  * Create a new MonthField
20303  * @param {Object} config The config object
20304  */
20305
20306 Roo.bootstrap.MonthField = function(config){
20307     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20308     
20309     this.addEvents({
20310         /**
20311          * @event show
20312          * Fires when this field show.
20313          * @param {Roo.bootstrap.MonthField} this
20314          * @param {Mixed} date The date value
20315          */
20316         show : true,
20317         /**
20318          * @event show
20319          * Fires when this field hide.
20320          * @param {Roo.bootstrap.MonthField} this
20321          * @param {Mixed} date The date value
20322          */
20323         hide : true,
20324         /**
20325          * @event select
20326          * Fires when select a date.
20327          * @param {Roo.bootstrap.MonthField} this
20328          * @param {String} oldvalue The old value
20329          * @param {String} newvalue The new value
20330          */
20331         select : true
20332     });
20333 };
20334
20335 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20336     
20337     onRender: function(ct, position)
20338     {
20339         
20340         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20341         
20342         this.language = this.language || 'en';
20343         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20344         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20345         
20346         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20347         this.isInline = false;
20348         this.isInput = true;
20349         this.component = this.el.select('.add-on', true).first() || false;
20350         this.component = (this.component && this.component.length === 0) ? false : this.component;
20351         this.hasInput = this.component && this.inputEL().length;
20352         
20353         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20354         
20355         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20356         
20357         this.picker().on('mousedown', this.onMousedown, this);
20358         this.picker().on('click', this.onClick, this);
20359         
20360         this.picker().addClass('datepicker-dropdown');
20361         
20362         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20363             v.setStyle('width', '189px');
20364         });
20365         
20366         this.fillMonths();
20367         
20368         this.update();
20369         
20370         if(this.isInline) {
20371             this.show();
20372         }
20373         
20374     },
20375     
20376     setValue: function(v, suppressEvent)
20377     {   
20378         var o = this.getValue();
20379         
20380         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20381         
20382         this.update();
20383
20384         if(suppressEvent !== true){
20385             this.fireEvent('select', this, o, v);
20386         }
20387         
20388     },
20389     
20390     getValue: function()
20391     {
20392         return this.value;
20393     },
20394     
20395     onClick: function(e) 
20396     {
20397         e.stopPropagation();
20398         e.preventDefault();
20399         
20400         var target = e.getTarget();
20401         
20402         if(target.nodeName.toLowerCase() === 'i'){
20403             target = Roo.get(target).dom.parentNode;
20404         }
20405         
20406         var nodeName = target.nodeName;
20407         var className = target.className;
20408         var html = target.innerHTML;
20409         
20410         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20411             return;
20412         }
20413         
20414         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20415         
20416         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20417         
20418         this.hide();
20419                         
20420     },
20421     
20422     picker : function()
20423     {
20424         return this.pickerEl;
20425     },
20426     
20427     fillMonths: function()
20428     {    
20429         var i = 0;
20430         var months = this.picker().select('>.datepicker-months td', true).first();
20431         
20432         months.dom.innerHTML = '';
20433         
20434         while (i < 12) {
20435             var month = {
20436                 tag: 'span',
20437                 cls: 'month',
20438                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20439             };
20440             
20441             months.createChild(month);
20442         }
20443         
20444     },
20445     
20446     update: function()
20447     {
20448         var _this = this;
20449         
20450         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20451             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20452         }
20453         
20454         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20455             e.removeClass('active');
20456             
20457             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20458                 e.addClass('active');
20459             }
20460         })
20461     },
20462     
20463     place: function()
20464     {
20465         if(this.isInline) {
20466             return;
20467         }
20468         
20469         this.picker().removeClass(['bottom', 'top']);
20470         
20471         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20472             /*
20473              * place to the top of element!
20474              *
20475              */
20476             
20477             this.picker().addClass('top');
20478             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20479             
20480             return;
20481         }
20482         
20483         this.picker().addClass('bottom');
20484         
20485         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20486     },
20487     
20488     onFocus : function()
20489     {
20490         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20491         this.show();
20492     },
20493     
20494     onBlur : function()
20495     {
20496         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20497         
20498         var d = this.inputEl().getValue();
20499         
20500         this.setValue(d);
20501                 
20502         this.hide();
20503     },
20504     
20505     show : function()
20506     {
20507         this.picker().show();
20508         this.picker().select('>.datepicker-months', true).first().show();
20509         this.update();
20510         this.place();
20511         
20512         this.fireEvent('show', this, this.date);
20513     },
20514     
20515     hide : function()
20516     {
20517         if(this.isInline) {
20518             return;
20519         }
20520         this.picker().hide();
20521         this.fireEvent('hide', this, this.date);
20522         
20523     },
20524     
20525     onMousedown: function(e)
20526     {
20527         e.stopPropagation();
20528         e.preventDefault();
20529     },
20530     
20531     keyup: function(e)
20532     {
20533         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20534         this.update();
20535     },
20536
20537     fireKey: function(e)
20538     {
20539         if (!this.picker().isVisible()){
20540             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20541                 this.show();
20542             }
20543             return;
20544         }
20545         
20546         var dir;
20547         
20548         switch(e.keyCode){
20549             case 27: // escape
20550                 this.hide();
20551                 e.preventDefault();
20552                 break;
20553             case 37: // left
20554             case 39: // right
20555                 dir = e.keyCode == 37 ? -1 : 1;
20556                 
20557                 this.vIndex = this.vIndex + dir;
20558                 
20559                 if(this.vIndex < 0){
20560                     this.vIndex = 0;
20561                 }
20562                 
20563                 if(this.vIndex > 11){
20564                     this.vIndex = 11;
20565                 }
20566                 
20567                 if(isNaN(this.vIndex)){
20568                     this.vIndex = 0;
20569                 }
20570                 
20571                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20572                 
20573                 break;
20574             case 38: // up
20575             case 40: // down
20576                 
20577                 dir = e.keyCode == 38 ? -1 : 1;
20578                 
20579                 this.vIndex = this.vIndex + dir * 4;
20580                 
20581                 if(this.vIndex < 0){
20582                     this.vIndex = 0;
20583                 }
20584                 
20585                 if(this.vIndex > 11){
20586                     this.vIndex = 11;
20587                 }
20588                 
20589                 if(isNaN(this.vIndex)){
20590                     this.vIndex = 0;
20591                 }
20592                 
20593                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20594                 break;
20595                 
20596             case 13: // enter
20597                 
20598                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20599                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20600                 }
20601                 
20602                 this.hide();
20603                 e.preventDefault();
20604                 break;
20605             case 9: // tab
20606                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20607                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20608                 }
20609                 this.hide();
20610                 break;
20611             case 16: // shift
20612             case 17: // ctrl
20613             case 18: // alt
20614                 break;
20615             default :
20616                 this.hide();
20617                 
20618         }
20619     },
20620     
20621     remove: function() 
20622     {
20623         this.picker().remove();
20624     }
20625    
20626 });
20627
20628 Roo.apply(Roo.bootstrap.MonthField,  {
20629     
20630     content : {
20631         tag: 'tbody',
20632         cn: [
20633         {
20634             tag: 'tr',
20635             cn: [
20636             {
20637                 tag: 'td',
20638                 colspan: '7'
20639             }
20640             ]
20641         }
20642         ]
20643     },
20644     
20645     dates:{
20646         en: {
20647             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20648             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20649         }
20650     }
20651 });
20652
20653 Roo.apply(Roo.bootstrap.MonthField,  {
20654   
20655     template : {
20656         tag: 'div',
20657         cls: 'datepicker dropdown-menu roo-dynamic',
20658         cn: [
20659             {
20660                 tag: 'div',
20661                 cls: 'datepicker-months',
20662                 cn: [
20663                 {
20664                     tag: 'table',
20665                     cls: 'table-condensed',
20666                     cn:[
20667                         Roo.bootstrap.DateField.content
20668                     ]
20669                 }
20670                 ]
20671             }
20672         ]
20673     }
20674 });
20675
20676  
20677
20678  
20679  /*
20680  * - LGPL
20681  *
20682  * CheckBox
20683  * 
20684  */
20685
20686 /**
20687  * @class Roo.bootstrap.CheckBox
20688  * @extends Roo.bootstrap.Input
20689  * Bootstrap CheckBox class
20690  * 
20691  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20692  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20693  * @cfg {String} boxLabel The text that appears beside the checkbox
20694  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20695  * @cfg {Boolean} checked initnal the element
20696  * @cfg {Boolean} inline inline the element (default false)
20697  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20698  * @cfg {String} tooltip label tooltip
20699  * 
20700  * @constructor
20701  * Create a new CheckBox
20702  * @param {Object} config The config object
20703  */
20704
20705 Roo.bootstrap.CheckBox = function(config){
20706     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20707    
20708     this.addEvents({
20709         /**
20710         * @event check
20711         * Fires when the element is checked or unchecked.
20712         * @param {Roo.bootstrap.CheckBox} this This input
20713         * @param {Boolean} checked The new checked value
20714         */
20715        check : true,
20716        /**
20717         * @event click
20718         * Fires when the element is click.
20719         * @param {Roo.bootstrap.CheckBox} this This input
20720         */
20721        click : true
20722     });
20723     
20724 };
20725
20726 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20727   
20728     inputType: 'checkbox',
20729     inputValue: 1,
20730     valueOff: 0,
20731     boxLabel: false,
20732     checked: false,
20733     weight : false,
20734     inline: false,
20735     tooltip : '',
20736     
20737     getAutoCreate : function()
20738     {
20739         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20740         
20741         var id = Roo.id();
20742         
20743         var cfg = {};
20744         
20745         cfg.cls = 'form-group ' + this.inputType; //input-group
20746         
20747         if(this.inline){
20748             cfg.cls += ' ' + this.inputType + '-inline';
20749         }
20750         
20751         var input =  {
20752             tag: 'input',
20753             id : id,
20754             type : this.inputType,
20755             value : this.inputValue,
20756             cls : 'roo-' + this.inputType, //'form-box',
20757             placeholder : this.placeholder || ''
20758             
20759         };
20760         
20761         if(this.inputType != 'radio'){
20762             var hidden =  {
20763                 tag: 'input',
20764                 type : 'hidden',
20765                 cls : 'roo-hidden-value',
20766                 value : this.checked ? this.inputValue : this.valueOff
20767             };
20768         }
20769         
20770             
20771         if (this.weight) { // Validity check?
20772             cfg.cls += " " + this.inputType + "-" + this.weight;
20773         }
20774         
20775         if (this.disabled) {
20776             input.disabled=true;
20777         }
20778         
20779         if(this.checked){
20780             input.checked = this.checked;
20781         }
20782         
20783         if (this.name) {
20784             
20785             input.name = this.name;
20786             
20787             if(this.inputType != 'radio'){
20788                 hidden.name = this.name;
20789                 input.name = '_hidden_' + this.name;
20790             }
20791         }
20792         
20793         if (this.size) {
20794             input.cls += ' input-' + this.size;
20795         }
20796         
20797         var settings=this;
20798         
20799         ['xs','sm','md','lg'].map(function(size){
20800             if (settings[size]) {
20801                 cfg.cls += ' col-' + size + '-' + settings[size];
20802             }
20803         });
20804         
20805         var inputblock = input;
20806          
20807         if (this.before || this.after) {
20808             
20809             inputblock = {
20810                 cls : 'input-group',
20811                 cn :  [] 
20812             };
20813             
20814             if (this.before) {
20815                 inputblock.cn.push({
20816                     tag :'span',
20817                     cls : 'input-group-addon',
20818                     html : this.before
20819                 });
20820             }
20821             
20822             inputblock.cn.push(input);
20823             
20824             if(this.inputType != 'radio'){
20825                 inputblock.cn.push(hidden);
20826             }
20827             
20828             if (this.after) {
20829                 inputblock.cn.push({
20830                     tag :'span',
20831                     cls : 'input-group-addon',
20832                     html : this.after
20833                 });
20834             }
20835             
20836         }
20837         
20838         if (align ==='left' && this.fieldLabel.length) {
20839 //                Roo.log("left and has label");
20840             cfg.cn = [
20841                 {
20842                     tag: 'label',
20843                     'for' :  id,
20844                     cls : 'control-label',
20845                     html : this.fieldLabel
20846                 },
20847                 {
20848                     cls : "", 
20849                     cn: [
20850                         inputblock
20851                     ]
20852                 }
20853             ];
20854             
20855             if(this.labelWidth > 12){
20856                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20857             }
20858             
20859             if(this.labelWidth < 13 && this.labelmd == 0){
20860                 this.labelmd = this.labelWidth;
20861             }
20862             
20863             if(this.labellg > 0){
20864                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20865                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20866             }
20867             
20868             if(this.labelmd > 0){
20869                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20870                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20871             }
20872             
20873             if(this.labelsm > 0){
20874                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20875                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20876             }
20877             
20878             if(this.labelxs > 0){
20879                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20880                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20881             }
20882             
20883         } else if ( this.fieldLabel.length) {
20884 //                Roo.log(" label");
20885                 cfg.cn = [
20886                    
20887                     {
20888                         tag: this.boxLabel ? 'span' : 'label',
20889                         'for': id,
20890                         cls: 'control-label box-input-label',
20891                         //cls : 'input-group-addon',
20892                         html : this.fieldLabel
20893                     },
20894                     
20895                     inputblock
20896                     
20897                 ];
20898
20899         } else {
20900             
20901 //                Roo.log(" no label && no align");
20902                 cfg.cn = [  inputblock ] ;
20903                 
20904                 
20905         }
20906         
20907         if(this.boxLabel){
20908              var boxLabelCfg = {
20909                 tag: 'label',
20910                 //'for': id, // box label is handled by onclick - so no for...
20911                 cls: 'box-label',
20912                 html: this.boxLabel
20913             };
20914             
20915             if(this.tooltip){
20916                 boxLabelCfg.tooltip = this.tooltip;
20917             }
20918              
20919             cfg.cn.push(boxLabelCfg);
20920         }
20921         
20922         if(this.inputType != 'radio'){
20923             cfg.cn.push(hidden);
20924         }
20925         
20926         return cfg;
20927         
20928     },
20929     
20930     /**
20931      * return the real input element.
20932      */
20933     inputEl: function ()
20934     {
20935         return this.el.select('input.roo-' + this.inputType,true).first();
20936     },
20937     hiddenEl: function ()
20938     {
20939         return this.el.select('input.roo-hidden-value',true).first();
20940     },
20941     
20942     labelEl: function()
20943     {
20944         return this.el.select('label.control-label',true).first();
20945     },
20946     /* depricated... */
20947     
20948     label: function()
20949     {
20950         return this.labelEl();
20951     },
20952     
20953     boxLabelEl: function()
20954     {
20955         return this.el.select('label.box-label',true).first();
20956     },
20957     
20958     initEvents : function()
20959     {
20960 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20961         
20962         this.inputEl().on('click', this.onClick,  this);
20963         
20964         if (this.boxLabel) { 
20965             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20966         }
20967         
20968         this.startValue = this.getValue();
20969         
20970         if(this.groupId){
20971             Roo.bootstrap.CheckBox.register(this);
20972         }
20973     },
20974     
20975     onClick : function(e)
20976     {   
20977         if(this.fireEvent('click', this, e) !== false){
20978             this.setChecked(!this.checked);
20979         }
20980         
20981     },
20982     
20983     setChecked : function(state,suppressEvent)
20984     {
20985         this.startValue = this.getValue();
20986
20987         if(this.inputType == 'radio'){
20988             
20989             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20990                 e.dom.checked = false;
20991             });
20992             
20993             this.inputEl().dom.checked = true;
20994             
20995             this.inputEl().dom.value = this.inputValue;
20996             
20997             if(suppressEvent !== true){
20998                 this.fireEvent('check', this, true);
20999             }
21000             
21001             this.validate();
21002             
21003             return;
21004         }
21005         
21006         this.checked = state;
21007         
21008         this.inputEl().dom.checked = state;
21009         
21010         
21011         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21012         
21013         if(suppressEvent !== true){
21014             this.fireEvent('check', this, state);
21015         }
21016         
21017         this.validate();
21018     },
21019     
21020     getValue : function()
21021     {
21022         if(this.inputType == 'radio'){
21023             return this.getGroupValue();
21024         }
21025         
21026         return this.hiddenEl().dom.value;
21027         
21028     },
21029     
21030     getGroupValue : function()
21031     {
21032         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21033             return '';
21034         }
21035         
21036         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21037     },
21038     
21039     setValue : function(v,suppressEvent)
21040     {
21041         if(this.inputType == 'radio'){
21042             this.setGroupValue(v, suppressEvent);
21043             return;
21044         }
21045         
21046         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21047         
21048         this.validate();
21049     },
21050     
21051     setGroupValue : function(v, suppressEvent)
21052     {
21053         this.startValue = this.getValue();
21054         
21055         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21056             e.dom.checked = false;
21057             
21058             if(e.dom.value == v){
21059                 e.dom.checked = true;
21060             }
21061         });
21062         
21063         if(suppressEvent !== true){
21064             this.fireEvent('check', this, true);
21065         }
21066
21067         this.validate();
21068         
21069         return;
21070     },
21071     
21072     validate : function()
21073     {
21074         if(this.getVisibilityEl().hasClass('hidden')){
21075             return true;
21076         }
21077         
21078         if(
21079                 this.disabled || 
21080                 (this.inputType == 'radio' && this.validateRadio()) ||
21081                 (this.inputType == 'checkbox' && this.validateCheckbox())
21082         ){
21083             this.markValid();
21084             return true;
21085         }
21086         
21087         this.markInvalid();
21088         return false;
21089     },
21090     
21091     validateRadio : function()
21092     {
21093         if(this.getVisibilityEl().hasClass('hidden')){
21094             return true;
21095         }
21096         
21097         if(this.allowBlank){
21098             return true;
21099         }
21100         
21101         var valid = false;
21102         
21103         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21104             if(!e.dom.checked){
21105                 return;
21106             }
21107             
21108             valid = true;
21109             
21110             return false;
21111         });
21112         
21113         return valid;
21114     },
21115     
21116     validateCheckbox : function()
21117     {
21118         if(!this.groupId){
21119             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21120             //return (this.getValue() == this.inputValue) ? true : false;
21121         }
21122         
21123         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21124         
21125         if(!group){
21126             return false;
21127         }
21128         
21129         var r = false;
21130         
21131         for(var i in group){
21132             if(group[i].el.isVisible(true)){
21133                 r = false;
21134                 break;
21135             }
21136             
21137             r = true;
21138         }
21139         
21140         for(var i in group){
21141             if(r){
21142                 break;
21143             }
21144             
21145             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21146         }
21147         
21148         return r;
21149     },
21150     
21151     /**
21152      * Mark this field as valid
21153      */
21154     markValid : function()
21155     {
21156         var _this = this;
21157         
21158         this.fireEvent('valid', this);
21159         
21160         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21161         
21162         if(this.groupId){
21163             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21164         }
21165         
21166         if(label){
21167             label.markValid();
21168         }
21169
21170         if(this.inputType == 'radio'){
21171             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21172                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21173                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21174             });
21175             
21176             return;
21177         }
21178
21179         if(!this.groupId){
21180             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21181             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21182             return;
21183         }
21184         
21185         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21186         
21187         if(!group){
21188             return;
21189         }
21190         
21191         for(var i in group){
21192             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21193             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21194         }
21195     },
21196     
21197      /**
21198      * Mark this field as invalid
21199      * @param {String} msg The validation message
21200      */
21201     markInvalid : function(msg)
21202     {
21203         if(this.allowBlank){
21204             return;
21205         }
21206         
21207         var _this = this;
21208         
21209         this.fireEvent('invalid', this, msg);
21210         
21211         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21212         
21213         if(this.groupId){
21214             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21215         }
21216         
21217         if(label){
21218             label.markInvalid();
21219         }
21220             
21221         if(this.inputType == 'radio'){
21222             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21223                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21224                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21225             });
21226             
21227             return;
21228         }
21229         
21230         if(!this.groupId){
21231             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21232             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21233             return;
21234         }
21235         
21236         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21237         
21238         if(!group){
21239             return;
21240         }
21241         
21242         for(var i in group){
21243             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21244             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21245         }
21246         
21247     },
21248     
21249     clearInvalid : function()
21250     {
21251         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21252         
21253         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21254         
21255         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21256         
21257         if (label && label.iconEl) {
21258             label.iconEl.removeClass(label.validClass);
21259             label.iconEl.removeClass(label.invalidClass);
21260         }
21261     },
21262     
21263     disable : function()
21264     {
21265         if(this.inputType != 'radio'){
21266             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21267             return;
21268         }
21269         
21270         var _this = this;
21271         
21272         if(this.rendered){
21273             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21274                 _this.getActionEl().addClass(this.disabledClass);
21275                 e.dom.disabled = true;
21276             });
21277         }
21278         
21279         this.disabled = true;
21280         this.fireEvent("disable", this);
21281         return this;
21282     },
21283
21284     enable : function()
21285     {
21286         if(this.inputType != 'radio'){
21287             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21288             return;
21289         }
21290         
21291         var _this = this;
21292         
21293         if(this.rendered){
21294             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21295                 _this.getActionEl().removeClass(this.disabledClass);
21296                 e.dom.disabled = false;
21297             });
21298         }
21299         
21300         this.disabled = false;
21301         this.fireEvent("enable", this);
21302         return this;
21303     },
21304     
21305     setBoxLabel : function(v)
21306     {
21307         this.boxLabel = v;
21308         
21309         if(this.rendered){
21310             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21311         }
21312     }
21313
21314 });
21315
21316 Roo.apply(Roo.bootstrap.CheckBox, {
21317     
21318     groups: {},
21319     
21320      /**
21321     * register a CheckBox Group
21322     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21323     */
21324     register : function(checkbox)
21325     {
21326         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21327             this.groups[checkbox.groupId] = {};
21328         }
21329         
21330         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21331             return;
21332         }
21333         
21334         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21335         
21336     },
21337     /**
21338     * fetch a CheckBox Group based on the group ID
21339     * @param {string} the group ID
21340     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21341     */
21342     get: function(groupId) {
21343         if (typeof(this.groups[groupId]) == 'undefined') {
21344             return false;
21345         }
21346         
21347         return this.groups[groupId] ;
21348     }
21349     
21350     
21351 });
21352 /*
21353  * - LGPL
21354  *
21355  * RadioItem
21356  * 
21357  */
21358
21359 /**
21360  * @class Roo.bootstrap.Radio
21361  * @extends Roo.bootstrap.Component
21362  * Bootstrap Radio class
21363  * @cfg {String} boxLabel - the label associated
21364  * @cfg {String} value - the value of radio
21365  * 
21366  * @constructor
21367  * Create a new Radio
21368  * @param {Object} config The config object
21369  */
21370 Roo.bootstrap.Radio = function(config){
21371     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21372     
21373 };
21374
21375 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21376     
21377     boxLabel : '',
21378     
21379     value : '',
21380     
21381     getAutoCreate : function()
21382     {
21383         var cfg = {
21384             tag : 'div',
21385             cls : 'form-group radio',
21386             cn : [
21387                 {
21388                     tag : 'label',
21389                     cls : 'box-label',
21390                     html : this.boxLabel
21391                 }
21392             ]
21393         };
21394         
21395         return cfg;
21396     },
21397     
21398     initEvents : function() 
21399     {
21400         this.parent().register(this);
21401         
21402         this.el.on('click', this.onClick, this);
21403         
21404     },
21405     
21406     onClick : function(e)
21407     {
21408         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21409             this.setChecked(true);
21410         }
21411     },
21412     
21413     setChecked : function(state, suppressEvent)
21414     {
21415         this.parent().setValue(this.value, suppressEvent);
21416         
21417     },
21418     
21419     setBoxLabel : function(v)
21420     {
21421         this.boxLabel = v;
21422         
21423         if(this.rendered){
21424             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21425         }
21426     }
21427     
21428 });
21429  
21430
21431  /*
21432  * - LGPL
21433  *
21434  * Input
21435  * 
21436  */
21437
21438 /**
21439  * @class Roo.bootstrap.SecurePass
21440  * @extends Roo.bootstrap.Input
21441  * Bootstrap SecurePass class
21442  *
21443  * 
21444  * @constructor
21445  * Create a new SecurePass
21446  * @param {Object} config The config object
21447  */
21448  
21449 Roo.bootstrap.SecurePass = function (config) {
21450     // these go here, so the translation tool can replace them..
21451     this.errors = {
21452         PwdEmpty: "Please type a password, and then retype it to confirm.",
21453         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21454         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21455         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21456         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21457         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21458         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21459         TooWeak: "Your password is Too Weak."
21460     },
21461     this.meterLabel = "Password strength:";
21462     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21463     this.meterClass = [
21464         "roo-password-meter-tooweak", 
21465         "roo-password-meter-weak", 
21466         "roo-password-meter-medium", 
21467         "roo-password-meter-strong", 
21468         "roo-password-meter-grey"
21469     ];
21470     
21471     this.errors = {};
21472     
21473     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21474 }
21475
21476 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21477     /**
21478      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21479      * {
21480      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21481      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21482      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21483      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21484      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21485      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21486      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21487      * })
21488      */
21489     // private
21490     
21491     meterWidth: 300,
21492     errorMsg :'',    
21493     errors: false,
21494     imageRoot: '/',
21495     /**
21496      * @cfg {String/Object} Label for the strength meter (defaults to
21497      * 'Password strength:')
21498      */
21499     // private
21500     meterLabel: '',
21501     /**
21502      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21503      * ['Weak', 'Medium', 'Strong'])
21504      */
21505     // private    
21506     pwdStrengths: false,    
21507     // private
21508     strength: 0,
21509     // private
21510     _lastPwd: null,
21511     // private
21512     kCapitalLetter: 0,
21513     kSmallLetter: 1,
21514     kDigit: 2,
21515     kPunctuation: 3,
21516     
21517     insecure: false,
21518     // private
21519     initEvents: function ()
21520     {
21521         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21522
21523         if (this.el.is('input[type=password]') && Roo.isSafari) {
21524             this.el.on('keydown', this.SafariOnKeyDown, this);
21525         }
21526
21527         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21528     },
21529     // private
21530     onRender: function (ct, position)
21531     {
21532         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21533         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21534         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21535
21536         this.trigger.createChild({
21537                    cn: [
21538                     {
21539                     //id: 'PwdMeter',
21540                     tag: 'div',
21541                     cls: 'roo-password-meter-grey col-xs-12',
21542                     style: {
21543                         //width: 0,
21544                         //width: this.meterWidth + 'px'                                                
21545                         }
21546                     },
21547                     {                            
21548                          cls: 'roo-password-meter-text'                          
21549                     }
21550                 ]            
21551         });
21552
21553          
21554         if (this.hideTrigger) {
21555             this.trigger.setDisplayed(false);
21556         }
21557         this.setSize(this.width || '', this.height || '');
21558     },
21559     // private
21560     onDestroy: function ()
21561     {
21562         if (this.trigger) {
21563             this.trigger.removeAllListeners();
21564             this.trigger.remove();
21565         }
21566         if (this.wrap) {
21567             this.wrap.remove();
21568         }
21569         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21570     },
21571     // private
21572     checkStrength: function ()
21573     {
21574         var pwd = this.inputEl().getValue();
21575         if (pwd == this._lastPwd) {
21576             return;
21577         }
21578
21579         var strength;
21580         if (this.ClientSideStrongPassword(pwd)) {
21581             strength = 3;
21582         } else if (this.ClientSideMediumPassword(pwd)) {
21583             strength = 2;
21584         } else if (this.ClientSideWeakPassword(pwd)) {
21585             strength = 1;
21586         } else {
21587             strength = 0;
21588         }
21589         
21590         Roo.log('strength1: ' + strength);
21591         
21592         //var pm = this.trigger.child('div/div/div').dom;
21593         var pm = this.trigger.child('div/div');
21594         pm.removeClass(this.meterClass);
21595         pm.addClass(this.meterClass[strength]);
21596                 
21597         
21598         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21599                 
21600         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21601         
21602         this._lastPwd = pwd;
21603     },
21604     reset: function ()
21605     {
21606         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21607         
21608         this._lastPwd = '';
21609         
21610         var pm = this.trigger.child('div/div');
21611         pm.removeClass(this.meterClass);
21612         pm.addClass('roo-password-meter-grey');        
21613         
21614         
21615         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21616         
21617         pt.innerHTML = '';
21618         this.inputEl().dom.type='password';
21619     },
21620     // private
21621     validateValue: function (value)
21622     {
21623         
21624         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21625             return false;
21626         }
21627         if (value.length == 0) {
21628             if (this.allowBlank) {
21629                 this.clearInvalid();
21630                 return true;
21631             }
21632
21633             this.markInvalid(this.errors.PwdEmpty);
21634             this.errorMsg = this.errors.PwdEmpty;
21635             return false;
21636         }
21637         
21638         if(this.insecure){
21639             return true;
21640         }
21641         
21642         if ('[\x21-\x7e]*'.match(value)) {
21643             this.markInvalid(this.errors.PwdBadChar);
21644             this.errorMsg = this.errors.PwdBadChar;
21645             return false;
21646         }
21647         if (value.length < 6) {
21648             this.markInvalid(this.errors.PwdShort);
21649             this.errorMsg = this.errors.PwdShort;
21650             return false;
21651         }
21652         if (value.length > 16) {
21653             this.markInvalid(this.errors.PwdLong);
21654             this.errorMsg = this.errors.PwdLong;
21655             return false;
21656         }
21657         var strength;
21658         if (this.ClientSideStrongPassword(value)) {
21659             strength = 3;
21660         } else if (this.ClientSideMediumPassword(value)) {
21661             strength = 2;
21662         } else if (this.ClientSideWeakPassword(value)) {
21663             strength = 1;
21664         } else {
21665             strength = 0;
21666         }
21667
21668         
21669         if (strength < 2) {
21670             //this.markInvalid(this.errors.TooWeak);
21671             this.errorMsg = this.errors.TooWeak;
21672             //return false;
21673         }
21674         
21675         
21676         console.log('strength2: ' + strength);
21677         
21678         //var pm = this.trigger.child('div/div/div').dom;
21679         
21680         var pm = this.trigger.child('div/div');
21681         pm.removeClass(this.meterClass);
21682         pm.addClass(this.meterClass[strength]);
21683                 
21684         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21685                 
21686         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21687         
21688         this.errorMsg = ''; 
21689         return true;
21690     },
21691     // private
21692     CharacterSetChecks: function (type)
21693     {
21694         this.type = type;
21695         this.fResult = false;
21696     },
21697     // private
21698     isctype: function (character, type)
21699     {
21700         switch (type) {  
21701             case this.kCapitalLetter:
21702                 if (character >= 'A' && character <= 'Z') {
21703                     return true;
21704                 }
21705                 break;
21706             
21707             case this.kSmallLetter:
21708                 if (character >= 'a' && character <= 'z') {
21709                     return true;
21710                 }
21711                 break;
21712             
21713             case this.kDigit:
21714                 if (character >= '0' && character <= '9') {
21715                     return true;
21716                 }
21717                 break;
21718             
21719             case this.kPunctuation:
21720                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21721                     return true;
21722                 }
21723                 break;
21724             
21725             default:
21726                 return false;
21727         }
21728
21729     },
21730     // private
21731     IsLongEnough: function (pwd, size)
21732     {
21733         return !(pwd == null || isNaN(size) || pwd.length < size);
21734     },
21735     // private
21736     SpansEnoughCharacterSets: function (word, nb)
21737     {
21738         if (!this.IsLongEnough(word, nb))
21739         {
21740             return false;
21741         }
21742
21743         var characterSetChecks = new Array(
21744             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21745             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21746         );
21747         
21748         for (var index = 0; index < word.length; ++index) {
21749             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21750                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21751                     characterSetChecks[nCharSet].fResult = true;
21752                     break;
21753                 }
21754             }
21755         }
21756
21757         var nCharSets = 0;
21758         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21759             if (characterSetChecks[nCharSet].fResult) {
21760                 ++nCharSets;
21761             }
21762         }
21763
21764         if (nCharSets < nb) {
21765             return false;
21766         }
21767         return true;
21768     },
21769     // private
21770     ClientSideStrongPassword: function (pwd)
21771     {
21772         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21773     },
21774     // private
21775     ClientSideMediumPassword: function (pwd)
21776     {
21777         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21778     },
21779     // private
21780     ClientSideWeakPassword: function (pwd)
21781     {
21782         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21783     }
21784           
21785 })//<script type="text/javascript">
21786
21787 /*
21788  * Based  Ext JS Library 1.1.1
21789  * Copyright(c) 2006-2007, Ext JS, LLC.
21790  * LGPL
21791  *
21792  */
21793  
21794 /**
21795  * @class Roo.HtmlEditorCore
21796  * @extends Roo.Component
21797  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21798  *
21799  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21800  */
21801
21802 Roo.HtmlEditorCore = function(config){
21803     
21804     
21805     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21806     
21807     
21808     this.addEvents({
21809         /**
21810          * @event initialize
21811          * Fires when the editor is fully initialized (including the iframe)
21812          * @param {Roo.HtmlEditorCore} this
21813          */
21814         initialize: true,
21815         /**
21816          * @event activate
21817          * Fires when the editor is first receives the focus. Any insertion must wait
21818          * until after this event.
21819          * @param {Roo.HtmlEditorCore} this
21820          */
21821         activate: true,
21822          /**
21823          * @event beforesync
21824          * Fires before the textarea is updated with content from the editor iframe. Return false
21825          * to cancel the sync.
21826          * @param {Roo.HtmlEditorCore} this
21827          * @param {String} html
21828          */
21829         beforesync: true,
21830          /**
21831          * @event beforepush
21832          * Fires before the iframe editor is updated with content from the textarea. Return false
21833          * to cancel the push.
21834          * @param {Roo.HtmlEditorCore} this
21835          * @param {String} html
21836          */
21837         beforepush: true,
21838          /**
21839          * @event sync
21840          * Fires when the textarea is updated with content from the editor iframe.
21841          * @param {Roo.HtmlEditorCore} this
21842          * @param {String} html
21843          */
21844         sync: true,
21845          /**
21846          * @event push
21847          * Fires when the iframe editor is updated with content from the textarea.
21848          * @param {Roo.HtmlEditorCore} this
21849          * @param {String} html
21850          */
21851         push: true,
21852         
21853         /**
21854          * @event editorevent
21855          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21856          * @param {Roo.HtmlEditorCore} this
21857          */
21858         editorevent: true
21859         
21860     });
21861     
21862     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21863     
21864     // defaults : white / black...
21865     this.applyBlacklists();
21866     
21867     
21868     
21869 };
21870
21871
21872 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21873
21874
21875      /**
21876      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21877      */
21878     
21879     owner : false,
21880     
21881      /**
21882      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21883      *                        Roo.resizable.
21884      */
21885     resizable : false,
21886      /**
21887      * @cfg {Number} height (in pixels)
21888      */   
21889     height: 300,
21890    /**
21891      * @cfg {Number} width (in pixels)
21892      */   
21893     width: 500,
21894     
21895     /**
21896      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21897      * 
21898      */
21899     stylesheets: false,
21900     
21901     // id of frame..
21902     frameId: false,
21903     
21904     // private properties
21905     validationEvent : false,
21906     deferHeight: true,
21907     initialized : false,
21908     activated : false,
21909     sourceEditMode : false,
21910     onFocus : Roo.emptyFn,
21911     iframePad:3,
21912     hideMode:'offsets',
21913     
21914     clearUp: true,
21915     
21916     // blacklist + whitelisted elements..
21917     black: false,
21918     white: false,
21919      
21920     bodyCls : '',
21921
21922     /**
21923      * Protected method that will not generally be called directly. It
21924      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21925      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21926      */
21927     getDocMarkup : function(){
21928         // body styles..
21929         var st = '';
21930         
21931         // inherit styels from page...?? 
21932         if (this.stylesheets === false) {
21933             
21934             Roo.get(document.head).select('style').each(function(node) {
21935                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21936             });
21937             
21938             Roo.get(document.head).select('link').each(function(node) { 
21939                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21940             });
21941             
21942         } else if (!this.stylesheets.length) {
21943                 // simple..
21944                 st = '<style type="text/css">' +
21945                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21946                    '</style>';
21947         } else { 
21948             st = '<style type="text/css">' +
21949                     this.stylesheets +
21950                 '</style>';
21951         }
21952         
21953         st +=  '<style type="text/css">' +
21954             'IMG { cursor: pointer } ' +
21955         '</style>';
21956
21957         var cls = 'roo-htmleditor-body';
21958         
21959         if(this.bodyCls.length){
21960             cls += ' ' + this.bodyCls;
21961         }
21962         
21963         return '<html><head>' + st  +
21964             //<style type="text/css">' +
21965             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21966             //'</style>' +
21967             ' </head><body class="' +  cls + '"></body></html>';
21968     },
21969
21970     // private
21971     onRender : function(ct, position)
21972     {
21973         var _t = this;
21974         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21975         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21976         
21977         
21978         this.el.dom.style.border = '0 none';
21979         this.el.dom.setAttribute('tabIndex', -1);
21980         this.el.addClass('x-hidden hide');
21981         
21982         
21983         
21984         if(Roo.isIE){ // fix IE 1px bogus margin
21985             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21986         }
21987        
21988         
21989         this.frameId = Roo.id();
21990         
21991          
21992         
21993         var iframe = this.owner.wrap.createChild({
21994             tag: 'iframe',
21995             cls: 'form-control', // bootstrap..
21996             id: this.frameId,
21997             name: this.frameId,
21998             frameBorder : 'no',
21999             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22000         }, this.el
22001         );
22002         
22003         
22004         this.iframe = iframe.dom;
22005
22006          this.assignDocWin();
22007         
22008         this.doc.designMode = 'on';
22009        
22010         this.doc.open();
22011         this.doc.write(this.getDocMarkup());
22012         this.doc.close();
22013
22014         
22015         var task = { // must defer to wait for browser to be ready
22016             run : function(){
22017                 //console.log("run task?" + this.doc.readyState);
22018                 this.assignDocWin();
22019                 if(this.doc.body || this.doc.readyState == 'complete'){
22020                     try {
22021                         this.doc.designMode="on";
22022                     } catch (e) {
22023                         return;
22024                     }
22025                     Roo.TaskMgr.stop(task);
22026                     this.initEditor.defer(10, this);
22027                 }
22028             },
22029             interval : 10,
22030             duration: 10000,
22031             scope: this
22032         };
22033         Roo.TaskMgr.start(task);
22034
22035     },
22036
22037     // private
22038     onResize : function(w, h)
22039     {
22040          Roo.log('resize: ' +w + ',' + h );
22041         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22042         if(!this.iframe){
22043             return;
22044         }
22045         if(typeof w == 'number'){
22046             
22047             this.iframe.style.width = w + 'px';
22048         }
22049         if(typeof h == 'number'){
22050             
22051             this.iframe.style.height = h + 'px';
22052             if(this.doc){
22053                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22054             }
22055         }
22056         
22057     },
22058
22059     /**
22060      * Toggles the editor between standard and source edit mode.
22061      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22062      */
22063     toggleSourceEdit : function(sourceEditMode){
22064         
22065         this.sourceEditMode = sourceEditMode === true;
22066         
22067         if(this.sourceEditMode){
22068  
22069             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22070             
22071         }else{
22072             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22073             //this.iframe.className = '';
22074             this.deferFocus();
22075         }
22076         //this.setSize(this.owner.wrap.getSize());
22077         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22078     },
22079
22080     
22081   
22082
22083     /**
22084      * Protected method that will not generally be called directly. If you need/want
22085      * custom HTML cleanup, this is the method you should override.
22086      * @param {String} html The HTML to be cleaned
22087      * return {String} The cleaned HTML
22088      */
22089     cleanHtml : function(html){
22090         html = String(html);
22091         if(html.length > 5){
22092             if(Roo.isSafari){ // strip safari nonsense
22093                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22094             }
22095         }
22096         if(html == '&nbsp;'){
22097             html = '';
22098         }
22099         return html;
22100     },
22101
22102     /**
22103      * HTML Editor -> Textarea
22104      * Protected method that will not generally be called directly. Syncs the contents
22105      * of the editor iframe with the textarea.
22106      */
22107     syncValue : function(){
22108         if(this.initialized){
22109             var bd = (this.doc.body || this.doc.documentElement);
22110             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22111             var html = bd.innerHTML;
22112             if(Roo.isSafari){
22113                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22114                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22115                 if(m && m[1]){
22116                     html = '<div style="'+m[0]+'">' + html + '</div>';
22117                 }
22118             }
22119             html = this.cleanHtml(html);
22120             // fix up the special chars.. normaly like back quotes in word...
22121             // however we do not want to do this with chinese..
22122             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22123                 var cc = b.charCodeAt();
22124                 if (
22125                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22126                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22127                     (cc >= 0xf900 && cc < 0xfb00 )
22128                 ) {
22129                         return b;
22130                 }
22131                 return "&#"+cc+";" 
22132             });
22133             if(this.owner.fireEvent('beforesync', this, html) !== false){
22134                 this.el.dom.value = html;
22135                 this.owner.fireEvent('sync', this, html);
22136             }
22137         }
22138     },
22139
22140     /**
22141      * Protected method that will not generally be called directly. Pushes the value of the textarea
22142      * into the iframe editor.
22143      */
22144     pushValue : function(){
22145         if(this.initialized){
22146             var v = this.el.dom.value.trim();
22147             
22148 //            if(v.length < 1){
22149 //                v = '&#160;';
22150 //            }
22151             
22152             if(this.owner.fireEvent('beforepush', this, v) !== false){
22153                 var d = (this.doc.body || this.doc.documentElement);
22154                 d.innerHTML = v;
22155                 this.cleanUpPaste();
22156                 this.el.dom.value = d.innerHTML;
22157                 this.owner.fireEvent('push', this, v);
22158             }
22159         }
22160     },
22161
22162     // private
22163     deferFocus : function(){
22164         this.focus.defer(10, this);
22165     },
22166
22167     // doc'ed in Field
22168     focus : function(){
22169         if(this.win && !this.sourceEditMode){
22170             this.win.focus();
22171         }else{
22172             this.el.focus();
22173         }
22174     },
22175     
22176     assignDocWin: function()
22177     {
22178         var iframe = this.iframe;
22179         
22180          if(Roo.isIE){
22181             this.doc = iframe.contentWindow.document;
22182             this.win = iframe.contentWindow;
22183         } else {
22184 //            if (!Roo.get(this.frameId)) {
22185 //                return;
22186 //            }
22187 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22188 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22189             
22190             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22191                 return;
22192             }
22193             
22194             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22195             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22196         }
22197     },
22198     
22199     // private
22200     initEditor : function(){
22201         //console.log("INIT EDITOR");
22202         this.assignDocWin();
22203         
22204         
22205         
22206         this.doc.designMode="on";
22207         this.doc.open();
22208         this.doc.write(this.getDocMarkup());
22209         this.doc.close();
22210         
22211         var dbody = (this.doc.body || this.doc.documentElement);
22212         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22213         // this copies styles from the containing element into thsi one..
22214         // not sure why we need all of this..
22215         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22216         
22217         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22218         //ss['background-attachment'] = 'fixed'; // w3c
22219         dbody.bgProperties = 'fixed'; // ie
22220         //Roo.DomHelper.applyStyles(dbody, ss);
22221         Roo.EventManager.on(this.doc, {
22222             //'mousedown': this.onEditorEvent,
22223             'mouseup': this.onEditorEvent,
22224             'dblclick': this.onEditorEvent,
22225             'click': this.onEditorEvent,
22226             'keyup': this.onEditorEvent,
22227             buffer:100,
22228             scope: this
22229         });
22230         if(Roo.isGecko){
22231             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22232         }
22233         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22234             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22235         }
22236         this.initialized = true;
22237
22238         this.owner.fireEvent('initialize', this);
22239         this.pushValue();
22240     },
22241
22242     // private
22243     onDestroy : function(){
22244         
22245         
22246         
22247         if(this.rendered){
22248             
22249             //for (var i =0; i < this.toolbars.length;i++) {
22250             //    // fixme - ask toolbars for heights?
22251             //    this.toolbars[i].onDestroy();
22252            // }
22253             
22254             //this.wrap.dom.innerHTML = '';
22255             //this.wrap.remove();
22256         }
22257     },
22258
22259     // private
22260     onFirstFocus : function(){
22261         
22262         this.assignDocWin();
22263         
22264         
22265         this.activated = true;
22266          
22267     
22268         if(Roo.isGecko){ // prevent silly gecko errors
22269             this.win.focus();
22270             var s = this.win.getSelection();
22271             if(!s.focusNode || s.focusNode.nodeType != 3){
22272                 var r = s.getRangeAt(0);
22273                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22274                 r.collapse(true);
22275                 this.deferFocus();
22276             }
22277             try{
22278                 this.execCmd('useCSS', true);
22279                 this.execCmd('styleWithCSS', false);
22280             }catch(e){}
22281         }
22282         this.owner.fireEvent('activate', this);
22283     },
22284
22285     // private
22286     adjustFont: function(btn){
22287         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22288         //if(Roo.isSafari){ // safari
22289         //    adjust *= 2;
22290        // }
22291         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22292         if(Roo.isSafari){ // safari
22293             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22294             v =  (v < 10) ? 10 : v;
22295             v =  (v > 48) ? 48 : v;
22296             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22297             
22298         }
22299         
22300         
22301         v = Math.max(1, v+adjust);
22302         
22303         this.execCmd('FontSize', v  );
22304     },
22305
22306     onEditorEvent : function(e)
22307     {
22308         this.owner.fireEvent('editorevent', this, e);
22309       //  this.updateToolbar();
22310         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22311     },
22312
22313     insertTag : function(tg)
22314     {
22315         // could be a bit smarter... -> wrap the current selected tRoo..
22316         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22317             
22318             range = this.createRange(this.getSelection());
22319             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22320             wrappingNode.appendChild(range.extractContents());
22321             range.insertNode(wrappingNode);
22322
22323             return;
22324             
22325             
22326             
22327         }
22328         this.execCmd("formatblock",   tg);
22329         
22330     },
22331     
22332     insertText : function(txt)
22333     {
22334         
22335         
22336         var range = this.createRange();
22337         range.deleteContents();
22338                //alert(Sender.getAttribute('label'));
22339                
22340         range.insertNode(this.doc.createTextNode(txt));
22341     } ,
22342     
22343      
22344
22345     /**
22346      * Executes a Midas editor command on the editor document and performs necessary focus and
22347      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22348      * @param {String} cmd The Midas command
22349      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22350      */
22351     relayCmd : function(cmd, value){
22352         this.win.focus();
22353         this.execCmd(cmd, value);
22354         this.owner.fireEvent('editorevent', this);
22355         //this.updateToolbar();
22356         this.owner.deferFocus();
22357     },
22358
22359     /**
22360      * Executes a Midas editor command directly on the editor document.
22361      * For visual commands, you should use {@link #relayCmd} instead.
22362      * <b>This should only be called after the editor is initialized.</b>
22363      * @param {String} cmd The Midas command
22364      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22365      */
22366     execCmd : function(cmd, value){
22367         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22368         this.syncValue();
22369     },
22370  
22371  
22372    
22373     /**
22374      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22375      * to insert tRoo.
22376      * @param {String} text | dom node.. 
22377      */
22378     insertAtCursor : function(text)
22379     {
22380         
22381         if(!this.activated){
22382             return;
22383         }
22384         /*
22385         if(Roo.isIE){
22386             this.win.focus();
22387             var r = this.doc.selection.createRange();
22388             if(r){
22389                 r.collapse(true);
22390                 r.pasteHTML(text);
22391                 this.syncValue();
22392                 this.deferFocus();
22393             
22394             }
22395             return;
22396         }
22397         */
22398         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22399             this.win.focus();
22400             
22401             
22402             // from jquery ui (MIT licenced)
22403             var range, node;
22404             var win = this.win;
22405             
22406             if (win.getSelection && win.getSelection().getRangeAt) {
22407                 range = win.getSelection().getRangeAt(0);
22408                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22409                 range.insertNode(node);
22410             } else if (win.document.selection && win.document.selection.createRange) {
22411                 // no firefox support
22412                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22413                 win.document.selection.createRange().pasteHTML(txt);
22414             } else {
22415                 // no firefox support
22416                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22417                 this.execCmd('InsertHTML', txt);
22418             } 
22419             
22420             this.syncValue();
22421             
22422             this.deferFocus();
22423         }
22424     },
22425  // private
22426     mozKeyPress : function(e){
22427         if(e.ctrlKey){
22428             var c = e.getCharCode(), cmd;
22429           
22430             if(c > 0){
22431                 c = String.fromCharCode(c).toLowerCase();
22432                 switch(c){
22433                     case 'b':
22434                         cmd = 'bold';
22435                         break;
22436                     case 'i':
22437                         cmd = 'italic';
22438                         break;
22439                     
22440                     case 'u':
22441                         cmd = 'underline';
22442                         break;
22443                     
22444                     case 'v':
22445                         this.cleanUpPaste.defer(100, this);
22446                         return;
22447                         
22448                 }
22449                 if(cmd){
22450                     this.win.focus();
22451                     this.execCmd(cmd);
22452                     this.deferFocus();
22453                     e.preventDefault();
22454                 }
22455                 
22456             }
22457         }
22458     },
22459
22460     // private
22461     fixKeys : function(){ // load time branching for fastest keydown performance
22462         if(Roo.isIE){
22463             return function(e){
22464                 var k = e.getKey(), r;
22465                 if(k == e.TAB){
22466                     e.stopEvent();
22467                     r = this.doc.selection.createRange();
22468                     if(r){
22469                         r.collapse(true);
22470                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22471                         this.deferFocus();
22472                     }
22473                     return;
22474                 }
22475                 
22476                 if(k == e.ENTER){
22477                     r = this.doc.selection.createRange();
22478                     if(r){
22479                         var target = r.parentElement();
22480                         if(!target || target.tagName.toLowerCase() != 'li'){
22481                             e.stopEvent();
22482                             r.pasteHTML('<br />');
22483                             r.collapse(false);
22484                             r.select();
22485                         }
22486                     }
22487                 }
22488                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22489                     this.cleanUpPaste.defer(100, this);
22490                     return;
22491                 }
22492                 
22493                 
22494             };
22495         }else if(Roo.isOpera){
22496             return function(e){
22497                 var k = e.getKey();
22498                 if(k == e.TAB){
22499                     e.stopEvent();
22500                     this.win.focus();
22501                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22502                     this.deferFocus();
22503                 }
22504                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22505                     this.cleanUpPaste.defer(100, this);
22506                     return;
22507                 }
22508                 
22509             };
22510         }else if(Roo.isSafari){
22511             return function(e){
22512                 var k = e.getKey();
22513                 
22514                 if(k == e.TAB){
22515                     e.stopEvent();
22516                     this.execCmd('InsertText','\t');
22517                     this.deferFocus();
22518                     return;
22519                 }
22520                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22521                     this.cleanUpPaste.defer(100, this);
22522                     return;
22523                 }
22524                 
22525              };
22526         }
22527     }(),
22528     
22529     getAllAncestors: function()
22530     {
22531         var p = this.getSelectedNode();
22532         var a = [];
22533         if (!p) {
22534             a.push(p); // push blank onto stack..
22535             p = this.getParentElement();
22536         }
22537         
22538         
22539         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22540             a.push(p);
22541             p = p.parentNode;
22542         }
22543         a.push(this.doc.body);
22544         return a;
22545     },
22546     lastSel : false,
22547     lastSelNode : false,
22548     
22549     
22550     getSelection : function() 
22551     {
22552         this.assignDocWin();
22553         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22554     },
22555     
22556     getSelectedNode: function() 
22557     {
22558         // this may only work on Gecko!!!
22559         
22560         // should we cache this!!!!
22561         
22562         
22563         
22564          
22565         var range = this.createRange(this.getSelection()).cloneRange();
22566         
22567         if (Roo.isIE) {
22568             var parent = range.parentElement();
22569             while (true) {
22570                 var testRange = range.duplicate();
22571                 testRange.moveToElementText(parent);
22572                 if (testRange.inRange(range)) {
22573                     break;
22574                 }
22575                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22576                     break;
22577                 }
22578                 parent = parent.parentElement;
22579             }
22580             return parent;
22581         }
22582         
22583         // is ancestor a text element.
22584         var ac =  range.commonAncestorContainer;
22585         if (ac.nodeType == 3) {
22586             ac = ac.parentNode;
22587         }
22588         
22589         var ar = ac.childNodes;
22590          
22591         var nodes = [];
22592         var other_nodes = [];
22593         var has_other_nodes = false;
22594         for (var i=0;i<ar.length;i++) {
22595             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22596                 continue;
22597             }
22598             // fullly contained node.
22599             
22600             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22601                 nodes.push(ar[i]);
22602                 continue;
22603             }
22604             
22605             // probably selected..
22606             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22607                 other_nodes.push(ar[i]);
22608                 continue;
22609             }
22610             // outer..
22611             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22612                 continue;
22613             }
22614             
22615             
22616             has_other_nodes = true;
22617         }
22618         if (!nodes.length && other_nodes.length) {
22619             nodes= other_nodes;
22620         }
22621         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22622             return false;
22623         }
22624         
22625         return nodes[0];
22626     },
22627     createRange: function(sel)
22628     {
22629         // this has strange effects when using with 
22630         // top toolbar - not sure if it's a great idea.
22631         //this.editor.contentWindow.focus();
22632         if (typeof sel != "undefined") {
22633             try {
22634                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22635             } catch(e) {
22636                 return this.doc.createRange();
22637             }
22638         } else {
22639             return this.doc.createRange();
22640         }
22641     },
22642     getParentElement: function()
22643     {
22644         
22645         this.assignDocWin();
22646         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22647         
22648         var range = this.createRange(sel);
22649          
22650         try {
22651             var p = range.commonAncestorContainer;
22652             while (p.nodeType == 3) { // text node
22653                 p = p.parentNode;
22654             }
22655             return p;
22656         } catch (e) {
22657             return null;
22658         }
22659     
22660     },
22661     /***
22662      *
22663      * Range intersection.. the hard stuff...
22664      *  '-1' = before
22665      *  '0' = hits..
22666      *  '1' = after.
22667      *         [ -- selected range --- ]
22668      *   [fail]                        [fail]
22669      *
22670      *    basically..
22671      *      if end is before start or  hits it. fail.
22672      *      if start is after end or hits it fail.
22673      *
22674      *   if either hits (but other is outside. - then it's not 
22675      *   
22676      *    
22677      **/
22678     
22679     
22680     // @see http://www.thismuchiknow.co.uk/?p=64.
22681     rangeIntersectsNode : function(range, node)
22682     {
22683         var nodeRange = node.ownerDocument.createRange();
22684         try {
22685             nodeRange.selectNode(node);
22686         } catch (e) {
22687             nodeRange.selectNodeContents(node);
22688         }
22689     
22690         var rangeStartRange = range.cloneRange();
22691         rangeStartRange.collapse(true);
22692     
22693         var rangeEndRange = range.cloneRange();
22694         rangeEndRange.collapse(false);
22695     
22696         var nodeStartRange = nodeRange.cloneRange();
22697         nodeStartRange.collapse(true);
22698     
22699         var nodeEndRange = nodeRange.cloneRange();
22700         nodeEndRange.collapse(false);
22701     
22702         return rangeStartRange.compareBoundaryPoints(
22703                  Range.START_TO_START, nodeEndRange) == -1 &&
22704                rangeEndRange.compareBoundaryPoints(
22705                  Range.START_TO_START, nodeStartRange) == 1;
22706         
22707          
22708     },
22709     rangeCompareNode : function(range, node)
22710     {
22711         var nodeRange = node.ownerDocument.createRange();
22712         try {
22713             nodeRange.selectNode(node);
22714         } catch (e) {
22715             nodeRange.selectNodeContents(node);
22716         }
22717         
22718         
22719         range.collapse(true);
22720     
22721         nodeRange.collapse(true);
22722      
22723         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22724         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22725          
22726         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22727         
22728         var nodeIsBefore   =  ss == 1;
22729         var nodeIsAfter    = ee == -1;
22730         
22731         if (nodeIsBefore && nodeIsAfter) {
22732             return 0; // outer
22733         }
22734         if (!nodeIsBefore && nodeIsAfter) {
22735             return 1; //right trailed.
22736         }
22737         
22738         if (nodeIsBefore && !nodeIsAfter) {
22739             return 2;  // left trailed.
22740         }
22741         // fully contined.
22742         return 3;
22743     },
22744
22745     // private? - in a new class?
22746     cleanUpPaste :  function()
22747     {
22748         // cleans up the whole document..
22749         Roo.log('cleanuppaste');
22750         
22751         this.cleanUpChildren(this.doc.body);
22752         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22753         if (clean != this.doc.body.innerHTML) {
22754             this.doc.body.innerHTML = clean;
22755         }
22756         
22757     },
22758     
22759     cleanWordChars : function(input) {// change the chars to hex code
22760         var he = Roo.HtmlEditorCore;
22761         
22762         var output = input;
22763         Roo.each(he.swapCodes, function(sw) { 
22764             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22765             
22766             output = output.replace(swapper, sw[1]);
22767         });
22768         
22769         return output;
22770     },
22771     
22772     
22773     cleanUpChildren : function (n)
22774     {
22775         if (!n.childNodes.length) {
22776             return;
22777         }
22778         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22779            this.cleanUpChild(n.childNodes[i]);
22780         }
22781     },
22782     
22783     
22784         
22785     
22786     cleanUpChild : function (node)
22787     {
22788         var ed = this;
22789         //console.log(node);
22790         if (node.nodeName == "#text") {
22791             // clean up silly Windows -- stuff?
22792             return; 
22793         }
22794         if (node.nodeName == "#comment") {
22795             node.parentNode.removeChild(node);
22796             // clean up silly Windows -- stuff?
22797             return; 
22798         }
22799         var lcname = node.tagName.toLowerCase();
22800         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22801         // whitelist of tags..
22802         
22803         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22804             // remove node.
22805             node.parentNode.removeChild(node);
22806             return;
22807             
22808         }
22809         
22810         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22811         
22812         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22813         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22814         
22815         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22816         //    remove_keep_children = true;
22817         //}
22818         
22819         if (remove_keep_children) {
22820             this.cleanUpChildren(node);
22821             // inserts everything just before this node...
22822             while (node.childNodes.length) {
22823                 var cn = node.childNodes[0];
22824                 node.removeChild(cn);
22825                 node.parentNode.insertBefore(cn, node);
22826             }
22827             node.parentNode.removeChild(node);
22828             return;
22829         }
22830         
22831         if (!node.attributes || !node.attributes.length) {
22832             this.cleanUpChildren(node);
22833             return;
22834         }
22835         
22836         function cleanAttr(n,v)
22837         {
22838             
22839             if (v.match(/^\./) || v.match(/^\//)) {
22840                 return;
22841             }
22842             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22843                 return;
22844             }
22845             if (v.match(/^#/)) {
22846                 return;
22847             }
22848 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22849             node.removeAttribute(n);
22850             
22851         }
22852         
22853         var cwhite = this.cwhite;
22854         var cblack = this.cblack;
22855             
22856         function cleanStyle(n,v)
22857         {
22858             if (v.match(/expression/)) { //XSS?? should we even bother..
22859                 node.removeAttribute(n);
22860                 return;
22861             }
22862             
22863             var parts = v.split(/;/);
22864             var clean = [];
22865             
22866             Roo.each(parts, function(p) {
22867                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22868                 if (!p.length) {
22869                     return true;
22870                 }
22871                 var l = p.split(':').shift().replace(/\s+/g,'');
22872                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22873                 
22874                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22875 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22876                     //node.removeAttribute(n);
22877                     return true;
22878                 }
22879                 //Roo.log()
22880                 // only allow 'c whitelisted system attributes'
22881                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22882 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22883                     //node.removeAttribute(n);
22884                     return true;
22885                 }
22886                 
22887                 
22888                  
22889                 
22890                 clean.push(p);
22891                 return true;
22892             });
22893             if (clean.length) { 
22894                 node.setAttribute(n, clean.join(';'));
22895             } else {
22896                 node.removeAttribute(n);
22897             }
22898             
22899         }
22900         
22901         
22902         for (var i = node.attributes.length-1; i > -1 ; i--) {
22903             var a = node.attributes[i];
22904             //console.log(a);
22905             
22906             if (a.name.toLowerCase().substr(0,2)=='on')  {
22907                 node.removeAttribute(a.name);
22908                 continue;
22909             }
22910             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22911                 node.removeAttribute(a.name);
22912                 continue;
22913             }
22914             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22915                 cleanAttr(a.name,a.value); // fixme..
22916                 continue;
22917             }
22918             if (a.name == 'style') {
22919                 cleanStyle(a.name,a.value);
22920                 continue;
22921             }
22922             /// clean up MS crap..
22923             // tecnically this should be a list of valid class'es..
22924             
22925             
22926             if (a.name == 'class') {
22927                 if (a.value.match(/^Mso/)) {
22928                     node.className = '';
22929                 }
22930                 
22931                 if (a.value.match(/^body$/)) {
22932                     node.className = '';
22933                 }
22934                 continue;
22935             }
22936             
22937             // style cleanup!?
22938             // class cleanup?
22939             
22940         }
22941         
22942         
22943         this.cleanUpChildren(node);
22944         
22945         
22946     },
22947     
22948     /**
22949      * Clean up MS wordisms...
22950      */
22951     cleanWord : function(node)
22952     {
22953         
22954         
22955         if (!node) {
22956             this.cleanWord(this.doc.body);
22957             return;
22958         }
22959         if (node.nodeName == "#text") {
22960             // clean up silly Windows -- stuff?
22961             return; 
22962         }
22963         if (node.nodeName == "#comment") {
22964             node.parentNode.removeChild(node);
22965             // clean up silly Windows -- stuff?
22966             return; 
22967         }
22968         
22969         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22970             node.parentNode.removeChild(node);
22971             return;
22972         }
22973         
22974         // remove - but keep children..
22975         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22976             while (node.childNodes.length) {
22977                 var cn = node.childNodes[0];
22978                 node.removeChild(cn);
22979                 node.parentNode.insertBefore(cn, node);
22980             }
22981             node.parentNode.removeChild(node);
22982             this.iterateChildren(node, this.cleanWord);
22983             return;
22984         }
22985         // clean styles
22986         if (node.className.length) {
22987             
22988             var cn = node.className.split(/\W+/);
22989             var cna = [];
22990             Roo.each(cn, function(cls) {
22991                 if (cls.match(/Mso[a-zA-Z]+/)) {
22992                     return;
22993                 }
22994                 cna.push(cls);
22995             });
22996             node.className = cna.length ? cna.join(' ') : '';
22997             if (!cna.length) {
22998                 node.removeAttribute("class");
22999             }
23000         }
23001         
23002         if (node.hasAttribute("lang")) {
23003             node.removeAttribute("lang");
23004         }
23005         
23006         if (node.hasAttribute("style")) {
23007             
23008             var styles = node.getAttribute("style").split(";");
23009             var nstyle = [];
23010             Roo.each(styles, function(s) {
23011                 if (!s.match(/:/)) {
23012                     return;
23013                 }
23014                 var kv = s.split(":");
23015                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23016                     return;
23017                 }
23018                 // what ever is left... we allow.
23019                 nstyle.push(s);
23020             });
23021             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23022             if (!nstyle.length) {
23023                 node.removeAttribute('style');
23024             }
23025         }
23026         this.iterateChildren(node, this.cleanWord);
23027         
23028         
23029         
23030     },
23031     /**
23032      * iterateChildren of a Node, calling fn each time, using this as the scole..
23033      * @param {DomNode} node node to iterate children of.
23034      * @param {Function} fn method of this class to call on each item.
23035      */
23036     iterateChildren : function(node, fn)
23037     {
23038         if (!node.childNodes.length) {
23039                 return;
23040         }
23041         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23042            fn.call(this, node.childNodes[i])
23043         }
23044     },
23045     
23046     
23047     /**
23048      * cleanTableWidths.
23049      *
23050      * Quite often pasting from word etc.. results in tables with column and widths.
23051      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23052      *
23053      */
23054     cleanTableWidths : function(node)
23055     {
23056          
23057          
23058         if (!node) {
23059             this.cleanTableWidths(this.doc.body);
23060             return;
23061         }
23062         
23063         // ignore list...
23064         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23065             return; 
23066         }
23067         Roo.log(node.tagName);
23068         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23069             this.iterateChildren(node, this.cleanTableWidths);
23070             return;
23071         }
23072         if (node.hasAttribute('width')) {
23073             node.removeAttribute('width');
23074         }
23075         
23076          
23077         if (node.hasAttribute("style")) {
23078             // pretty basic...
23079             
23080             var styles = node.getAttribute("style").split(";");
23081             var nstyle = [];
23082             Roo.each(styles, function(s) {
23083                 if (!s.match(/:/)) {
23084                     return;
23085                 }
23086                 var kv = s.split(":");
23087                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23088                     return;
23089                 }
23090                 // what ever is left... we allow.
23091                 nstyle.push(s);
23092             });
23093             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23094             if (!nstyle.length) {
23095                 node.removeAttribute('style');
23096             }
23097         }
23098         
23099         this.iterateChildren(node, this.cleanTableWidths);
23100         
23101         
23102     },
23103     
23104     
23105     
23106     
23107     domToHTML : function(currentElement, depth, nopadtext) {
23108         
23109         depth = depth || 0;
23110         nopadtext = nopadtext || false;
23111     
23112         if (!currentElement) {
23113             return this.domToHTML(this.doc.body);
23114         }
23115         
23116         //Roo.log(currentElement);
23117         var j;
23118         var allText = false;
23119         var nodeName = currentElement.nodeName;
23120         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23121         
23122         if  (nodeName == '#text') {
23123             
23124             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23125         }
23126         
23127         
23128         var ret = '';
23129         if (nodeName != 'BODY') {
23130              
23131             var i = 0;
23132             // Prints the node tagName, such as <A>, <IMG>, etc
23133             if (tagName) {
23134                 var attr = [];
23135                 for(i = 0; i < currentElement.attributes.length;i++) {
23136                     // quoting?
23137                     var aname = currentElement.attributes.item(i).name;
23138                     if (!currentElement.attributes.item(i).value.length) {
23139                         continue;
23140                     }
23141                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23142                 }
23143                 
23144                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23145             } 
23146             else {
23147                 
23148                 // eack
23149             }
23150         } else {
23151             tagName = false;
23152         }
23153         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23154             return ret;
23155         }
23156         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23157             nopadtext = true;
23158         }
23159         
23160         
23161         // Traverse the tree
23162         i = 0;
23163         var currentElementChild = currentElement.childNodes.item(i);
23164         var allText = true;
23165         var innerHTML  = '';
23166         lastnode = '';
23167         while (currentElementChild) {
23168             // Formatting code (indent the tree so it looks nice on the screen)
23169             var nopad = nopadtext;
23170             if (lastnode == 'SPAN') {
23171                 nopad  = true;
23172             }
23173             // text
23174             if  (currentElementChild.nodeName == '#text') {
23175                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23176                 toadd = nopadtext ? toadd : toadd.trim();
23177                 if (!nopad && toadd.length > 80) {
23178                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23179                 }
23180                 innerHTML  += toadd;
23181                 
23182                 i++;
23183                 currentElementChild = currentElement.childNodes.item(i);
23184                 lastNode = '';
23185                 continue;
23186             }
23187             allText = false;
23188             
23189             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23190                 
23191             // Recursively traverse the tree structure of the child node
23192             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23193             lastnode = currentElementChild.nodeName;
23194             i++;
23195             currentElementChild=currentElement.childNodes.item(i);
23196         }
23197         
23198         ret += innerHTML;
23199         
23200         if (!allText) {
23201                 // The remaining code is mostly for formatting the tree
23202             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23203         }
23204         
23205         
23206         if (tagName) {
23207             ret+= "</"+tagName+">";
23208         }
23209         return ret;
23210         
23211     },
23212         
23213     applyBlacklists : function()
23214     {
23215         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23216         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23217         
23218         this.white = [];
23219         this.black = [];
23220         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23221             if (b.indexOf(tag) > -1) {
23222                 return;
23223             }
23224             this.white.push(tag);
23225             
23226         }, this);
23227         
23228         Roo.each(w, function(tag) {
23229             if (b.indexOf(tag) > -1) {
23230                 return;
23231             }
23232             if (this.white.indexOf(tag) > -1) {
23233                 return;
23234             }
23235             this.white.push(tag);
23236             
23237         }, this);
23238         
23239         
23240         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23241             if (w.indexOf(tag) > -1) {
23242                 return;
23243             }
23244             this.black.push(tag);
23245             
23246         }, this);
23247         
23248         Roo.each(b, function(tag) {
23249             if (w.indexOf(tag) > -1) {
23250                 return;
23251             }
23252             if (this.black.indexOf(tag) > -1) {
23253                 return;
23254             }
23255             this.black.push(tag);
23256             
23257         }, this);
23258         
23259         
23260         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23261         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23262         
23263         this.cwhite = [];
23264         this.cblack = [];
23265         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23266             if (b.indexOf(tag) > -1) {
23267                 return;
23268             }
23269             this.cwhite.push(tag);
23270             
23271         }, this);
23272         
23273         Roo.each(w, function(tag) {
23274             if (b.indexOf(tag) > -1) {
23275                 return;
23276             }
23277             if (this.cwhite.indexOf(tag) > -1) {
23278                 return;
23279             }
23280             this.cwhite.push(tag);
23281             
23282         }, this);
23283         
23284         
23285         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23286             if (w.indexOf(tag) > -1) {
23287                 return;
23288             }
23289             this.cblack.push(tag);
23290             
23291         }, this);
23292         
23293         Roo.each(b, function(tag) {
23294             if (w.indexOf(tag) > -1) {
23295                 return;
23296             }
23297             if (this.cblack.indexOf(tag) > -1) {
23298                 return;
23299             }
23300             this.cblack.push(tag);
23301             
23302         }, this);
23303     },
23304     
23305     setStylesheets : function(stylesheets)
23306     {
23307         if(typeof(stylesheets) == 'string'){
23308             Roo.get(this.iframe.contentDocument.head).createChild({
23309                 tag : 'link',
23310                 rel : 'stylesheet',
23311                 type : 'text/css',
23312                 href : stylesheets
23313             });
23314             
23315             return;
23316         }
23317         var _this = this;
23318      
23319         Roo.each(stylesheets, function(s) {
23320             if(!s.length){
23321                 return;
23322             }
23323             
23324             Roo.get(_this.iframe.contentDocument.head).createChild({
23325                 tag : 'link',
23326                 rel : 'stylesheet',
23327                 type : 'text/css',
23328                 href : s
23329             });
23330         });
23331
23332         
23333     },
23334     
23335     removeStylesheets : function()
23336     {
23337         var _this = this;
23338         
23339         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23340             s.remove();
23341         });
23342     },
23343     
23344     setStyle : function(style)
23345     {
23346         Roo.get(this.iframe.contentDocument.head).createChild({
23347             tag : 'style',
23348             type : 'text/css',
23349             html : style
23350         });
23351
23352         return;
23353     }
23354     
23355     // hide stuff that is not compatible
23356     /**
23357      * @event blur
23358      * @hide
23359      */
23360     /**
23361      * @event change
23362      * @hide
23363      */
23364     /**
23365      * @event focus
23366      * @hide
23367      */
23368     /**
23369      * @event specialkey
23370      * @hide
23371      */
23372     /**
23373      * @cfg {String} fieldClass @hide
23374      */
23375     /**
23376      * @cfg {String} focusClass @hide
23377      */
23378     /**
23379      * @cfg {String} autoCreate @hide
23380      */
23381     /**
23382      * @cfg {String} inputType @hide
23383      */
23384     /**
23385      * @cfg {String} invalidClass @hide
23386      */
23387     /**
23388      * @cfg {String} invalidText @hide
23389      */
23390     /**
23391      * @cfg {String} msgFx @hide
23392      */
23393     /**
23394      * @cfg {String} validateOnBlur @hide
23395      */
23396 });
23397
23398 Roo.HtmlEditorCore.white = [
23399         'area', 'br', 'img', 'input', 'hr', 'wbr',
23400         
23401        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23402        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23403        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23404        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23405        'table',   'ul',         'xmp', 
23406        
23407        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23408       'thead',   'tr', 
23409      
23410       'dir', 'menu', 'ol', 'ul', 'dl',
23411        
23412       'embed',  'object'
23413 ];
23414
23415
23416 Roo.HtmlEditorCore.black = [
23417     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23418         'applet', // 
23419         'base',   'basefont', 'bgsound', 'blink',  'body', 
23420         'frame',  'frameset', 'head',    'html',   'ilayer', 
23421         'iframe', 'layer',  'link',     'meta',    'object',   
23422         'script', 'style' ,'title',  'xml' // clean later..
23423 ];
23424 Roo.HtmlEditorCore.clean = [
23425     'script', 'style', 'title', 'xml'
23426 ];
23427 Roo.HtmlEditorCore.remove = [
23428     'font'
23429 ];
23430 // attributes..
23431
23432 Roo.HtmlEditorCore.ablack = [
23433     'on'
23434 ];
23435     
23436 Roo.HtmlEditorCore.aclean = [ 
23437     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23438 ];
23439
23440 // protocols..
23441 Roo.HtmlEditorCore.pwhite= [
23442         'http',  'https',  'mailto'
23443 ];
23444
23445 // white listed style attributes.
23446 Roo.HtmlEditorCore.cwhite= [
23447       //  'text-align', /// default is to allow most things..
23448       
23449          
23450 //        'font-size'//??
23451 ];
23452
23453 // black listed style attributes.
23454 Roo.HtmlEditorCore.cblack= [
23455       //  'font-size' -- this can be set by the project 
23456 ];
23457
23458
23459 Roo.HtmlEditorCore.swapCodes   =[ 
23460     [    8211, "--" ], 
23461     [    8212, "--" ], 
23462     [    8216,  "'" ],  
23463     [    8217, "'" ],  
23464     [    8220, '"' ],  
23465     [    8221, '"' ],  
23466     [    8226, "*" ],  
23467     [    8230, "..." ]
23468 ]; 
23469
23470     /*
23471  * - LGPL
23472  *
23473  * HtmlEditor
23474  * 
23475  */
23476
23477 /**
23478  * @class Roo.bootstrap.HtmlEditor
23479  * @extends Roo.bootstrap.TextArea
23480  * Bootstrap HtmlEditor class
23481
23482  * @constructor
23483  * Create a new HtmlEditor
23484  * @param {Object} config The config object
23485  */
23486
23487 Roo.bootstrap.HtmlEditor = function(config){
23488     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23489     if (!this.toolbars) {
23490         this.toolbars = [];
23491     }
23492     
23493     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23494     this.addEvents({
23495             /**
23496              * @event initialize
23497              * Fires when the editor is fully initialized (including the iframe)
23498              * @param {HtmlEditor} this
23499              */
23500             initialize: true,
23501             /**
23502              * @event activate
23503              * Fires when the editor is first receives the focus. Any insertion must wait
23504              * until after this event.
23505              * @param {HtmlEditor} this
23506              */
23507             activate: true,
23508              /**
23509              * @event beforesync
23510              * Fires before the textarea is updated with content from the editor iframe. Return false
23511              * to cancel the sync.
23512              * @param {HtmlEditor} this
23513              * @param {String} html
23514              */
23515             beforesync: true,
23516              /**
23517              * @event beforepush
23518              * Fires before the iframe editor is updated with content from the textarea. Return false
23519              * to cancel the push.
23520              * @param {HtmlEditor} this
23521              * @param {String} html
23522              */
23523             beforepush: true,
23524              /**
23525              * @event sync
23526              * Fires when the textarea is updated with content from the editor iframe.
23527              * @param {HtmlEditor} this
23528              * @param {String} html
23529              */
23530             sync: true,
23531              /**
23532              * @event push
23533              * Fires when the iframe editor is updated with content from the textarea.
23534              * @param {HtmlEditor} this
23535              * @param {String} html
23536              */
23537             push: true,
23538              /**
23539              * @event editmodechange
23540              * Fires when the editor switches edit modes
23541              * @param {HtmlEditor} this
23542              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23543              */
23544             editmodechange: true,
23545             /**
23546              * @event editorevent
23547              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23548              * @param {HtmlEditor} this
23549              */
23550             editorevent: true,
23551             /**
23552              * @event firstfocus
23553              * Fires when on first focus - needed by toolbars..
23554              * @param {HtmlEditor} this
23555              */
23556             firstfocus: true,
23557             /**
23558              * @event autosave
23559              * Auto save the htmlEditor value as a file into Events
23560              * @param {HtmlEditor} this
23561              */
23562             autosave: true,
23563             /**
23564              * @event savedpreview
23565              * preview the saved version of htmlEditor
23566              * @param {HtmlEditor} this
23567              */
23568             savedpreview: true
23569         });
23570 };
23571
23572
23573 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23574     
23575     
23576       /**
23577      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23578      */
23579     toolbars : false,
23580     
23581      /**
23582     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23583     */
23584     btns : [],
23585    
23586      /**
23587      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23588      *                        Roo.resizable.
23589      */
23590     resizable : false,
23591      /**
23592      * @cfg {Number} height (in pixels)
23593      */   
23594     height: 300,
23595    /**
23596      * @cfg {Number} width (in pixels)
23597      */   
23598     width: false,
23599     
23600     /**
23601      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23602      * 
23603      */
23604     stylesheets: false,
23605     
23606     // id of frame..
23607     frameId: false,
23608     
23609     // private properties
23610     validationEvent : false,
23611     deferHeight: true,
23612     initialized : false,
23613     activated : false,
23614     
23615     onFocus : Roo.emptyFn,
23616     iframePad:3,
23617     hideMode:'offsets',
23618     
23619     tbContainer : false,
23620     
23621     bodyCls : '',
23622     
23623     toolbarContainer :function() {
23624         return this.wrap.select('.x-html-editor-tb',true).first();
23625     },
23626
23627     /**
23628      * Protected method that will not generally be called directly. It
23629      * is called when the editor creates its toolbar. Override this method if you need to
23630      * add custom toolbar buttons.
23631      * @param {HtmlEditor} editor
23632      */
23633     createToolbar : function(){
23634         Roo.log('renewing');
23635         Roo.log("create toolbars");
23636         
23637         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23638         this.toolbars[0].render(this.toolbarContainer());
23639         
23640         return;
23641         
23642 //        if (!editor.toolbars || !editor.toolbars.length) {
23643 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23644 //        }
23645 //        
23646 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23647 //            editor.toolbars[i] = Roo.factory(
23648 //                    typeof(editor.toolbars[i]) == 'string' ?
23649 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23650 //                Roo.bootstrap.HtmlEditor);
23651 //            editor.toolbars[i].init(editor);
23652 //        }
23653     },
23654
23655      
23656     // private
23657     onRender : function(ct, position)
23658     {
23659        // Roo.log("Call onRender: " + this.xtype);
23660         var _t = this;
23661         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23662       
23663         this.wrap = this.inputEl().wrap({
23664             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23665         });
23666         
23667         this.editorcore.onRender(ct, position);
23668          
23669         if (this.resizable) {
23670             this.resizeEl = new Roo.Resizable(this.wrap, {
23671                 pinned : true,
23672                 wrap: true,
23673                 dynamic : true,
23674                 minHeight : this.height,
23675                 height: this.height,
23676                 handles : this.resizable,
23677                 width: this.width,
23678                 listeners : {
23679                     resize : function(r, w, h) {
23680                         _t.onResize(w,h); // -something
23681                     }
23682                 }
23683             });
23684             
23685         }
23686         this.createToolbar(this);
23687        
23688         
23689         if(!this.width && this.resizable){
23690             this.setSize(this.wrap.getSize());
23691         }
23692         if (this.resizeEl) {
23693             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23694             // should trigger onReize..
23695         }
23696         
23697     },
23698
23699     // private
23700     onResize : function(w, h)
23701     {
23702         Roo.log('resize: ' +w + ',' + h );
23703         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23704         var ew = false;
23705         var eh = false;
23706         
23707         if(this.inputEl() ){
23708             if(typeof w == 'number'){
23709                 var aw = w - this.wrap.getFrameWidth('lr');
23710                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23711                 ew = aw;
23712             }
23713             if(typeof h == 'number'){
23714                  var tbh = -11;  // fixme it needs to tool bar size!
23715                 for (var i =0; i < this.toolbars.length;i++) {
23716                     // fixme - ask toolbars for heights?
23717                     tbh += this.toolbars[i].el.getHeight();
23718                     //if (this.toolbars[i].footer) {
23719                     //    tbh += this.toolbars[i].footer.el.getHeight();
23720                     //}
23721                 }
23722               
23723                 
23724                 
23725                 
23726                 
23727                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23728                 ah -= 5; // knock a few pixes off for look..
23729                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23730                 var eh = ah;
23731             }
23732         }
23733         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23734         this.editorcore.onResize(ew,eh);
23735         
23736     },
23737
23738     /**
23739      * Toggles the editor between standard and source edit mode.
23740      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23741      */
23742     toggleSourceEdit : function(sourceEditMode)
23743     {
23744         this.editorcore.toggleSourceEdit(sourceEditMode);
23745         
23746         if(this.editorcore.sourceEditMode){
23747             Roo.log('editor - showing textarea');
23748             
23749 //            Roo.log('in');
23750 //            Roo.log(this.syncValue());
23751             this.syncValue();
23752             this.inputEl().removeClass(['hide', 'x-hidden']);
23753             this.inputEl().dom.removeAttribute('tabIndex');
23754             this.inputEl().focus();
23755         }else{
23756             Roo.log('editor - hiding textarea');
23757 //            Roo.log('out')
23758 //            Roo.log(this.pushValue()); 
23759             this.pushValue();
23760             
23761             this.inputEl().addClass(['hide', 'x-hidden']);
23762             this.inputEl().dom.setAttribute('tabIndex', -1);
23763             //this.deferFocus();
23764         }
23765          
23766         if(this.resizable){
23767             this.setSize(this.wrap.getSize());
23768         }
23769         
23770         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23771     },
23772  
23773     // private (for BoxComponent)
23774     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23775
23776     // private (for BoxComponent)
23777     getResizeEl : function(){
23778         return this.wrap;
23779     },
23780
23781     // private (for BoxComponent)
23782     getPositionEl : function(){
23783         return this.wrap;
23784     },
23785
23786     // private
23787     initEvents : function(){
23788         this.originalValue = this.getValue();
23789     },
23790
23791 //    /**
23792 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23793 //     * @method
23794 //     */
23795 //    markInvalid : Roo.emptyFn,
23796 //    /**
23797 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23798 //     * @method
23799 //     */
23800 //    clearInvalid : Roo.emptyFn,
23801
23802     setValue : function(v){
23803         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23804         this.editorcore.pushValue();
23805     },
23806
23807      
23808     // private
23809     deferFocus : function(){
23810         this.focus.defer(10, this);
23811     },
23812
23813     // doc'ed in Field
23814     focus : function(){
23815         this.editorcore.focus();
23816         
23817     },
23818       
23819
23820     // private
23821     onDestroy : function(){
23822         
23823         
23824         
23825         if(this.rendered){
23826             
23827             for (var i =0; i < this.toolbars.length;i++) {
23828                 // fixme - ask toolbars for heights?
23829                 this.toolbars[i].onDestroy();
23830             }
23831             
23832             this.wrap.dom.innerHTML = '';
23833             this.wrap.remove();
23834         }
23835     },
23836
23837     // private
23838     onFirstFocus : function(){
23839         //Roo.log("onFirstFocus");
23840         this.editorcore.onFirstFocus();
23841          for (var i =0; i < this.toolbars.length;i++) {
23842             this.toolbars[i].onFirstFocus();
23843         }
23844         
23845     },
23846     
23847     // private
23848     syncValue : function()
23849     {   
23850         this.editorcore.syncValue();
23851     },
23852     
23853     pushValue : function()
23854     {   
23855         this.editorcore.pushValue();
23856     }
23857      
23858     
23859     // hide stuff that is not compatible
23860     /**
23861      * @event blur
23862      * @hide
23863      */
23864     /**
23865      * @event change
23866      * @hide
23867      */
23868     /**
23869      * @event focus
23870      * @hide
23871      */
23872     /**
23873      * @event specialkey
23874      * @hide
23875      */
23876     /**
23877      * @cfg {String} fieldClass @hide
23878      */
23879     /**
23880      * @cfg {String} focusClass @hide
23881      */
23882     /**
23883      * @cfg {String} autoCreate @hide
23884      */
23885     /**
23886      * @cfg {String} inputType @hide
23887      */
23888     /**
23889      * @cfg {String} invalidClass @hide
23890      */
23891     /**
23892      * @cfg {String} invalidText @hide
23893      */
23894     /**
23895      * @cfg {String} msgFx @hide
23896      */
23897     /**
23898      * @cfg {String} validateOnBlur @hide
23899      */
23900 });
23901  
23902     
23903    
23904    
23905    
23906       
23907 Roo.namespace('Roo.bootstrap.htmleditor');
23908 /**
23909  * @class Roo.bootstrap.HtmlEditorToolbar1
23910  * Basic Toolbar
23911  * 
23912  * Usage:
23913  *
23914  new Roo.bootstrap.HtmlEditor({
23915     ....
23916     toolbars : [
23917         new Roo.bootstrap.HtmlEditorToolbar1({
23918             disable : { fonts: 1 , format: 1, ..., ... , ...],
23919             btns : [ .... ]
23920         })
23921     }
23922      
23923  * 
23924  * @cfg {Object} disable List of elements to disable..
23925  * @cfg {Array} btns List of additional buttons.
23926  * 
23927  * 
23928  * NEEDS Extra CSS? 
23929  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23930  */
23931  
23932 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23933 {
23934     
23935     Roo.apply(this, config);
23936     
23937     // default disabled, based on 'good practice'..
23938     this.disable = this.disable || {};
23939     Roo.applyIf(this.disable, {
23940         fontSize : true,
23941         colors : true,
23942         specialElements : true
23943     });
23944     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23945     
23946     this.editor = config.editor;
23947     this.editorcore = config.editor.editorcore;
23948     
23949     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23950     
23951     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23952     // dont call parent... till later.
23953 }
23954 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23955      
23956     bar : true,
23957     
23958     editor : false,
23959     editorcore : false,
23960     
23961     
23962     formats : [
23963         "p" ,  
23964         "h1","h2","h3","h4","h5","h6", 
23965         "pre", "code", 
23966         "abbr", "acronym", "address", "cite", "samp", "var",
23967         'div','span'
23968     ],
23969     
23970     onRender : function(ct, position)
23971     {
23972        // Roo.log("Call onRender: " + this.xtype);
23973         
23974        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23975        Roo.log(this.el);
23976        this.el.dom.style.marginBottom = '0';
23977        var _this = this;
23978        var editorcore = this.editorcore;
23979        var editor= this.editor;
23980        
23981        var children = [];
23982        var btn = function(id,cmd , toggle, handler, html){
23983        
23984             var  event = toggle ? 'toggle' : 'click';
23985        
23986             var a = {
23987                 size : 'sm',
23988                 xtype: 'Button',
23989                 xns: Roo.bootstrap,
23990                 //glyphicon : id,
23991                 fa: id,
23992                 cmd : id || cmd,
23993                 enableToggle:toggle !== false,
23994                 html : html || '',
23995                 pressed : toggle ? false : null,
23996                 listeners : {}
23997             };
23998             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23999                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24000             };
24001             children.push(a);
24002             return a;
24003        }
24004        
24005     //    var cb_box = function...
24006         
24007         var style = {
24008                 xtype: 'Button',
24009                 size : 'sm',
24010                 xns: Roo.bootstrap,
24011                 fa : 'font',
24012                 //html : 'submit'
24013                 menu : {
24014                     xtype: 'Menu',
24015                     xns: Roo.bootstrap,
24016                     items:  []
24017                 }
24018         };
24019         Roo.each(this.formats, function(f) {
24020             style.menu.items.push({
24021                 xtype :'MenuItem',
24022                 xns: Roo.bootstrap,
24023                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24024                 tagname : f,
24025                 listeners : {
24026                     click : function()
24027                     {
24028                         editorcore.insertTag(this.tagname);
24029                         editor.focus();
24030                     }
24031                 }
24032                 
24033             });
24034         });
24035         children.push(style);   
24036         
24037         btn('bold',false,true);
24038         btn('italic',false,true);
24039         btn('align-left', 'justifyleft',true);
24040         btn('align-center', 'justifycenter',true);
24041         btn('align-right' , 'justifyright',true);
24042         btn('link', false, false, function(btn) {
24043             //Roo.log("create link?");
24044             var url = prompt(this.createLinkText, this.defaultLinkValue);
24045             if(url && url != 'http:/'+'/'){
24046                 this.editorcore.relayCmd('createlink', url);
24047             }
24048         }),
24049         btn('list','insertunorderedlist',true);
24050         btn('pencil', false,true, function(btn){
24051                 Roo.log(this);
24052                 this.toggleSourceEdit(btn.pressed);
24053         });
24054         
24055         if (this.editor.btns.length > 0) {
24056             for (var i = 0; i<this.editor.btns.length; i++) {
24057                 children.push(this.editor.btns[i]);
24058             }
24059         }
24060         
24061         /*
24062         var cog = {
24063                 xtype: 'Button',
24064                 size : 'sm',
24065                 xns: Roo.bootstrap,
24066                 glyphicon : 'cog',
24067                 //html : 'submit'
24068                 menu : {
24069                     xtype: 'Menu',
24070                     xns: Roo.bootstrap,
24071                     items:  []
24072                 }
24073         };
24074         
24075         cog.menu.items.push({
24076             xtype :'MenuItem',
24077             xns: Roo.bootstrap,
24078             html : Clean styles,
24079             tagname : f,
24080             listeners : {
24081                 click : function()
24082                 {
24083                     editorcore.insertTag(this.tagname);
24084                     editor.focus();
24085                 }
24086             }
24087             
24088         });
24089        */
24090         
24091          
24092        this.xtype = 'NavSimplebar';
24093         
24094         for(var i=0;i< children.length;i++) {
24095             
24096             this.buttons.add(this.addxtypeChild(children[i]));
24097             
24098         }
24099         
24100         editor.on('editorevent', this.updateToolbar, this);
24101     },
24102     onBtnClick : function(id)
24103     {
24104        this.editorcore.relayCmd(id);
24105        this.editorcore.focus();
24106     },
24107     
24108     /**
24109      * Protected method that will not generally be called directly. It triggers
24110      * a toolbar update by reading the markup state of the current selection in the editor.
24111      */
24112     updateToolbar: function(){
24113
24114         if(!this.editorcore.activated){
24115             this.editor.onFirstFocus(); // is this neeed?
24116             return;
24117         }
24118
24119         var btns = this.buttons; 
24120         var doc = this.editorcore.doc;
24121         btns.get('bold').setActive(doc.queryCommandState('bold'));
24122         btns.get('italic').setActive(doc.queryCommandState('italic'));
24123         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24124         
24125         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24126         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24127         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24128         
24129         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24130         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24131          /*
24132         
24133         var ans = this.editorcore.getAllAncestors();
24134         if (this.formatCombo) {
24135             
24136             
24137             var store = this.formatCombo.store;
24138             this.formatCombo.setValue("");
24139             for (var i =0; i < ans.length;i++) {
24140                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24141                     // select it..
24142                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24143                     break;
24144                 }
24145             }
24146         }
24147         
24148         
24149         
24150         // hides menus... - so this cant be on a menu...
24151         Roo.bootstrap.MenuMgr.hideAll();
24152         */
24153         Roo.bootstrap.MenuMgr.hideAll();
24154         //this.editorsyncValue();
24155     },
24156     onFirstFocus: function() {
24157         this.buttons.each(function(item){
24158            item.enable();
24159         });
24160     },
24161     toggleSourceEdit : function(sourceEditMode){
24162         
24163           
24164         if(sourceEditMode){
24165             Roo.log("disabling buttons");
24166            this.buttons.each( function(item){
24167                 if(item.cmd != 'pencil'){
24168                     item.disable();
24169                 }
24170             });
24171           
24172         }else{
24173             Roo.log("enabling buttons");
24174             if(this.editorcore.initialized){
24175                 this.buttons.each( function(item){
24176                     item.enable();
24177                 });
24178             }
24179             
24180         }
24181         Roo.log("calling toggole on editor");
24182         // tell the editor that it's been pressed..
24183         this.editor.toggleSourceEdit(sourceEditMode);
24184        
24185     }
24186 });
24187
24188
24189
24190
24191
24192 /**
24193  * @class Roo.bootstrap.Table.AbstractSelectionModel
24194  * @extends Roo.util.Observable
24195  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24196  * implemented by descendant classes.  This class should not be directly instantiated.
24197  * @constructor
24198  */
24199 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24200     this.locked = false;
24201     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24202 };
24203
24204
24205 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24206     /** @ignore Called by the grid automatically. Do not call directly. */
24207     init : function(grid){
24208         this.grid = grid;
24209         this.initEvents();
24210     },
24211
24212     /**
24213      * Locks the selections.
24214      */
24215     lock : function(){
24216         this.locked = true;
24217     },
24218
24219     /**
24220      * Unlocks the selections.
24221      */
24222     unlock : function(){
24223         this.locked = false;
24224     },
24225
24226     /**
24227      * Returns true if the selections are locked.
24228      * @return {Boolean}
24229      */
24230     isLocked : function(){
24231         return this.locked;
24232     }
24233 });
24234 /**
24235  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24236  * @class Roo.bootstrap.Table.RowSelectionModel
24237  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24238  * It supports multiple selections and keyboard selection/navigation. 
24239  * @constructor
24240  * @param {Object} config
24241  */
24242
24243 Roo.bootstrap.Table.RowSelectionModel = function(config){
24244     Roo.apply(this, config);
24245     this.selections = new Roo.util.MixedCollection(false, function(o){
24246         return o.id;
24247     });
24248
24249     this.last = false;
24250     this.lastActive = false;
24251
24252     this.addEvents({
24253         /**
24254              * @event selectionchange
24255              * Fires when the selection changes
24256              * @param {SelectionModel} this
24257              */
24258             "selectionchange" : true,
24259         /**
24260              * @event afterselectionchange
24261              * Fires after the selection changes (eg. by key press or clicking)
24262              * @param {SelectionModel} this
24263              */
24264             "afterselectionchange" : true,
24265         /**
24266              * @event beforerowselect
24267              * Fires when a row is selected being selected, return false to cancel.
24268              * @param {SelectionModel} this
24269              * @param {Number} rowIndex The selected index
24270              * @param {Boolean} keepExisting False if other selections will be cleared
24271              */
24272             "beforerowselect" : true,
24273         /**
24274              * @event rowselect
24275              * Fires when a row is selected.
24276              * @param {SelectionModel} this
24277              * @param {Number} rowIndex The selected index
24278              * @param {Roo.data.Record} r The record
24279              */
24280             "rowselect" : true,
24281         /**
24282              * @event rowdeselect
24283              * Fires when a row is deselected.
24284              * @param {SelectionModel} this
24285              * @param {Number} rowIndex The selected index
24286              */
24287         "rowdeselect" : true
24288     });
24289     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24290     this.locked = false;
24291  };
24292
24293 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24294     /**
24295      * @cfg {Boolean} singleSelect
24296      * True to allow selection of only one row at a time (defaults to false)
24297      */
24298     singleSelect : false,
24299
24300     // private
24301     initEvents : function()
24302     {
24303
24304         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24305         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24306         //}else{ // allow click to work like normal
24307          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24308         //}
24309         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24310         this.grid.on("rowclick", this.handleMouseDown, this);
24311         
24312         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24313             "up" : function(e){
24314                 if(!e.shiftKey){
24315                     this.selectPrevious(e.shiftKey);
24316                 }else if(this.last !== false && this.lastActive !== false){
24317                     var last = this.last;
24318                     this.selectRange(this.last,  this.lastActive-1);
24319                     this.grid.getView().focusRow(this.lastActive);
24320                     if(last !== false){
24321                         this.last = last;
24322                     }
24323                 }else{
24324                     this.selectFirstRow();
24325                 }
24326                 this.fireEvent("afterselectionchange", this);
24327             },
24328             "down" : function(e){
24329                 if(!e.shiftKey){
24330                     this.selectNext(e.shiftKey);
24331                 }else if(this.last !== false && this.lastActive !== false){
24332                     var last = this.last;
24333                     this.selectRange(this.last,  this.lastActive+1);
24334                     this.grid.getView().focusRow(this.lastActive);
24335                     if(last !== false){
24336                         this.last = last;
24337                     }
24338                 }else{
24339                     this.selectFirstRow();
24340                 }
24341                 this.fireEvent("afterselectionchange", this);
24342             },
24343             scope: this
24344         });
24345         this.grid.store.on('load', function(){
24346             this.selections.clear();
24347         },this);
24348         /*
24349         var view = this.grid.view;
24350         view.on("refresh", this.onRefresh, this);
24351         view.on("rowupdated", this.onRowUpdated, this);
24352         view.on("rowremoved", this.onRemove, this);
24353         */
24354     },
24355
24356     // private
24357     onRefresh : function()
24358     {
24359         var ds = this.grid.store, i, v = this.grid.view;
24360         var s = this.selections;
24361         s.each(function(r){
24362             if((i = ds.indexOfId(r.id)) != -1){
24363                 v.onRowSelect(i);
24364             }else{
24365                 s.remove(r);
24366             }
24367         });
24368     },
24369
24370     // private
24371     onRemove : function(v, index, r){
24372         this.selections.remove(r);
24373     },
24374
24375     // private
24376     onRowUpdated : function(v, index, r){
24377         if(this.isSelected(r)){
24378             v.onRowSelect(index);
24379         }
24380     },
24381
24382     /**
24383      * Select records.
24384      * @param {Array} records The records to select
24385      * @param {Boolean} keepExisting (optional) True to keep existing selections
24386      */
24387     selectRecords : function(records, keepExisting)
24388     {
24389         if(!keepExisting){
24390             this.clearSelections();
24391         }
24392             var ds = this.grid.store;
24393         for(var i = 0, len = records.length; i < len; i++){
24394             this.selectRow(ds.indexOf(records[i]), true);
24395         }
24396     },
24397
24398     /**
24399      * Gets the number of selected rows.
24400      * @return {Number}
24401      */
24402     getCount : function(){
24403         return this.selections.length;
24404     },
24405
24406     /**
24407      * Selects the first row in the grid.
24408      */
24409     selectFirstRow : function(){
24410         this.selectRow(0);
24411     },
24412
24413     /**
24414      * Select the last row.
24415      * @param {Boolean} keepExisting (optional) True to keep existing selections
24416      */
24417     selectLastRow : function(keepExisting){
24418         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24419         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24420     },
24421
24422     /**
24423      * Selects the row immediately following the last selected row.
24424      * @param {Boolean} keepExisting (optional) True to keep existing selections
24425      */
24426     selectNext : function(keepExisting)
24427     {
24428             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24429             this.selectRow(this.last+1, keepExisting);
24430             this.grid.getView().focusRow(this.last);
24431         }
24432     },
24433
24434     /**
24435      * Selects the row that precedes the last selected row.
24436      * @param {Boolean} keepExisting (optional) True to keep existing selections
24437      */
24438     selectPrevious : function(keepExisting){
24439         if(this.last){
24440             this.selectRow(this.last-1, keepExisting);
24441             this.grid.getView().focusRow(this.last);
24442         }
24443     },
24444
24445     /**
24446      * Returns the selected records
24447      * @return {Array} Array of selected records
24448      */
24449     getSelections : function(){
24450         return [].concat(this.selections.items);
24451     },
24452
24453     /**
24454      * Returns the first selected record.
24455      * @return {Record}
24456      */
24457     getSelected : function(){
24458         return this.selections.itemAt(0);
24459     },
24460
24461
24462     /**
24463      * Clears all selections.
24464      */
24465     clearSelections : function(fast)
24466     {
24467         if(this.locked) {
24468             return;
24469         }
24470         if(fast !== true){
24471                 var ds = this.grid.store;
24472             var s = this.selections;
24473             s.each(function(r){
24474                 this.deselectRow(ds.indexOfId(r.id));
24475             }, this);
24476             s.clear();
24477         }else{
24478             this.selections.clear();
24479         }
24480         this.last = false;
24481     },
24482
24483
24484     /**
24485      * Selects all rows.
24486      */
24487     selectAll : function(){
24488         if(this.locked) {
24489             return;
24490         }
24491         this.selections.clear();
24492         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24493             this.selectRow(i, true);
24494         }
24495     },
24496
24497     /**
24498      * Returns True if there is a selection.
24499      * @return {Boolean}
24500      */
24501     hasSelection : function(){
24502         return this.selections.length > 0;
24503     },
24504
24505     /**
24506      * Returns True if the specified row is selected.
24507      * @param {Number/Record} record The record or index of the record to check
24508      * @return {Boolean}
24509      */
24510     isSelected : function(index){
24511             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24512         return (r && this.selections.key(r.id) ? true : false);
24513     },
24514
24515     /**
24516      * Returns True if the specified record id is selected.
24517      * @param {String} id The id of record to check
24518      * @return {Boolean}
24519      */
24520     isIdSelected : function(id){
24521         return (this.selections.key(id) ? true : false);
24522     },
24523
24524
24525     // private
24526     handleMouseDBClick : function(e, t){
24527         
24528     },
24529     // private
24530     handleMouseDown : function(e, t)
24531     {
24532             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24533         if(this.isLocked() || rowIndex < 0 ){
24534             return;
24535         };
24536         if(e.shiftKey && this.last !== false){
24537             var last = this.last;
24538             this.selectRange(last, rowIndex, e.ctrlKey);
24539             this.last = last; // reset the last
24540             t.focus();
24541     
24542         }else{
24543             var isSelected = this.isSelected(rowIndex);
24544             //Roo.log("select row:" + rowIndex);
24545             if(isSelected){
24546                 this.deselectRow(rowIndex);
24547             } else {
24548                         this.selectRow(rowIndex, true);
24549             }
24550     
24551             /*
24552                 if(e.button !== 0 && isSelected){
24553                 alert('rowIndex 2: ' + rowIndex);
24554                     view.focusRow(rowIndex);
24555                 }else if(e.ctrlKey && isSelected){
24556                     this.deselectRow(rowIndex);
24557                 }else if(!isSelected){
24558                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24559                     view.focusRow(rowIndex);
24560                 }
24561             */
24562         }
24563         this.fireEvent("afterselectionchange", this);
24564     },
24565     // private
24566     handleDragableRowClick :  function(grid, rowIndex, e) 
24567     {
24568         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24569             this.selectRow(rowIndex, false);
24570             grid.view.focusRow(rowIndex);
24571              this.fireEvent("afterselectionchange", this);
24572         }
24573     },
24574     
24575     /**
24576      * Selects multiple rows.
24577      * @param {Array} rows Array of the indexes of the row to select
24578      * @param {Boolean} keepExisting (optional) True to keep existing selections
24579      */
24580     selectRows : function(rows, keepExisting){
24581         if(!keepExisting){
24582             this.clearSelections();
24583         }
24584         for(var i = 0, len = rows.length; i < len; i++){
24585             this.selectRow(rows[i], true);
24586         }
24587     },
24588
24589     /**
24590      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24591      * @param {Number} startRow The index of the first row in the range
24592      * @param {Number} endRow The index of the last row in the range
24593      * @param {Boolean} keepExisting (optional) True to retain existing selections
24594      */
24595     selectRange : function(startRow, endRow, keepExisting){
24596         if(this.locked) {
24597             return;
24598         }
24599         if(!keepExisting){
24600             this.clearSelections();
24601         }
24602         if(startRow <= endRow){
24603             for(var i = startRow; i <= endRow; i++){
24604                 this.selectRow(i, true);
24605             }
24606         }else{
24607             for(var i = startRow; i >= endRow; i--){
24608                 this.selectRow(i, true);
24609             }
24610         }
24611     },
24612
24613     /**
24614      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24615      * @param {Number} startRow The index of the first row in the range
24616      * @param {Number} endRow The index of the last row in the range
24617      */
24618     deselectRange : function(startRow, endRow, preventViewNotify){
24619         if(this.locked) {
24620             return;
24621         }
24622         for(var i = startRow; i <= endRow; i++){
24623             this.deselectRow(i, preventViewNotify);
24624         }
24625     },
24626
24627     /**
24628      * Selects a row.
24629      * @param {Number} row The index of the row to select
24630      * @param {Boolean} keepExisting (optional) True to keep existing selections
24631      */
24632     selectRow : function(index, keepExisting, preventViewNotify)
24633     {
24634             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24635             return;
24636         }
24637         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24638             if(!keepExisting || this.singleSelect){
24639                 this.clearSelections();
24640             }
24641             
24642             var r = this.grid.store.getAt(index);
24643             //console.log('selectRow - record id :' + r.id);
24644             
24645             this.selections.add(r);
24646             this.last = this.lastActive = index;
24647             if(!preventViewNotify){
24648                 var proxy = new Roo.Element(
24649                                 this.grid.getRowDom(index)
24650                 );
24651                 proxy.addClass('bg-info info');
24652             }
24653             this.fireEvent("rowselect", this, index, r);
24654             this.fireEvent("selectionchange", this);
24655         }
24656     },
24657
24658     /**
24659      * Deselects a row.
24660      * @param {Number} row The index of the row to deselect
24661      */
24662     deselectRow : function(index, preventViewNotify)
24663     {
24664         if(this.locked) {
24665             return;
24666         }
24667         if(this.last == index){
24668             this.last = false;
24669         }
24670         if(this.lastActive == index){
24671             this.lastActive = false;
24672         }
24673         
24674         var r = this.grid.store.getAt(index);
24675         if (!r) {
24676             return;
24677         }
24678         
24679         this.selections.remove(r);
24680         //.console.log('deselectRow - record id :' + r.id);
24681         if(!preventViewNotify){
24682         
24683             var proxy = new Roo.Element(
24684                 this.grid.getRowDom(index)
24685             );
24686             proxy.removeClass('bg-info info');
24687         }
24688         this.fireEvent("rowdeselect", this, index);
24689         this.fireEvent("selectionchange", this);
24690     },
24691
24692     // private
24693     restoreLast : function(){
24694         if(this._last){
24695             this.last = this._last;
24696         }
24697     },
24698
24699     // private
24700     acceptsNav : function(row, col, cm){
24701         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24702     },
24703
24704     // private
24705     onEditorKey : function(field, e){
24706         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24707         if(k == e.TAB){
24708             e.stopEvent();
24709             ed.completeEdit();
24710             if(e.shiftKey){
24711                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24712             }else{
24713                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24714             }
24715         }else if(k == e.ENTER && !e.ctrlKey){
24716             e.stopEvent();
24717             ed.completeEdit();
24718             if(e.shiftKey){
24719                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24720             }else{
24721                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24722             }
24723         }else if(k == e.ESC){
24724             ed.cancelEdit();
24725         }
24726         if(newCell){
24727             g.startEditing(newCell[0], newCell[1]);
24728         }
24729     }
24730 });
24731 /*
24732  * Based on:
24733  * Ext JS Library 1.1.1
24734  * Copyright(c) 2006-2007, Ext JS, LLC.
24735  *
24736  * Originally Released Under LGPL - original licence link has changed is not relivant.
24737  *
24738  * Fork - LGPL
24739  * <script type="text/javascript">
24740  */
24741  
24742 /**
24743  * @class Roo.bootstrap.PagingToolbar
24744  * @extends Roo.bootstrap.NavSimplebar
24745  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24746  * @constructor
24747  * Create a new PagingToolbar
24748  * @param {Object} config The config object
24749  * @param {Roo.data.Store} store
24750  */
24751 Roo.bootstrap.PagingToolbar = function(config)
24752 {
24753     // old args format still supported... - xtype is prefered..
24754         // created from xtype...
24755     
24756     this.ds = config.dataSource;
24757     
24758     if (config.store && !this.ds) {
24759         this.store= Roo.factory(config.store, Roo.data);
24760         this.ds = this.store;
24761         this.ds.xmodule = this.xmodule || false;
24762     }
24763     
24764     this.toolbarItems = [];
24765     if (config.items) {
24766         this.toolbarItems = config.items;
24767     }
24768     
24769     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24770     
24771     this.cursor = 0;
24772     
24773     if (this.ds) { 
24774         this.bind(this.ds);
24775     }
24776     
24777     if (Roo.bootstrap.version == 4) {
24778         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24779     } else {
24780         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24781     }
24782     
24783 };
24784
24785 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24786     /**
24787      * @cfg {Roo.data.Store} dataSource
24788      * The underlying data store providing the paged data
24789      */
24790     /**
24791      * @cfg {String/HTMLElement/Element} container
24792      * container The id or element that will contain the toolbar
24793      */
24794     /**
24795      * @cfg {Boolean} displayInfo
24796      * True to display the displayMsg (defaults to false)
24797      */
24798     /**
24799      * @cfg {Number} pageSize
24800      * The number of records to display per page (defaults to 20)
24801      */
24802     pageSize: 20,
24803     /**
24804      * @cfg {String} displayMsg
24805      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24806      */
24807     displayMsg : 'Displaying {0} - {1} of {2}',
24808     /**
24809      * @cfg {String} emptyMsg
24810      * The message to display when no records are found (defaults to "No data to display")
24811      */
24812     emptyMsg : 'No data to display',
24813     /**
24814      * Customizable piece of the default paging text (defaults to "Page")
24815      * @type String
24816      */
24817     beforePageText : "Page",
24818     /**
24819      * Customizable piece of the default paging text (defaults to "of %0")
24820      * @type String
24821      */
24822     afterPageText : "of {0}",
24823     /**
24824      * Customizable piece of the default paging text (defaults to "First Page")
24825      * @type String
24826      */
24827     firstText : "First Page",
24828     /**
24829      * Customizable piece of the default paging text (defaults to "Previous Page")
24830      * @type String
24831      */
24832     prevText : "Previous Page",
24833     /**
24834      * Customizable piece of the default paging text (defaults to "Next Page")
24835      * @type String
24836      */
24837     nextText : "Next Page",
24838     /**
24839      * Customizable piece of the default paging text (defaults to "Last Page")
24840      * @type String
24841      */
24842     lastText : "Last Page",
24843     /**
24844      * Customizable piece of the default paging text (defaults to "Refresh")
24845      * @type String
24846      */
24847     refreshText : "Refresh",
24848
24849     buttons : false,
24850     // private
24851     onRender : function(ct, position) 
24852     {
24853         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24854         this.navgroup.parentId = this.id;
24855         this.navgroup.onRender(this.el, null);
24856         // add the buttons to the navgroup
24857         
24858         if(this.displayInfo){
24859             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24860             this.displayEl = this.el.select('.x-paging-info', true).first();
24861 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24862 //            this.displayEl = navel.el.select('span',true).first();
24863         }
24864         
24865         var _this = this;
24866         
24867         if(this.buttons){
24868             Roo.each(_this.buttons, function(e){ // this might need to use render????
24869                Roo.factory(e).render(_this.el);
24870             });
24871         }
24872             
24873         Roo.each(_this.toolbarItems, function(e) {
24874             _this.navgroup.addItem(e);
24875         });
24876         
24877         
24878         this.first = this.navgroup.addItem({
24879             tooltip: this.firstText,
24880             cls: "prev btn-outline-secondary",
24881             html : ' <i class="fa fa-step-backward"></i>',
24882             disabled: true,
24883             preventDefault: true,
24884             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24885         });
24886         
24887         this.prev =  this.navgroup.addItem({
24888             tooltip: this.prevText,
24889             cls: "prev btn-outline-secondary",
24890             html : ' <i class="fa fa-backward"></i>',
24891             disabled: true,
24892             preventDefault: true,
24893             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24894         });
24895     //this.addSeparator();
24896         
24897         
24898         var field = this.navgroup.addItem( {
24899             tagtype : 'span',
24900             cls : 'x-paging-position  btn-outline-secondary',
24901              disabled: true,
24902             html : this.beforePageText  +
24903                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24904                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24905          } ); //?? escaped?
24906         
24907         this.field = field.el.select('input', true).first();
24908         this.field.on("keydown", this.onPagingKeydown, this);
24909         this.field.on("focus", function(){this.dom.select();});
24910     
24911     
24912         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24913         //this.field.setHeight(18);
24914         //this.addSeparator();
24915         this.next = this.navgroup.addItem({
24916             tooltip: this.nextText,
24917             cls: "next btn-outline-secondary",
24918             html : ' <i class="fa fa-forward"></i>',
24919             disabled: true,
24920             preventDefault: true,
24921             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24922         });
24923         this.last = this.navgroup.addItem({
24924             tooltip: this.lastText,
24925             html : ' <i class="fa fa-step-forward"></i>',
24926             cls: "next btn-outline-secondary",
24927             disabled: true,
24928             preventDefault: true,
24929             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24930         });
24931     //this.addSeparator();
24932         this.loading = this.navgroup.addItem({
24933             tooltip: this.refreshText,
24934             cls: "btn-outline-secondary",
24935             html : ' <i class="fa fa-refresh"></i>',
24936             preventDefault: true,
24937             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24938         });
24939         
24940     },
24941
24942     // private
24943     updateInfo : function(){
24944         if(this.displayEl){
24945             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24946             var msg = count == 0 ?
24947                 this.emptyMsg :
24948                 String.format(
24949                     this.displayMsg,
24950                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24951                 );
24952             this.displayEl.update(msg);
24953         }
24954     },
24955
24956     // private
24957     onLoad : function(ds, r, o)
24958     {
24959         this.cursor = o.params.start ? o.params.start : 0;
24960         
24961         var d = this.getPageData(),
24962             ap = d.activePage,
24963             ps = d.pages;
24964         
24965         
24966         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24967         this.field.dom.value = ap;
24968         this.first.setDisabled(ap == 1);
24969         this.prev.setDisabled(ap == 1);
24970         this.next.setDisabled(ap == ps);
24971         this.last.setDisabled(ap == ps);
24972         this.loading.enable();
24973         this.updateInfo();
24974     },
24975
24976     // private
24977     getPageData : function(){
24978         var total = this.ds.getTotalCount();
24979         return {
24980             total : total,
24981             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24982             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24983         };
24984     },
24985
24986     // private
24987     onLoadError : function(){
24988         this.loading.enable();
24989     },
24990
24991     // private
24992     onPagingKeydown : function(e){
24993         var k = e.getKey();
24994         var d = this.getPageData();
24995         if(k == e.RETURN){
24996             var v = this.field.dom.value, pageNum;
24997             if(!v || isNaN(pageNum = parseInt(v, 10))){
24998                 this.field.dom.value = d.activePage;
24999                 return;
25000             }
25001             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25002             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25003             e.stopEvent();
25004         }
25005         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))
25006         {
25007           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25008           this.field.dom.value = pageNum;
25009           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25010           e.stopEvent();
25011         }
25012         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25013         {
25014           var v = this.field.dom.value, pageNum; 
25015           var increment = (e.shiftKey) ? 10 : 1;
25016           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25017                 increment *= -1;
25018           }
25019           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25020             this.field.dom.value = d.activePage;
25021             return;
25022           }
25023           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25024           {
25025             this.field.dom.value = parseInt(v, 10) + increment;
25026             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25027             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25028           }
25029           e.stopEvent();
25030         }
25031     },
25032
25033     // private
25034     beforeLoad : function(){
25035         if(this.loading){
25036             this.loading.disable();
25037         }
25038     },
25039
25040     // private
25041     onClick : function(which){
25042         
25043         var ds = this.ds;
25044         if (!ds) {
25045             return;
25046         }
25047         
25048         switch(which){
25049             case "first":
25050                 ds.load({params:{start: 0, limit: this.pageSize}});
25051             break;
25052             case "prev":
25053                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25054             break;
25055             case "next":
25056                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25057             break;
25058             case "last":
25059                 var total = ds.getTotalCount();
25060                 var extra = total % this.pageSize;
25061                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25062                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25063             break;
25064             case "refresh":
25065                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25066             break;
25067         }
25068     },
25069
25070     /**
25071      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25072      * @param {Roo.data.Store} store The data store to unbind
25073      */
25074     unbind : function(ds){
25075         ds.un("beforeload", this.beforeLoad, this);
25076         ds.un("load", this.onLoad, this);
25077         ds.un("loadexception", this.onLoadError, this);
25078         ds.un("remove", this.updateInfo, this);
25079         ds.un("add", this.updateInfo, this);
25080         this.ds = undefined;
25081     },
25082
25083     /**
25084      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25085      * @param {Roo.data.Store} store The data store to bind
25086      */
25087     bind : function(ds){
25088         ds.on("beforeload", this.beforeLoad, this);
25089         ds.on("load", this.onLoad, this);
25090         ds.on("loadexception", this.onLoadError, this);
25091         ds.on("remove", this.updateInfo, this);
25092         ds.on("add", this.updateInfo, this);
25093         this.ds = ds;
25094     }
25095 });/*
25096  * - LGPL
25097  *
25098  * element
25099  * 
25100  */
25101
25102 /**
25103  * @class Roo.bootstrap.MessageBar
25104  * @extends Roo.bootstrap.Component
25105  * Bootstrap MessageBar class
25106  * @cfg {String} html contents of the MessageBar
25107  * @cfg {String} weight (info | success | warning | danger) default info
25108  * @cfg {String} beforeClass insert the bar before the given class
25109  * @cfg {Boolean} closable (true | false) default false
25110  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25111  * 
25112  * @constructor
25113  * Create a new Element
25114  * @param {Object} config The config object
25115  */
25116
25117 Roo.bootstrap.MessageBar = function(config){
25118     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25119 };
25120
25121 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25122     
25123     html: '',
25124     weight: 'info',
25125     closable: false,
25126     fixed: false,
25127     beforeClass: 'bootstrap-sticky-wrap',
25128     
25129     getAutoCreate : function(){
25130         
25131         var cfg = {
25132             tag: 'div',
25133             cls: 'alert alert-dismissable alert-' + this.weight,
25134             cn: [
25135                 {
25136                     tag: 'span',
25137                     cls: 'message',
25138                     html: this.html || ''
25139                 }
25140             ]
25141         };
25142         
25143         if(this.fixed){
25144             cfg.cls += ' alert-messages-fixed';
25145         }
25146         
25147         if(this.closable){
25148             cfg.cn.push({
25149                 tag: 'button',
25150                 cls: 'close',
25151                 html: 'x'
25152             });
25153         }
25154         
25155         return cfg;
25156     },
25157     
25158     onRender : function(ct, position)
25159     {
25160         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25161         
25162         if(!this.el){
25163             var cfg = Roo.apply({},  this.getAutoCreate());
25164             cfg.id = Roo.id();
25165             
25166             if (this.cls) {
25167                 cfg.cls += ' ' + this.cls;
25168             }
25169             if (this.style) {
25170                 cfg.style = this.style;
25171             }
25172             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25173             
25174             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25175         }
25176         
25177         this.el.select('>button.close').on('click', this.hide, this);
25178         
25179     },
25180     
25181     show : function()
25182     {
25183         if (!this.rendered) {
25184             this.render();
25185         }
25186         
25187         this.el.show();
25188         
25189         this.fireEvent('show', this);
25190         
25191     },
25192     
25193     hide : function()
25194     {
25195         if (!this.rendered) {
25196             this.render();
25197         }
25198         
25199         this.el.hide();
25200         
25201         this.fireEvent('hide', this);
25202     },
25203     
25204     update : function()
25205     {
25206 //        var e = this.el.dom.firstChild;
25207 //        
25208 //        if(this.closable){
25209 //            e = e.nextSibling;
25210 //        }
25211 //        
25212 //        e.data = this.html || '';
25213
25214         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25215     }
25216    
25217 });
25218
25219  
25220
25221      /*
25222  * - LGPL
25223  *
25224  * Graph
25225  * 
25226  */
25227
25228
25229 /**
25230  * @class Roo.bootstrap.Graph
25231  * @extends Roo.bootstrap.Component
25232  * Bootstrap Graph class
25233 > Prameters
25234  -sm {number} sm 4
25235  -md {number} md 5
25236  @cfg {String} graphtype  bar | vbar | pie
25237  @cfg {number} g_x coodinator | centre x (pie)
25238  @cfg {number} g_y coodinator | centre y (pie)
25239  @cfg {number} g_r radius (pie)
25240  @cfg {number} g_height height of the chart (respected by all elements in the set)
25241  @cfg {number} g_width width of the chart (respected by all elements in the set)
25242  @cfg {Object} title The title of the chart
25243     
25244  -{Array}  values
25245  -opts (object) options for the chart 
25246      o {
25247      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25248      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25249      o vgutter (number)
25250      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.
25251      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25252      o to
25253      o stretch (boolean)
25254      o }
25255  -opts (object) options for the pie
25256      o{
25257      o cut
25258      o startAngle (number)
25259      o endAngle (number)
25260      } 
25261  *
25262  * @constructor
25263  * Create a new Input
25264  * @param {Object} config The config object
25265  */
25266
25267 Roo.bootstrap.Graph = function(config){
25268     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25269     
25270     this.addEvents({
25271         // img events
25272         /**
25273          * @event click
25274          * The img click event for the img.
25275          * @param {Roo.EventObject} e
25276          */
25277         "click" : true
25278     });
25279 };
25280
25281 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25282     
25283     sm: 4,
25284     md: 5,
25285     graphtype: 'bar',
25286     g_height: 250,
25287     g_width: 400,
25288     g_x: 50,
25289     g_y: 50,
25290     g_r: 30,
25291     opts:{
25292         //g_colors: this.colors,
25293         g_type: 'soft',
25294         g_gutter: '20%'
25295
25296     },
25297     title : false,
25298
25299     getAutoCreate : function(){
25300         
25301         var cfg = {
25302             tag: 'div',
25303             html : null
25304         };
25305         
25306         
25307         return  cfg;
25308     },
25309
25310     onRender : function(ct,position){
25311         
25312         
25313         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25314         
25315         if (typeof(Raphael) == 'undefined') {
25316             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25317             return;
25318         }
25319         
25320         this.raphael = Raphael(this.el.dom);
25321         
25322                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25323                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25324                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25325                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25326                 /*
25327                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25328                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25329                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25330                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25331                 
25332                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25333                 r.barchart(330, 10, 300, 220, data1);
25334                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25335                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25336                 */
25337                 
25338                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25339                 // r.barchart(30, 30, 560, 250,  xdata, {
25340                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25341                 //     axis : "0 0 1 1",
25342                 //     axisxlabels :  xdata
25343                 //     //yvalues : cols,
25344                    
25345                 // });
25346 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25347 //        
25348 //        this.load(null,xdata,{
25349 //                axis : "0 0 1 1",
25350 //                axisxlabels :  xdata
25351 //                });
25352
25353     },
25354
25355     load : function(graphtype,xdata,opts)
25356     {
25357         this.raphael.clear();
25358         if(!graphtype) {
25359             graphtype = this.graphtype;
25360         }
25361         if(!opts){
25362             opts = this.opts;
25363         }
25364         var r = this.raphael,
25365             fin = function () {
25366                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25367             },
25368             fout = function () {
25369                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25370             },
25371             pfin = function() {
25372                 this.sector.stop();
25373                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25374
25375                 if (this.label) {
25376                     this.label[0].stop();
25377                     this.label[0].attr({ r: 7.5 });
25378                     this.label[1].attr({ "font-weight": 800 });
25379                 }
25380             },
25381             pfout = function() {
25382                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25383
25384                 if (this.label) {
25385                     this.label[0].animate({ r: 5 }, 500, "bounce");
25386                     this.label[1].attr({ "font-weight": 400 });
25387                 }
25388             };
25389
25390         switch(graphtype){
25391             case 'bar':
25392                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25393                 break;
25394             case 'hbar':
25395                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25396                 break;
25397             case 'pie':
25398 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25399 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25400 //            
25401                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25402                 
25403                 break;
25404
25405         }
25406         
25407         if(this.title){
25408             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25409         }
25410         
25411     },
25412     
25413     setTitle: function(o)
25414     {
25415         this.title = o;
25416     },
25417     
25418     initEvents: function() {
25419         
25420         if(!this.href){
25421             this.el.on('click', this.onClick, this);
25422         }
25423     },
25424     
25425     onClick : function(e)
25426     {
25427         Roo.log('img onclick');
25428         this.fireEvent('click', this, e);
25429     }
25430    
25431 });
25432
25433  
25434 /*
25435  * - LGPL
25436  *
25437  * numberBox
25438  * 
25439  */
25440 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25441
25442 /**
25443  * @class Roo.bootstrap.dash.NumberBox
25444  * @extends Roo.bootstrap.Component
25445  * Bootstrap NumberBox class
25446  * @cfg {String} headline Box headline
25447  * @cfg {String} content Box content
25448  * @cfg {String} icon Box icon
25449  * @cfg {String} footer Footer text
25450  * @cfg {String} fhref Footer href
25451  * 
25452  * @constructor
25453  * Create a new NumberBox
25454  * @param {Object} config The config object
25455  */
25456
25457
25458 Roo.bootstrap.dash.NumberBox = function(config){
25459     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25460     
25461 };
25462
25463 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25464     
25465     headline : '',
25466     content : '',
25467     icon : '',
25468     footer : '',
25469     fhref : '',
25470     ficon : '',
25471     
25472     getAutoCreate : function(){
25473         
25474         var cfg = {
25475             tag : 'div',
25476             cls : 'small-box ',
25477             cn : [
25478                 {
25479                     tag : 'div',
25480                     cls : 'inner',
25481                     cn :[
25482                         {
25483                             tag : 'h3',
25484                             cls : 'roo-headline',
25485                             html : this.headline
25486                         },
25487                         {
25488                             tag : 'p',
25489                             cls : 'roo-content',
25490                             html : this.content
25491                         }
25492                     ]
25493                 }
25494             ]
25495         };
25496         
25497         if(this.icon){
25498             cfg.cn.push({
25499                 tag : 'div',
25500                 cls : 'icon',
25501                 cn :[
25502                     {
25503                         tag : 'i',
25504                         cls : 'ion ' + this.icon
25505                     }
25506                 ]
25507             });
25508         }
25509         
25510         if(this.footer){
25511             var footer = {
25512                 tag : 'a',
25513                 cls : 'small-box-footer',
25514                 href : this.fhref || '#',
25515                 html : this.footer
25516             };
25517             
25518             cfg.cn.push(footer);
25519             
25520         }
25521         
25522         return  cfg;
25523     },
25524
25525     onRender : function(ct,position){
25526         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25527
25528
25529        
25530                 
25531     },
25532
25533     setHeadline: function (value)
25534     {
25535         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25536     },
25537     
25538     setFooter: function (value, href)
25539     {
25540         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25541         
25542         if(href){
25543             this.el.select('a.small-box-footer',true).first().attr('href', href);
25544         }
25545         
25546     },
25547
25548     setContent: function (value)
25549     {
25550         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25551     },
25552
25553     initEvents: function() 
25554     {   
25555         
25556     }
25557     
25558 });
25559
25560  
25561 /*
25562  * - LGPL
25563  *
25564  * TabBox
25565  * 
25566  */
25567 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25568
25569 /**
25570  * @class Roo.bootstrap.dash.TabBox
25571  * @extends Roo.bootstrap.Component
25572  * Bootstrap TabBox class
25573  * @cfg {String} title Title of the TabBox
25574  * @cfg {String} icon Icon of the TabBox
25575  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25576  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25577  * 
25578  * @constructor
25579  * Create a new TabBox
25580  * @param {Object} config The config object
25581  */
25582
25583
25584 Roo.bootstrap.dash.TabBox = function(config){
25585     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25586     this.addEvents({
25587         // raw events
25588         /**
25589          * @event addpane
25590          * When a pane is added
25591          * @param {Roo.bootstrap.dash.TabPane} pane
25592          */
25593         "addpane" : true,
25594         /**
25595          * @event activatepane
25596          * When a pane is activated
25597          * @param {Roo.bootstrap.dash.TabPane} pane
25598          */
25599         "activatepane" : true
25600         
25601          
25602     });
25603     
25604     this.panes = [];
25605 };
25606
25607 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25608
25609     title : '',
25610     icon : false,
25611     showtabs : true,
25612     tabScrollable : false,
25613     
25614     getChildContainer : function()
25615     {
25616         return this.el.select('.tab-content', true).first();
25617     },
25618     
25619     getAutoCreate : function(){
25620         
25621         var header = {
25622             tag: 'li',
25623             cls: 'pull-left header',
25624             html: this.title,
25625             cn : []
25626         };
25627         
25628         if(this.icon){
25629             header.cn.push({
25630                 tag: 'i',
25631                 cls: 'fa ' + this.icon
25632             });
25633         }
25634         
25635         var h = {
25636             tag: 'ul',
25637             cls: 'nav nav-tabs pull-right',
25638             cn: [
25639                 header
25640             ]
25641         };
25642         
25643         if(this.tabScrollable){
25644             h = {
25645                 tag: 'div',
25646                 cls: 'tab-header',
25647                 cn: [
25648                     {
25649                         tag: 'ul',
25650                         cls: 'nav nav-tabs pull-right',
25651                         cn: [
25652                             header
25653                         ]
25654                     }
25655                 ]
25656             };
25657         }
25658         
25659         var cfg = {
25660             tag: 'div',
25661             cls: 'nav-tabs-custom',
25662             cn: [
25663                 h,
25664                 {
25665                     tag: 'div',
25666                     cls: 'tab-content no-padding',
25667                     cn: []
25668                 }
25669             ]
25670         };
25671
25672         return  cfg;
25673     },
25674     initEvents : function()
25675     {
25676         //Roo.log('add add pane handler');
25677         this.on('addpane', this.onAddPane, this);
25678     },
25679      /**
25680      * Updates the box title
25681      * @param {String} html to set the title to.
25682      */
25683     setTitle : function(value)
25684     {
25685         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25686     },
25687     onAddPane : function(pane)
25688     {
25689         this.panes.push(pane);
25690         //Roo.log('addpane');
25691         //Roo.log(pane);
25692         // tabs are rendere left to right..
25693         if(!this.showtabs){
25694             return;
25695         }
25696         
25697         var ctr = this.el.select('.nav-tabs', true).first();
25698          
25699          
25700         var existing = ctr.select('.nav-tab',true);
25701         var qty = existing.getCount();;
25702         
25703         
25704         var tab = ctr.createChild({
25705             tag : 'li',
25706             cls : 'nav-tab' + (qty ? '' : ' active'),
25707             cn : [
25708                 {
25709                     tag : 'a',
25710                     href:'#',
25711                     html : pane.title
25712                 }
25713             ]
25714         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25715         pane.tab = tab;
25716         
25717         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25718         if (!qty) {
25719             pane.el.addClass('active');
25720         }
25721         
25722                 
25723     },
25724     onTabClick : function(ev,un,ob,pane)
25725     {
25726         //Roo.log('tab - prev default');
25727         ev.preventDefault();
25728         
25729         
25730         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25731         pane.tab.addClass('active');
25732         //Roo.log(pane.title);
25733         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25734         // technically we should have a deactivate event.. but maybe add later.
25735         // and it should not de-activate the selected tab...
25736         this.fireEvent('activatepane', pane);
25737         pane.el.addClass('active');
25738         pane.fireEvent('activate');
25739         
25740         
25741     },
25742     
25743     getActivePane : function()
25744     {
25745         var r = false;
25746         Roo.each(this.panes, function(p) {
25747             if(p.el.hasClass('active')){
25748                 r = p;
25749                 return false;
25750             }
25751             
25752             return;
25753         });
25754         
25755         return r;
25756     }
25757     
25758     
25759 });
25760
25761  
25762 /*
25763  * - LGPL
25764  *
25765  * Tab pane
25766  * 
25767  */
25768 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25769 /**
25770  * @class Roo.bootstrap.TabPane
25771  * @extends Roo.bootstrap.Component
25772  * Bootstrap TabPane class
25773  * @cfg {Boolean} active (false | true) Default false
25774  * @cfg {String} title title of panel
25775
25776  * 
25777  * @constructor
25778  * Create a new TabPane
25779  * @param {Object} config The config object
25780  */
25781
25782 Roo.bootstrap.dash.TabPane = function(config){
25783     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25784     
25785     this.addEvents({
25786         // raw events
25787         /**
25788          * @event activate
25789          * When a pane is activated
25790          * @param {Roo.bootstrap.dash.TabPane} pane
25791          */
25792         "activate" : true
25793          
25794     });
25795 };
25796
25797 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25798     
25799     active : false,
25800     title : '',
25801     
25802     // the tabBox that this is attached to.
25803     tab : false,
25804      
25805     getAutoCreate : function() 
25806     {
25807         var cfg = {
25808             tag: 'div',
25809             cls: 'tab-pane'
25810         };
25811         
25812         if(this.active){
25813             cfg.cls += ' active';
25814         }
25815         
25816         return cfg;
25817     },
25818     initEvents  : function()
25819     {
25820         //Roo.log('trigger add pane handler');
25821         this.parent().fireEvent('addpane', this)
25822     },
25823     
25824      /**
25825      * Updates the tab title 
25826      * @param {String} html to set the title to.
25827      */
25828     setTitle: function(str)
25829     {
25830         if (!this.tab) {
25831             return;
25832         }
25833         this.title = str;
25834         this.tab.select('a', true).first().dom.innerHTML = str;
25835         
25836     }
25837     
25838     
25839     
25840 });
25841
25842  
25843
25844
25845  /*
25846  * - LGPL
25847  *
25848  * menu
25849  * 
25850  */
25851 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25852
25853 /**
25854  * @class Roo.bootstrap.menu.Menu
25855  * @extends Roo.bootstrap.Component
25856  * Bootstrap Menu class - container for Menu
25857  * @cfg {String} html Text of the menu
25858  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25859  * @cfg {String} icon Font awesome icon
25860  * @cfg {String} pos Menu align to (top | bottom) default bottom
25861  * 
25862  * 
25863  * @constructor
25864  * Create a new Menu
25865  * @param {Object} config The config object
25866  */
25867
25868
25869 Roo.bootstrap.menu.Menu = function(config){
25870     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25871     
25872     this.addEvents({
25873         /**
25874          * @event beforeshow
25875          * Fires before this menu is displayed
25876          * @param {Roo.bootstrap.menu.Menu} this
25877          */
25878         beforeshow : true,
25879         /**
25880          * @event beforehide
25881          * Fires before this menu is hidden
25882          * @param {Roo.bootstrap.menu.Menu} this
25883          */
25884         beforehide : true,
25885         /**
25886          * @event show
25887          * Fires after this menu is displayed
25888          * @param {Roo.bootstrap.menu.Menu} this
25889          */
25890         show : true,
25891         /**
25892          * @event hide
25893          * Fires after this menu is hidden
25894          * @param {Roo.bootstrap.menu.Menu} this
25895          */
25896         hide : true,
25897         /**
25898          * @event click
25899          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25900          * @param {Roo.bootstrap.menu.Menu} this
25901          * @param {Roo.EventObject} e
25902          */
25903         click : true
25904     });
25905     
25906 };
25907
25908 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25909     
25910     submenu : false,
25911     html : '',
25912     weight : 'default',
25913     icon : false,
25914     pos : 'bottom',
25915     
25916     
25917     getChildContainer : function() {
25918         if(this.isSubMenu){
25919             return this.el;
25920         }
25921         
25922         return this.el.select('ul.dropdown-menu', true).first();  
25923     },
25924     
25925     getAutoCreate : function()
25926     {
25927         var text = [
25928             {
25929                 tag : 'span',
25930                 cls : 'roo-menu-text',
25931                 html : this.html
25932             }
25933         ];
25934         
25935         if(this.icon){
25936             text.unshift({
25937                 tag : 'i',
25938                 cls : 'fa ' + this.icon
25939             })
25940         }
25941         
25942         
25943         var cfg = {
25944             tag : 'div',
25945             cls : 'btn-group',
25946             cn : [
25947                 {
25948                     tag : 'button',
25949                     cls : 'dropdown-button btn btn-' + this.weight,
25950                     cn : text
25951                 },
25952                 {
25953                     tag : 'button',
25954                     cls : 'dropdown-toggle btn btn-' + this.weight,
25955                     cn : [
25956                         {
25957                             tag : 'span',
25958                             cls : 'caret'
25959                         }
25960                     ]
25961                 },
25962                 {
25963                     tag : 'ul',
25964                     cls : 'dropdown-menu'
25965                 }
25966             ]
25967             
25968         };
25969         
25970         if(this.pos == 'top'){
25971             cfg.cls += ' dropup';
25972         }
25973         
25974         if(this.isSubMenu){
25975             cfg = {
25976                 tag : 'ul',
25977                 cls : 'dropdown-menu'
25978             }
25979         }
25980         
25981         return cfg;
25982     },
25983     
25984     onRender : function(ct, position)
25985     {
25986         this.isSubMenu = ct.hasClass('dropdown-submenu');
25987         
25988         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25989     },
25990     
25991     initEvents : function() 
25992     {
25993         if(this.isSubMenu){
25994             return;
25995         }
25996         
25997         this.hidden = true;
25998         
25999         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26000         this.triggerEl.on('click', this.onTriggerPress, this);
26001         
26002         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26003         this.buttonEl.on('click', this.onClick, this);
26004         
26005     },
26006     
26007     list : function()
26008     {
26009         if(this.isSubMenu){
26010             return this.el;
26011         }
26012         
26013         return this.el.select('ul.dropdown-menu', true).first();
26014     },
26015     
26016     onClick : function(e)
26017     {
26018         this.fireEvent("click", this, e);
26019     },
26020     
26021     onTriggerPress  : function(e)
26022     {   
26023         if (this.isVisible()) {
26024             this.hide();
26025         } else {
26026             this.show();
26027         }
26028     },
26029     
26030     isVisible : function(){
26031         return !this.hidden;
26032     },
26033     
26034     show : function()
26035     {
26036         this.fireEvent("beforeshow", this);
26037         
26038         this.hidden = false;
26039         this.el.addClass('open');
26040         
26041         Roo.get(document).on("mouseup", this.onMouseUp, this);
26042         
26043         this.fireEvent("show", this);
26044         
26045         
26046     },
26047     
26048     hide : function()
26049     {
26050         this.fireEvent("beforehide", this);
26051         
26052         this.hidden = true;
26053         this.el.removeClass('open');
26054         
26055         Roo.get(document).un("mouseup", this.onMouseUp);
26056         
26057         this.fireEvent("hide", this);
26058     },
26059     
26060     onMouseUp : function()
26061     {
26062         this.hide();
26063     }
26064     
26065 });
26066
26067  
26068  /*
26069  * - LGPL
26070  *
26071  * menu item
26072  * 
26073  */
26074 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26075
26076 /**
26077  * @class Roo.bootstrap.menu.Item
26078  * @extends Roo.bootstrap.Component
26079  * Bootstrap MenuItem class
26080  * @cfg {Boolean} submenu (true | false) default false
26081  * @cfg {String} html text of the item
26082  * @cfg {String} href the link
26083  * @cfg {Boolean} disable (true | false) default false
26084  * @cfg {Boolean} preventDefault (true | false) default true
26085  * @cfg {String} icon Font awesome icon
26086  * @cfg {String} pos Submenu align to (left | right) default right 
26087  * 
26088  * 
26089  * @constructor
26090  * Create a new Item
26091  * @param {Object} config The config object
26092  */
26093
26094
26095 Roo.bootstrap.menu.Item = function(config){
26096     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26097     this.addEvents({
26098         /**
26099          * @event mouseover
26100          * Fires when the mouse is hovering over this menu
26101          * @param {Roo.bootstrap.menu.Item} this
26102          * @param {Roo.EventObject} e
26103          */
26104         mouseover : true,
26105         /**
26106          * @event mouseout
26107          * Fires when the mouse exits this menu
26108          * @param {Roo.bootstrap.menu.Item} this
26109          * @param {Roo.EventObject} e
26110          */
26111         mouseout : true,
26112         // raw events
26113         /**
26114          * @event click
26115          * The raw click event for the entire grid.
26116          * @param {Roo.EventObject} e
26117          */
26118         click : true
26119     });
26120 };
26121
26122 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26123     
26124     submenu : false,
26125     href : '',
26126     html : '',
26127     preventDefault: true,
26128     disable : false,
26129     icon : false,
26130     pos : 'right',
26131     
26132     getAutoCreate : function()
26133     {
26134         var text = [
26135             {
26136                 tag : 'span',
26137                 cls : 'roo-menu-item-text',
26138                 html : this.html
26139             }
26140         ];
26141         
26142         if(this.icon){
26143             text.unshift({
26144                 tag : 'i',
26145                 cls : 'fa ' + this.icon
26146             })
26147         }
26148         
26149         var cfg = {
26150             tag : 'li',
26151             cn : [
26152                 {
26153                     tag : 'a',
26154                     href : this.href || '#',
26155                     cn : text
26156                 }
26157             ]
26158         };
26159         
26160         if(this.disable){
26161             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26162         }
26163         
26164         if(this.submenu){
26165             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26166             
26167             if(this.pos == 'left'){
26168                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26169             }
26170         }
26171         
26172         return cfg;
26173     },
26174     
26175     initEvents : function() 
26176     {
26177         this.el.on('mouseover', this.onMouseOver, this);
26178         this.el.on('mouseout', this.onMouseOut, this);
26179         
26180         this.el.select('a', true).first().on('click', this.onClick, this);
26181         
26182     },
26183     
26184     onClick : function(e)
26185     {
26186         if(this.preventDefault){
26187             e.preventDefault();
26188         }
26189         
26190         this.fireEvent("click", this, e);
26191     },
26192     
26193     onMouseOver : function(e)
26194     {
26195         if(this.submenu && this.pos == 'left'){
26196             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26197         }
26198         
26199         this.fireEvent("mouseover", this, e);
26200     },
26201     
26202     onMouseOut : function(e)
26203     {
26204         this.fireEvent("mouseout", this, e);
26205     }
26206 });
26207
26208  
26209
26210  /*
26211  * - LGPL
26212  *
26213  * menu separator
26214  * 
26215  */
26216 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26217
26218 /**
26219  * @class Roo.bootstrap.menu.Separator
26220  * @extends Roo.bootstrap.Component
26221  * Bootstrap Separator class
26222  * 
26223  * @constructor
26224  * Create a new Separator
26225  * @param {Object} config The config object
26226  */
26227
26228
26229 Roo.bootstrap.menu.Separator = function(config){
26230     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26231 };
26232
26233 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26234     
26235     getAutoCreate : function(){
26236         var cfg = {
26237             tag : 'li',
26238             cls: 'divider'
26239         };
26240         
26241         return cfg;
26242     }
26243    
26244 });
26245
26246  
26247
26248  /*
26249  * - LGPL
26250  *
26251  * Tooltip
26252  * 
26253  */
26254
26255 /**
26256  * @class Roo.bootstrap.Tooltip
26257  * Bootstrap Tooltip class
26258  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26259  * to determine which dom element triggers the tooltip.
26260  * 
26261  * It needs to add support for additional attributes like tooltip-position
26262  * 
26263  * @constructor
26264  * Create a new Toolti
26265  * @param {Object} config The config object
26266  */
26267
26268 Roo.bootstrap.Tooltip = function(config){
26269     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26270     
26271     this.alignment = Roo.bootstrap.Tooltip.alignment;
26272     
26273     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26274         this.alignment = config.alignment;
26275     }
26276     
26277 };
26278
26279 Roo.apply(Roo.bootstrap.Tooltip, {
26280     /**
26281      * @function init initialize tooltip monitoring.
26282      * @static
26283      */
26284     currentEl : false,
26285     currentTip : false,
26286     currentRegion : false,
26287     
26288     //  init : delay?
26289     
26290     init : function()
26291     {
26292         Roo.get(document).on('mouseover', this.enter ,this);
26293         Roo.get(document).on('mouseout', this.leave, this);
26294          
26295         
26296         this.currentTip = new Roo.bootstrap.Tooltip();
26297     },
26298     
26299     enter : function(ev)
26300     {
26301         var dom = ev.getTarget();
26302         
26303         //Roo.log(['enter',dom]);
26304         var el = Roo.fly(dom);
26305         if (this.currentEl) {
26306             //Roo.log(dom);
26307             //Roo.log(this.currentEl);
26308             //Roo.log(this.currentEl.contains(dom));
26309             if (this.currentEl == el) {
26310                 return;
26311             }
26312             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26313                 return;
26314             }
26315
26316         }
26317         
26318         if (this.currentTip.el) {
26319             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26320         }    
26321         //Roo.log(ev);
26322         
26323         if(!el || el.dom == document){
26324             return;
26325         }
26326         
26327         var bindEl = el;
26328         
26329         // you can not look for children, as if el is the body.. then everythign is the child..
26330         if (!el.attr('tooltip')) { //
26331             if (!el.select("[tooltip]").elements.length) {
26332                 return;
26333             }
26334             // is the mouse over this child...?
26335             bindEl = el.select("[tooltip]").first();
26336             var xy = ev.getXY();
26337             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26338                 //Roo.log("not in region.");
26339                 return;
26340             }
26341             //Roo.log("child element over..");
26342             
26343         }
26344         this.currentEl = bindEl;
26345         this.currentTip.bind(bindEl);
26346         this.currentRegion = Roo.lib.Region.getRegion(dom);
26347         this.currentTip.enter();
26348         
26349     },
26350     leave : function(ev)
26351     {
26352         var dom = ev.getTarget();
26353         //Roo.log(['leave',dom]);
26354         if (!this.currentEl) {
26355             return;
26356         }
26357         
26358         
26359         if (dom != this.currentEl.dom) {
26360             return;
26361         }
26362         var xy = ev.getXY();
26363         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26364             return;
26365         }
26366         // only activate leave if mouse cursor is outside... bounding box..
26367         
26368         
26369         
26370         
26371         if (this.currentTip) {
26372             this.currentTip.leave();
26373         }
26374         //Roo.log('clear currentEl');
26375         this.currentEl = false;
26376         
26377         
26378     },
26379     alignment : {
26380         'left' : ['r-l', [-2,0], 'right'],
26381         'right' : ['l-r', [2,0], 'left'],
26382         'bottom' : ['t-b', [0,2], 'top'],
26383         'top' : [ 'b-t', [0,-2], 'bottom']
26384     }
26385     
26386 });
26387
26388
26389 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26390     
26391     
26392     bindEl : false,
26393     
26394     delay : null, // can be { show : 300 , hide: 500}
26395     
26396     timeout : null,
26397     
26398     hoverState : null, //???
26399     
26400     placement : 'bottom', 
26401     
26402     alignment : false,
26403     
26404     getAutoCreate : function(){
26405     
26406         var cfg = {
26407            cls : 'tooltip',
26408            role : 'tooltip',
26409            cn : [
26410                 {
26411                     cls : 'tooltip-arrow'
26412                 },
26413                 {
26414                     cls : 'tooltip-inner'
26415                 }
26416            ]
26417         };
26418         
26419         return cfg;
26420     },
26421     bind : function(el)
26422     {
26423         this.bindEl = el;
26424     },
26425       
26426     
26427     enter : function () {
26428        
26429         if (this.timeout != null) {
26430             clearTimeout(this.timeout);
26431         }
26432         
26433         this.hoverState = 'in';
26434          //Roo.log("enter - show");
26435         if (!this.delay || !this.delay.show) {
26436             this.show();
26437             return;
26438         }
26439         var _t = this;
26440         this.timeout = setTimeout(function () {
26441             if (_t.hoverState == 'in') {
26442                 _t.show();
26443             }
26444         }, this.delay.show);
26445     },
26446     leave : function()
26447     {
26448         clearTimeout(this.timeout);
26449     
26450         this.hoverState = 'out';
26451          if (!this.delay || !this.delay.hide) {
26452             this.hide();
26453             return;
26454         }
26455        
26456         var _t = this;
26457         this.timeout = setTimeout(function () {
26458             //Roo.log("leave - timeout");
26459             
26460             if (_t.hoverState == 'out') {
26461                 _t.hide();
26462                 Roo.bootstrap.Tooltip.currentEl = false;
26463             }
26464         }, delay);
26465     },
26466     
26467     show : function (msg)
26468     {
26469         if (!this.el) {
26470             this.render(document.body);
26471         }
26472         // set content.
26473         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26474         
26475         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26476         
26477         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26478         
26479         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26480         
26481         var placement = typeof this.placement == 'function' ?
26482             this.placement.call(this, this.el, on_el) :
26483             this.placement;
26484             
26485         var autoToken = /\s?auto?\s?/i;
26486         var autoPlace = autoToken.test(placement);
26487         if (autoPlace) {
26488             placement = placement.replace(autoToken, '') || 'top';
26489         }
26490         
26491         //this.el.detach()
26492         //this.el.setXY([0,0]);
26493         this.el.show();
26494         //this.el.dom.style.display='block';
26495         
26496         //this.el.appendTo(on_el);
26497         
26498         var p = this.getPosition();
26499         var box = this.el.getBox();
26500         
26501         if (autoPlace) {
26502             // fixme..
26503         }
26504         
26505         var align = this.alignment[placement];
26506         
26507         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26508         
26509         if(placement == 'top' || placement == 'bottom'){
26510             if(xy[0] < 0){
26511                 placement = 'right';
26512             }
26513             
26514             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26515                 placement = 'left';
26516             }
26517             
26518             var scroll = Roo.select('body', true).first().getScroll();
26519             
26520             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26521                 placement = 'top';
26522             }
26523             
26524             align = this.alignment[placement];
26525         }
26526         
26527         this.el.alignTo(this.bindEl, align[0],align[1]);
26528         //var arrow = this.el.select('.arrow',true).first();
26529         //arrow.set(align[2], 
26530         
26531         this.el.addClass(placement);
26532         
26533         this.el.addClass('in fade');
26534         
26535         this.hoverState = null;
26536         
26537         if (this.el.hasClass('fade')) {
26538             // fade it?
26539         }
26540         
26541     },
26542     hide : function()
26543     {
26544          
26545         if (!this.el) {
26546             return;
26547         }
26548         //this.el.setXY([0,0]);
26549         this.el.removeClass('in');
26550         //this.el.hide();
26551         
26552     }
26553     
26554 });
26555  
26556
26557  /*
26558  * - LGPL
26559  *
26560  * Location Picker
26561  * 
26562  */
26563
26564 /**
26565  * @class Roo.bootstrap.LocationPicker
26566  * @extends Roo.bootstrap.Component
26567  * Bootstrap LocationPicker class
26568  * @cfg {Number} latitude Position when init default 0
26569  * @cfg {Number} longitude Position when init default 0
26570  * @cfg {Number} zoom default 15
26571  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26572  * @cfg {Boolean} mapTypeControl default false
26573  * @cfg {Boolean} disableDoubleClickZoom default false
26574  * @cfg {Boolean} scrollwheel default true
26575  * @cfg {Boolean} streetViewControl default false
26576  * @cfg {Number} radius default 0
26577  * @cfg {String} locationName
26578  * @cfg {Boolean} draggable default true
26579  * @cfg {Boolean} enableAutocomplete default false
26580  * @cfg {Boolean} enableReverseGeocode default true
26581  * @cfg {String} markerTitle
26582  * 
26583  * @constructor
26584  * Create a new LocationPicker
26585  * @param {Object} config The config object
26586  */
26587
26588
26589 Roo.bootstrap.LocationPicker = function(config){
26590     
26591     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26592     
26593     this.addEvents({
26594         /**
26595          * @event initial
26596          * Fires when the picker initialized.
26597          * @param {Roo.bootstrap.LocationPicker} this
26598          * @param {Google Location} location
26599          */
26600         initial : true,
26601         /**
26602          * @event positionchanged
26603          * Fires when the picker position changed.
26604          * @param {Roo.bootstrap.LocationPicker} this
26605          * @param {Google Location} location
26606          */
26607         positionchanged : true,
26608         /**
26609          * @event resize
26610          * Fires when the map resize.
26611          * @param {Roo.bootstrap.LocationPicker} this
26612          */
26613         resize : true,
26614         /**
26615          * @event show
26616          * Fires when the map show.
26617          * @param {Roo.bootstrap.LocationPicker} this
26618          */
26619         show : true,
26620         /**
26621          * @event hide
26622          * Fires when the map hide.
26623          * @param {Roo.bootstrap.LocationPicker} this
26624          */
26625         hide : true,
26626         /**
26627          * @event mapClick
26628          * Fires when click the map.
26629          * @param {Roo.bootstrap.LocationPicker} this
26630          * @param {Map event} e
26631          */
26632         mapClick : true,
26633         /**
26634          * @event mapRightClick
26635          * Fires when right click the map.
26636          * @param {Roo.bootstrap.LocationPicker} this
26637          * @param {Map event} e
26638          */
26639         mapRightClick : true,
26640         /**
26641          * @event markerClick
26642          * Fires when click the marker.
26643          * @param {Roo.bootstrap.LocationPicker} this
26644          * @param {Map event} e
26645          */
26646         markerClick : true,
26647         /**
26648          * @event markerRightClick
26649          * Fires when right click the marker.
26650          * @param {Roo.bootstrap.LocationPicker} this
26651          * @param {Map event} e
26652          */
26653         markerRightClick : true,
26654         /**
26655          * @event OverlayViewDraw
26656          * Fires when OverlayView Draw
26657          * @param {Roo.bootstrap.LocationPicker} this
26658          */
26659         OverlayViewDraw : true,
26660         /**
26661          * @event OverlayViewOnAdd
26662          * Fires when OverlayView Draw
26663          * @param {Roo.bootstrap.LocationPicker} this
26664          */
26665         OverlayViewOnAdd : true,
26666         /**
26667          * @event OverlayViewOnRemove
26668          * Fires when OverlayView Draw
26669          * @param {Roo.bootstrap.LocationPicker} this
26670          */
26671         OverlayViewOnRemove : true,
26672         /**
26673          * @event OverlayViewShow
26674          * Fires when OverlayView Draw
26675          * @param {Roo.bootstrap.LocationPicker} this
26676          * @param {Pixel} cpx
26677          */
26678         OverlayViewShow : true,
26679         /**
26680          * @event OverlayViewHide
26681          * Fires when OverlayView Draw
26682          * @param {Roo.bootstrap.LocationPicker} this
26683          */
26684         OverlayViewHide : true,
26685         /**
26686          * @event loadexception
26687          * Fires when load google lib failed.
26688          * @param {Roo.bootstrap.LocationPicker} this
26689          */
26690         loadexception : true
26691     });
26692         
26693 };
26694
26695 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26696     
26697     gMapContext: false,
26698     
26699     latitude: 0,
26700     longitude: 0,
26701     zoom: 15,
26702     mapTypeId: false,
26703     mapTypeControl: false,
26704     disableDoubleClickZoom: false,
26705     scrollwheel: true,
26706     streetViewControl: false,
26707     radius: 0,
26708     locationName: '',
26709     draggable: true,
26710     enableAutocomplete: false,
26711     enableReverseGeocode: true,
26712     markerTitle: '',
26713     
26714     getAutoCreate: function()
26715     {
26716
26717         var cfg = {
26718             tag: 'div',
26719             cls: 'roo-location-picker'
26720         };
26721         
26722         return cfg
26723     },
26724     
26725     initEvents: function(ct, position)
26726     {       
26727         if(!this.el.getWidth() || this.isApplied()){
26728             return;
26729         }
26730         
26731         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26732         
26733         this.initial();
26734     },
26735     
26736     initial: function()
26737     {
26738         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26739             this.fireEvent('loadexception', this);
26740             return;
26741         }
26742         
26743         if(!this.mapTypeId){
26744             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26745         }
26746         
26747         this.gMapContext = this.GMapContext();
26748         
26749         this.initOverlayView();
26750         
26751         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26752         
26753         var _this = this;
26754                 
26755         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26756             _this.setPosition(_this.gMapContext.marker.position);
26757         });
26758         
26759         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26760             _this.fireEvent('mapClick', this, event);
26761             
26762         });
26763
26764         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26765             _this.fireEvent('mapRightClick', this, event);
26766             
26767         });
26768         
26769         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26770             _this.fireEvent('markerClick', this, event);
26771             
26772         });
26773
26774         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26775             _this.fireEvent('markerRightClick', this, event);
26776             
26777         });
26778         
26779         this.setPosition(this.gMapContext.location);
26780         
26781         this.fireEvent('initial', this, this.gMapContext.location);
26782     },
26783     
26784     initOverlayView: function()
26785     {
26786         var _this = this;
26787         
26788         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26789             
26790             draw: function()
26791             {
26792                 _this.fireEvent('OverlayViewDraw', _this);
26793             },
26794             
26795             onAdd: function()
26796             {
26797                 _this.fireEvent('OverlayViewOnAdd', _this);
26798             },
26799             
26800             onRemove: function()
26801             {
26802                 _this.fireEvent('OverlayViewOnRemove', _this);
26803             },
26804             
26805             show: function(cpx)
26806             {
26807                 _this.fireEvent('OverlayViewShow', _this, cpx);
26808             },
26809             
26810             hide: function()
26811             {
26812                 _this.fireEvent('OverlayViewHide', _this);
26813             }
26814             
26815         });
26816     },
26817     
26818     fromLatLngToContainerPixel: function(event)
26819     {
26820         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26821     },
26822     
26823     isApplied: function() 
26824     {
26825         return this.getGmapContext() == false ? false : true;
26826     },
26827     
26828     getGmapContext: function() 
26829     {
26830         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26831     },
26832     
26833     GMapContext: function() 
26834     {
26835         var position = new google.maps.LatLng(this.latitude, this.longitude);
26836         
26837         var _map = new google.maps.Map(this.el.dom, {
26838             center: position,
26839             zoom: this.zoom,
26840             mapTypeId: this.mapTypeId,
26841             mapTypeControl: this.mapTypeControl,
26842             disableDoubleClickZoom: this.disableDoubleClickZoom,
26843             scrollwheel: this.scrollwheel,
26844             streetViewControl: this.streetViewControl,
26845             locationName: this.locationName,
26846             draggable: this.draggable,
26847             enableAutocomplete: this.enableAutocomplete,
26848             enableReverseGeocode: this.enableReverseGeocode
26849         });
26850         
26851         var _marker = new google.maps.Marker({
26852             position: position,
26853             map: _map,
26854             title: this.markerTitle,
26855             draggable: this.draggable
26856         });
26857         
26858         return {
26859             map: _map,
26860             marker: _marker,
26861             circle: null,
26862             location: position,
26863             radius: this.radius,
26864             locationName: this.locationName,
26865             addressComponents: {
26866                 formatted_address: null,
26867                 addressLine1: null,
26868                 addressLine2: null,
26869                 streetName: null,
26870                 streetNumber: null,
26871                 city: null,
26872                 district: null,
26873                 state: null,
26874                 stateOrProvince: null
26875             },
26876             settings: this,
26877             domContainer: this.el.dom,
26878             geodecoder: new google.maps.Geocoder()
26879         };
26880     },
26881     
26882     drawCircle: function(center, radius, options) 
26883     {
26884         if (this.gMapContext.circle != null) {
26885             this.gMapContext.circle.setMap(null);
26886         }
26887         if (radius > 0) {
26888             radius *= 1;
26889             options = Roo.apply({}, options, {
26890                 strokeColor: "#0000FF",
26891                 strokeOpacity: .35,
26892                 strokeWeight: 2,
26893                 fillColor: "#0000FF",
26894                 fillOpacity: .2
26895             });
26896             
26897             options.map = this.gMapContext.map;
26898             options.radius = radius;
26899             options.center = center;
26900             this.gMapContext.circle = new google.maps.Circle(options);
26901             return this.gMapContext.circle;
26902         }
26903         
26904         return null;
26905     },
26906     
26907     setPosition: function(location) 
26908     {
26909         this.gMapContext.location = location;
26910         this.gMapContext.marker.setPosition(location);
26911         this.gMapContext.map.panTo(location);
26912         this.drawCircle(location, this.gMapContext.radius, {});
26913         
26914         var _this = this;
26915         
26916         if (this.gMapContext.settings.enableReverseGeocode) {
26917             this.gMapContext.geodecoder.geocode({
26918                 latLng: this.gMapContext.location
26919             }, function(results, status) {
26920                 
26921                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26922                     _this.gMapContext.locationName = results[0].formatted_address;
26923                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26924                     
26925                     _this.fireEvent('positionchanged', this, location);
26926                 }
26927             });
26928             
26929             return;
26930         }
26931         
26932         this.fireEvent('positionchanged', this, location);
26933     },
26934     
26935     resize: function()
26936     {
26937         google.maps.event.trigger(this.gMapContext.map, "resize");
26938         
26939         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26940         
26941         this.fireEvent('resize', this);
26942     },
26943     
26944     setPositionByLatLng: function(latitude, longitude)
26945     {
26946         this.setPosition(new google.maps.LatLng(latitude, longitude));
26947     },
26948     
26949     getCurrentPosition: function() 
26950     {
26951         return {
26952             latitude: this.gMapContext.location.lat(),
26953             longitude: this.gMapContext.location.lng()
26954         };
26955     },
26956     
26957     getAddressName: function() 
26958     {
26959         return this.gMapContext.locationName;
26960     },
26961     
26962     getAddressComponents: function() 
26963     {
26964         return this.gMapContext.addressComponents;
26965     },
26966     
26967     address_component_from_google_geocode: function(address_components) 
26968     {
26969         var result = {};
26970         
26971         for (var i = 0; i < address_components.length; i++) {
26972             var component = address_components[i];
26973             if (component.types.indexOf("postal_code") >= 0) {
26974                 result.postalCode = component.short_name;
26975             } else if (component.types.indexOf("street_number") >= 0) {
26976                 result.streetNumber = component.short_name;
26977             } else if (component.types.indexOf("route") >= 0) {
26978                 result.streetName = component.short_name;
26979             } else if (component.types.indexOf("neighborhood") >= 0) {
26980                 result.city = component.short_name;
26981             } else if (component.types.indexOf("locality") >= 0) {
26982                 result.city = component.short_name;
26983             } else if (component.types.indexOf("sublocality") >= 0) {
26984                 result.district = component.short_name;
26985             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26986                 result.stateOrProvince = component.short_name;
26987             } else if (component.types.indexOf("country") >= 0) {
26988                 result.country = component.short_name;
26989             }
26990         }
26991         
26992         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26993         result.addressLine2 = "";
26994         return result;
26995     },
26996     
26997     setZoomLevel: function(zoom)
26998     {
26999         this.gMapContext.map.setZoom(zoom);
27000     },
27001     
27002     show: function()
27003     {
27004         if(!this.el){
27005             return;
27006         }
27007         
27008         this.el.show();
27009         
27010         this.resize();
27011         
27012         this.fireEvent('show', this);
27013     },
27014     
27015     hide: function()
27016     {
27017         if(!this.el){
27018             return;
27019         }
27020         
27021         this.el.hide();
27022         
27023         this.fireEvent('hide', this);
27024     }
27025     
27026 });
27027
27028 Roo.apply(Roo.bootstrap.LocationPicker, {
27029     
27030     OverlayView : function(map, options)
27031     {
27032         options = options || {};
27033         
27034         this.setMap(map);
27035     }
27036     
27037     
27038 });/*
27039  * - LGPL
27040  *
27041  * Alert
27042  * 
27043  */
27044
27045 /**
27046  * @class Roo.bootstrap.Alert
27047  * @extends Roo.bootstrap.Component
27048  * Bootstrap Alert class
27049  * @cfg {String} title The title of alert
27050  * @cfg {String} html The content of alert
27051  * @cfg {String} weight (  success | info | warning | danger )
27052  * @cfg {String} faicon font-awesomeicon
27053  * 
27054  * @constructor
27055  * Create a new alert
27056  * @param {Object} config The config object
27057  */
27058
27059
27060 Roo.bootstrap.Alert = function(config){
27061     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27062     
27063 };
27064
27065 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27066     
27067     title: '',
27068     html: '',
27069     weight: false,
27070     faicon: false,
27071     
27072     getAutoCreate : function()
27073     {
27074         
27075         var cfg = {
27076             tag : 'div',
27077             cls : 'alert',
27078             cn : [
27079                 {
27080                     tag : 'i',
27081                     cls : 'roo-alert-icon'
27082                     
27083                 },
27084                 {
27085                     tag : 'b',
27086                     cls : 'roo-alert-title',
27087                     html : this.title
27088                 },
27089                 {
27090                     tag : 'span',
27091                     cls : 'roo-alert-text',
27092                     html : this.html
27093                 }
27094             ]
27095         };
27096         
27097         if(this.faicon){
27098             cfg.cn[0].cls += ' fa ' + this.faicon;
27099         }
27100         
27101         if(this.weight){
27102             cfg.cls += ' alert-' + this.weight;
27103         }
27104         
27105         return cfg;
27106     },
27107     
27108     initEvents: function() 
27109     {
27110         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27111     },
27112     
27113     setTitle : function(str)
27114     {
27115         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27116     },
27117     
27118     setText : function(str)
27119     {
27120         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27121     },
27122     
27123     setWeight : function(weight)
27124     {
27125         if(this.weight){
27126             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27127         }
27128         
27129         this.weight = weight;
27130         
27131         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27132     },
27133     
27134     setIcon : function(icon)
27135     {
27136         if(this.faicon){
27137             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27138         }
27139         
27140         this.faicon = icon;
27141         
27142         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27143     },
27144     
27145     hide: function() 
27146     {
27147         this.el.hide();   
27148     },
27149     
27150     show: function() 
27151     {  
27152         this.el.show();   
27153     }
27154     
27155 });
27156
27157  
27158 /*
27159 * Licence: LGPL
27160 */
27161
27162 /**
27163  * @class Roo.bootstrap.UploadCropbox
27164  * @extends Roo.bootstrap.Component
27165  * Bootstrap UploadCropbox class
27166  * @cfg {String} emptyText show when image has been loaded
27167  * @cfg {String} rotateNotify show when image too small to rotate
27168  * @cfg {Number} errorTimeout default 3000
27169  * @cfg {Number} minWidth default 300
27170  * @cfg {Number} minHeight default 300
27171  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27172  * @cfg {Boolean} isDocument (true|false) default false
27173  * @cfg {String} url action url
27174  * @cfg {String} paramName default 'imageUpload'
27175  * @cfg {String} method default POST
27176  * @cfg {Boolean} loadMask (true|false) default true
27177  * @cfg {Boolean} loadingText default 'Loading...'
27178  * 
27179  * @constructor
27180  * Create a new UploadCropbox
27181  * @param {Object} config The config object
27182  */
27183
27184 Roo.bootstrap.UploadCropbox = function(config){
27185     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27186     
27187     this.addEvents({
27188         /**
27189          * @event beforeselectfile
27190          * Fire before select file
27191          * @param {Roo.bootstrap.UploadCropbox} this
27192          */
27193         "beforeselectfile" : true,
27194         /**
27195          * @event initial
27196          * Fire after initEvent
27197          * @param {Roo.bootstrap.UploadCropbox} this
27198          */
27199         "initial" : true,
27200         /**
27201          * @event crop
27202          * Fire after initEvent
27203          * @param {Roo.bootstrap.UploadCropbox} this
27204          * @param {String} data
27205          */
27206         "crop" : true,
27207         /**
27208          * @event prepare
27209          * Fire when preparing the file data
27210          * @param {Roo.bootstrap.UploadCropbox} this
27211          * @param {Object} file
27212          */
27213         "prepare" : true,
27214         /**
27215          * @event exception
27216          * Fire when get exception
27217          * @param {Roo.bootstrap.UploadCropbox} this
27218          * @param {XMLHttpRequest} xhr
27219          */
27220         "exception" : true,
27221         /**
27222          * @event beforeloadcanvas
27223          * Fire before load the canvas
27224          * @param {Roo.bootstrap.UploadCropbox} this
27225          * @param {String} src
27226          */
27227         "beforeloadcanvas" : true,
27228         /**
27229          * @event trash
27230          * Fire when trash image
27231          * @param {Roo.bootstrap.UploadCropbox} this
27232          */
27233         "trash" : true,
27234         /**
27235          * @event download
27236          * Fire when download the image
27237          * @param {Roo.bootstrap.UploadCropbox} this
27238          */
27239         "download" : true,
27240         /**
27241          * @event footerbuttonclick
27242          * Fire when footerbuttonclick
27243          * @param {Roo.bootstrap.UploadCropbox} this
27244          * @param {String} type
27245          */
27246         "footerbuttonclick" : true,
27247         /**
27248          * @event resize
27249          * Fire when resize
27250          * @param {Roo.bootstrap.UploadCropbox} this
27251          */
27252         "resize" : true,
27253         /**
27254          * @event rotate
27255          * Fire when rotate the image
27256          * @param {Roo.bootstrap.UploadCropbox} this
27257          * @param {String} pos
27258          */
27259         "rotate" : true,
27260         /**
27261          * @event inspect
27262          * Fire when inspect the file
27263          * @param {Roo.bootstrap.UploadCropbox} this
27264          * @param {Object} file
27265          */
27266         "inspect" : true,
27267         /**
27268          * @event upload
27269          * Fire when xhr upload the file
27270          * @param {Roo.bootstrap.UploadCropbox} this
27271          * @param {Object} data
27272          */
27273         "upload" : true,
27274         /**
27275          * @event arrange
27276          * Fire when arrange the file data
27277          * @param {Roo.bootstrap.UploadCropbox} this
27278          * @param {Object} formData
27279          */
27280         "arrange" : true
27281     });
27282     
27283     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27284 };
27285
27286 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27287     
27288     emptyText : 'Click to upload image',
27289     rotateNotify : 'Image is too small to rotate',
27290     errorTimeout : 3000,
27291     scale : 0,
27292     baseScale : 1,
27293     rotate : 0,
27294     dragable : false,
27295     pinching : false,
27296     mouseX : 0,
27297     mouseY : 0,
27298     cropData : false,
27299     minWidth : 300,
27300     minHeight : 300,
27301     file : false,
27302     exif : {},
27303     baseRotate : 1,
27304     cropType : 'image/jpeg',
27305     buttons : false,
27306     canvasLoaded : false,
27307     isDocument : false,
27308     method : 'POST',
27309     paramName : 'imageUpload',
27310     loadMask : true,
27311     loadingText : 'Loading...',
27312     maskEl : false,
27313     
27314     getAutoCreate : function()
27315     {
27316         var cfg = {
27317             tag : 'div',
27318             cls : 'roo-upload-cropbox',
27319             cn : [
27320                 {
27321                     tag : 'input',
27322                     cls : 'roo-upload-cropbox-selector',
27323                     type : 'file'
27324                 },
27325                 {
27326                     tag : 'div',
27327                     cls : 'roo-upload-cropbox-body',
27328                     style : 'cursor:pointer',
27329                     cn : [
27330                         {
27331                             tag : 'div',
27332                             cls : 'roo-upload-cropbox-preview'
27333                         },
27334                         {
27335                             tag : 'div',
27336                             cls : 'roo-upload-cropbox-thumb'
27337                         },
27338                         {
27339                             tag : 'div',
27340                             cls : 'roo-upload-cropbox-empty-notify',
27341                             html : this.emptyText
27342                         },
27343                         {
27344                             tag : 'div',
27345                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27346                             html : this.rotateNotify
27347                         }
27348                     ]
27349                 },
27350                 {
27351                     tag : 'div',
27352                     cls : 'roo-upload-cropbox-footer',
27353                     cn : {
27354                         tag : 'div',
27355                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27356                         cn : []
27357                     }
27358                 }
27359             ]
27360         };
27361         
27362         return cfg;
27363     },
27364     
27365     onRender : function(ct, position)
27366     {
27367         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27368         
27369         if (this.buttons.length) {
27370             
27371             Roo.each(this.buttons, function(bb) {
27372                 
27373                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27374                 
27375                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27376                 
27377             }, this);
27378         }
27379         
27380         if(this.loadMask){
27381             this.maskEl = this.el;
27382         }
27383     },
27384     
27385     initEvents : function()
27386     {
27387         this.urlAPI = (window.createObjectURL && window) || 
27388                                 (window.URL && URL.revokeObjectURL && URL) || 
27389                                 (window.webkitURL && webkitURL);
27390                         
27391         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27392         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27393         
27394         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27395         this.selectorEl.hide();
27396         
27397         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27398         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27399         
27400         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27401         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27402         this.thumbEl.hide();
27403         
27404         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27405         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27406         
27407         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27408         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27409         this.errorEl.hide();
27410         
27411         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27412         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27413         this.footerEl.hide();
27414         
27415         this.setThumbBoxSize();
27416         
27417         this.bind();
27418         
27419         this.resize();
27420         
27421         this.fireEvent('initial', this);
27422     },
27423
27424     bind : function()
27425     {
27426         var _this = this;
27427         
27428         window.addEventListener("resize", function() { _this.resize(); } );
27429         
27430         this.bodyEl.on('click', this.beforeSelectFile, this);
27431         
27432         if(Roo.isTouch){
27433             this.bodyEl.on('touchstart', this.onTouchStart, this);
27434             this.bodyEl.on('touchmove', this.onTouchMove, this);
27435             this.bodyEl.on('touchend', this.onTouchEnd, this);
27436         }
27437         
27438         if(!Roo.isTouch){
27439             this.bodyEl.on('mousedown', this.onMouseDown, this);
27440             this.bodyEl.on('mousemove', this.onMouseMove, this);
27441             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27442             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27443             Roo.get(document).on('mouseup', this.onMouseUp, this);
27444         }
27445         
27446         this.selectorEl.on('change', this.onFileSelected, this);
27447     },
27448     
27449     reset : function()
27450     {    
27451         this.scale = 0;
27452         this.baseScale = 1;
27453         this.rotate = 0;
27454         this.baseRotate = 1;
27455         this.dragable = false;
27456         this.pinching = false;
27457         this.mouseX = 0;
27458         this.mouseY = 0;
27459         this.cropData = false;
27460         this.notifyEl.dom.innerHTML = this.emptyText;
27461         
27462         this.selectorEl.dom.value = '';
27463         
27464     },
27465     
27466     resize : function()
27467     {
27468         if(this.fireEvent('resize', this) != false){
27469             this.setThumbBoxPosition();
27470             this.setCanvasPosition();
27471         }
27472     },
27473     
27474     onFooterButtonClick : function(e, el, o, type)
27475     {
27476         switch (type) {
27477             case 'rotate-left' :
27478                 this.onRotateLeft(e);
27479                 break;
27480             case 'rotate-right' :
27481                 this.onRotateRight(e);
27482                 break;
27483             case 'picture' :
27484                 this.beforeSelectFile(e);
27485                 break;
27486             case 'trash' :
27487                 this.trash(e);
27488                 break;
27489             case 'crop' :
27490                 this.crop(e);
27491                 break;
27492             case 'download' :
27493                 this.download(e);
27494                 break;
27495             default :
27496                 break;
27497         }
27498         
27499         this.fireEvent('footerbuttonclick', this, type);
27500     },
27501     
27502     beforeSelectFile : function(e)
27503     {
27504         e.preventDefault();
27505         
27506         if(this.fireEvent('beforeselectfile', this) != false){
27507             this.selectorEl.dom.click();
27508         }
27509     },
27510     
27511     onFileSelected : function(e)
27512     {
27513         e.preventDefault();
27514         
27515         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27516             return;
27517         }
27518         
27519         var file = this.selectorEl.dom.files[0];
27520         
27521         if(this.fireEvent('inspect', this, file) != false){
27522             this.prepare(file);
27523         }
27524         
27525     },
27526     
27527     trash : function(e)
27528     {
27529         this.fireEvent('trash', this);
27530     },
27531     
27532     download : function(e)
27533     {
27534         this.fireEvent('download', this);
27535     },
27536     
27537     loadCanvas : function(src)
27538     {   
27539         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27540             
27541             this.reset();
27542             
27543             this.imageEl = document.createElement('img');
27544             
27545             var _this = this;
27546             
27547             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27548             
27549             this.imageEl.src = src;
27550         }
27551     },
27552     
27553     onLoadCanvas : function()
27554     {   
27555         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27556         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27557         
27558         this.bodyEl.un('click', this.beforeSelectFile, this);
27559         
27560         this.notifyEl.hide();
27561         this.thumbEl.show();
27562         this.footerEl.show();
27563         
27564         this.baseRotateLevel();
27565         
27566         if(this.isDocument){
27567             this.setThumbBoxSize();
27568         }
27569         
27570         this.setThumbBoxPosition();
27571         
27572         this.baseScaleLevel();
27573         
27574         this.draw();
27575         
27576         this.resize();
27577         
27578         this.canvasLoaded = true;
27579         
27580         if(this.loadMask){
27581             this.maskEl.unmask();
27582         }
27583         
27584     },
27585     
27586     setCanvasPosition : function()
27587     {   
27588         if(!this.canvasEl){
27589             return;
27590         }
27591         
27592         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27593         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27594         
27595         this.previewEl.setLeft(pw);
27596         this.previewEl.setTop(ph);
27597         
27598     },
27599     
27600     onMouseDown : function(e)
27601     {   
27602         e.stopEvent();
27603         
27604         this.dragable = true;
27605         this.pinching = false;
27606         
27607         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27608             this.dragable = false;
27609             return;
27610         }
27611         
27612         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27613         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27614         
27615     },
27616     
27617     onMouseMove : function(e)
27618     {   
27619         e.stopEvent();
27620         
27621         if(!this.canvasLoaded){
27622             return;
27623         }
27624         
27625         if (!this.dragable){
27626             return;
27627         }
27628         
27629         var minX = Math.ceil(this.thumbEl.getLeft(true));
27630         var minY = Math.ceil(this.thumbEl.getTop(true));
27631         
27632         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27633         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27634         
27635         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27636         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27637         
27638         x = x - this.mouseX;
27639         y = y - this.mouseY;
27640         
27641         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27642         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27643         
27644         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27645         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27646         
27647         this.previewEl.setLeft(bgX);
27648         this.previewEl.setTop(bgY);
27649         
27650         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27651         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27652     },
27653     
27654     onMouseUp : function(e)
27655     {   
27656         e.stopEvent();
27657         
27658         this.dragable = false;
27659     },
27660     
27661     onMouseWheel : function(e)
27662     {   
27663         e.stopEvent();
27664         
27665         this.startScale = this.scale;
27666         
27667         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27668         
27669         if(!this.zoomable()){
27670             this.scale = this.startScale;
27671             return;
27672         }
27673         
27674         this.draw();
27675         
27676         return;
27677     },
27678     
27679     zoomable : function()
27680     {
27681         var minScale = this.thumbEl.getWidth() / this.minWidth;
27682         
27683         if(this.minWidth < this.minHeight){
27684             minScale = this.thumbEl.getHeight() / this.minHeight;
27685         }
27686         
27687         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27688         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27689         
27690         if(
27691                 this.isDocument &&
27692                 (this.rotate == 0 || this.rotate == 180) && 
27693                 (
27694                     width > this.imageEl.OriginWidth || 
27695                     height > this.imageEl.OriginHeight ||
27696                     (width < this.minWidth && height < this.minHeight)
27697                 )
27698         ){
27699             return false;
27700         }
27701         
27702         if(
27703                 this.isDocument &&
27704                 (this.rotate == 90 || this.rotate == 270) && 
27705                 (
27706                     width > this.imageEl.OriginWidth || 
27707                     height > this.imageEl.OriginHeight ||
27708                     (width < this.minHeight && height < this.minWidth)
27709                 )
27710         ){
27711             return false;
27712         }
27713         
27714         if(
27715                 !this.isDocument &&
27716                 (this.rotate == 0 || this.rotate == 180) && 
27717                 (
27718                     width < this.minWidth || 
27719                     width > this.imageEl.OriginWidth || 
27720                     height < this.minHeight || 
27721                     height > this.imageEl.OriginHeight
27722                 )
27723         ){
27724             return false;
27725         }
27726         
27727         if(
27728                 !this.isDocument &&
27729                 (this.rotate == 90 || this.rotate == 270) && 
27730                 (
27731                     width < this.minHeight || 
27732                     width > this.imageEl.OriginWidth || 
27733                     height < this.minWidth || 
27734                     height > this.imageEl.OriginHeight
27735                 )
27736         ){
27737             return false;
27738         }
27739         
27740         return true;
27741         
27742     },
27743     
27744     onRotateLeft : function(e)
27745     {   
27746         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27747             
27748             var minScale = this.thumbEl.getWidth() / this.minWidth;
27749             
27750             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27751             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27752             
27753             this.startScale = this.scale;
27754             
27755             while (this.getScaleLevel() < minScale){
27756             
27757                 this.scale = this.scale + 1;
27758                 
27759                 if(!this.zoomable()){
27760                     break;
27761                 }
27762                 
27763                 if(
27764                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27765                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27766                 ){
27767                     continue;
27768                 }
27769                 
27770                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27771
27772                 this.draw();
27773                 
27774                 return;
27775             }
27776             
27777             this.scale = this.startScale;
27778             
27779             this.onRotateFail();
27780             
27781             return false;
27782         }
27783         
27784         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27785
27786         if(this.isDocument){
27787             this.setThumbBoxSize();
27788             this.setThumbBoxPosition();
27789             this.setCanvasPosition();
27790         }
27791         
27792         this.draw();
27793         
27794         this.fireEvent('rotate', this, 'left');
27795         
27796     },
27797     
27798     onRotateRight : function(e)
27799     {
27800         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27801             
27802             var minScale = this.thumbEl.getWidth() / this.minWidth;
27803         
27804             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27805             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27806             
27807             this.startScale = this.scale;
27808             
27809             while (this.getScaleLevel() < minScale){
27810             
27811                 this.scale = this.scale + 1;
27812                 
27813                 if(!this.zoomable()){
27814                     break;
27815                 }
27816                 
27817                 if(
27818                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27819                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27820                 ){
27821                     continue;
27822                 }
27823                 
27824                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27825
27826                 this.draw();
27827                 
27828                 return;
27829             }
27830             
27831             this.scale = this.startScale;
27832             
27833             this.onRotateFail();
27834             
27835             return false;
27836         }
27837         
27838         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27839
27840         if(this.isDocument){
27841             this.setThumbBoxSize();
27842             this.setThumbBoxPosition();
27843             this.setCanvasPosition();
27844         }
27845         
27846         this.draw();
27847         
27848         this.fireEvent('rotate', this, 'right');
27849     },
27850     
27851     onRotateFail : function()
27852     {
27853         this.errorEl.show(true);
27854         
27855         var _this = this;
27856         
27857         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27858     },
27859     
27860     draw : function()
27861     {
27862         this.previewEl.dom.innerHTML = '';
27863         
27864         var canvasEl = document.createElement("canvas");
27865         
27866         var contextEl = canvasEl.getContext("2d");
27867         
27868         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27869         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27870         var center = this.imageEl.OriginWidth / 2;
27871         
27872         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27873             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27874             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27875             center = this.imageEl.OriginHeight / 2;
27876         }
27877         
27878         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27879         
27880         contextEl.translate(center, center);
27881         contextEl.rotate(this.rotate * Math.PI / 180);
27882
27883         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27884         
27885         this.canvasEl = document.createElement("canvas");
27886         
27887         this.contextEl = this.canvasEl.getContext("2d");
27888         
27889         switch (this.rotate) {
27890             case 0 :
27891                 
27892                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27893                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27894                 
27895                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27896                 
27897                 break;
27898             case 90 : 
27899                 
27900                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27901                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27902                 
27903                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27904                     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);
27905                     break;
27906                 }
27907                 
27908                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27909                 
27910                 break;
27911             case 180 :
27912                 
27913                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27914                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27915                 
27916                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27917                     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);
27918                     break;
27919                 }
27920                 
27921                 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);
27922                 
27923                 break;
27924             case 270 :
27925                 
27926                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27927                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27928         
27929                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27930                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27931                     break;
27932                 }
27933                 
27934                 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);
27935                 
27936                 break;
27937             default : 
27938                 break;
27939         }
27940         
27941         this.previewEl.appendChild(this.canvasEl);
27942         
27943         this.setCanvasPosition();
27944     },
27945     
27946     crop : function()
27947     {
27948         if(!this.canvasLoaded){
27949             return;
27950         }
27951         
27952         var imageCanvas = document.createElement("canvas");
27953         
27954         var imageContext = imageCanvas.getContext("2d");
27955         
27956         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27957         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27958         
27959         var center = imageCanvas.width / 2;
27960         
27961         imageContext.translate(center, center);
27962         
27963         imageContext.rotate(this.rotate * Math.PI / 180);
27964         
27965         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27966         
27967         var canvas = document.createElement("canvas");
27968         
27969         var context = canvas.getContext("2d");
27970                 
27971         canvas.width = this.minWidth;
27972         canvas.height = this.minHeight;
27973
27974         switch (this.rotate) {
27975             case 0 :
27976                 
27977                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27978                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27979                 
27980                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27981                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27982                 
27983                 var targetWidth = this.minWidth - 2 * x;
27984                 var targetHeight = this.minHeight - 2 * y;
27985                 
27986                 var scale = 1;
27987                 
27988                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27989                     scale = targetWidth / width;
27990                 }
27991                 
27992                 if(x > 0 && y == 0){
27993                     scale = targetHeight / height;
27994                 }
27995                 
27996                 if(x > 0 && y > 0){
27997                     scale = targetWidth / width;
27998                     
27999                     if(width < height){
28000                         scale = targetHeight / height;
28001                     }
28002                 }
28003                 
28004                 context.scale(scale, scale);
28005                 
28006                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28007                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28008
28009                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28010                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28011
28012                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28013                 
28014                 break;
28015             case 90 : 
28016                 
28017                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28018                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28019                 
28020                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28021                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28022                 
28023                 var targetWidth = this.minWidth - 2 * x;
28024                 var targetHeight = this.minHeight - 2 * y;
28025                 
28026                 var scale = 1;
28027                 
28028                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28029                     scale = targetWidth / width;
28030                 }
28031                 
28032                 if(x > 0 && y == 0){
28033                     scale = targetHeight / height;
28034                 }
28035                 
28036                 if(x > 0 && y > 0){
28037                     scale = targetWidth / width;
28038                     
28039                     if(width < height){
28040                         scale = targetHeight / height;
28041                     }
28042                 }
28043                 
28044                 context.scale(scale, scale);
28045                 
28046                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28047                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28048
28049                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28050                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28051                 
28052                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28053                 
28054                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28055                 
28056                 break;
28057             case 180 :
28058                 
28059                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28060                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28061                 
28062                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28063                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28064                 
28065                 var targetWidth = this.minWidth - 2 * x;
28066                 var targetHeight = this.minHeight - 2 * y;
28067                 
28068                 var scale = 1;
28069                 
28070                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28071                     scale = targetWidth / width;
28072                 }
28073                 
28074                 if(x > 0 && y == 0){
28075                     scale = targetHeight / height;
28076                 }
28077                 
28078                 if(x > 0 && y > 0){
28079                     scale = targetWidth / width;
28080                     
28081                     if(width < height){
28082                         scale = targetHeight / height;
28083                     }
28084                 }
28085                 
28086                 context.scale(scale, scale);
28087                 
28088                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28089                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28090
28091                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28092                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28093
28094                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28095                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28096                 
28097                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28098                 
28099                 break;
28100             case 270 :
28101                 
28102                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28103                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28104                 
28105                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28106                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28107                 
28108                 var targetWidth = this.minWidth - 2 * x;
28109                 var targetHeight = this.minHeight - 2 * y;
28110                 
28111                 var scale = 1;
28112                 
28113                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28114                     scale = targetWidth / width;
28115                 }
28116                 
28117                 if(x > 0 && y == 0){
28118                     scale = targetHeight / height;
28119                 }
28120                 
28121                 if(x > 0 && y > 0){
28122                     scale = targetWidth / width;
28123                     
28124                     if(width < height){
28125                         scale = targetHeight / height;
28126                     }
28127                 }
28128                 
28129                 context.scale(scale, scale);
28130                 
28131                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28132                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28133
28134                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28135                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28136                 
28137                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28138                 
28139                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28140                 
28141                 break;
28142             default : 
28143                 break;
28144         }
28145         
28146         this.cropData = canvas.toDataURL(this.cropType);
28147         
28148         if(this.fireEvent('crop', this, this.cropData) !== false){
28149             this.process(this.file, this.cropData);
28150         }
28151         
28152         return;
28153         
28154     },
28155     
28156     setThumbBoxSize : function()
28157     {
28158         var width, height;
28159         
28160         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28161             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28162             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28163             
28164             this.minWidth = width;
28165             this.minHeight = height;
28166             
28167             if(this.rotate == 90 || this.rotate == 270){
28168                 this.minWidth = height;
28169                 this.minHeight = width;
28170             }
28171         }
28172         
28173         height = 300;
28174         width = Math.ceil(this.minWidth * height / this.minHeight);
28175         
28176         if(this.minWidth > this.minHeight){
28177             width = 300;
28178             height = Math.ceil(this.minHeight * width / this.minWidth);
28179         }
28180         
28181         this.thumbEl.setStyle({
28182             width : width + 'px',
28183             height : height + 'px'
28184         });
28185
28186         return;
28187             
28188     },
28189     
28190     setThumbBoxPosition : function()
28191     {
28192         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28193         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28194         
28195         this.thumbEl.setLeft(x);
28196         this.thumbEl.setTop(y);
28197         
28198     },
28199     
28200     baseRotateLevel : function()
28201     {
28202         this.baseRotate = 1;
28203         
28204         if(
28205                 typeof(this.exif) != 'undefined' &&
28206                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28207                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28208         ){
28209             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28210         }
28211         
28212         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28213         
28214     },
28215     
28216     baseScaleLevel : function()
28217     {
28218         var width, height;
28219         
28220         if(this.isDocument){
28221             
28222             if(this.baseRotate == 6 || this.baseRotate == 8){
28223             
28224                 height = this.thumbEl.getHeight();
28225                 this.baseScale = height / this.imageEl.OriginWidth;
28226
28227                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28228                     width = this.thumbEl.getWidth();
28229                     this.baseScale = width / this.imageEl.OriginHeight;
28230                 }
28231
28232                 return;
28233             }
28234
28235             height = this.thumbEl.getHeight();
28236             this.baseScale = height / this.imageEl.OriginHeight;
28237
28238             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28239                 width = this.thumbEl.getWidth();
28240                 this.baseScale = width / this.imageEl.OriginWidth;
28241             }
28242
28243             return;
28244         }
28245         
28246         if(this.baseRotate == 6 || this.baseRotate == 8){
28247             
28248             width = this.thumbEl.getHeight();
28249             this.baseScale = width / this.imageEl.OriginHeight;
28250             
28251             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28252                 height = this.thumbEl.getWidth();
28253                 this.baseScale = height / this.imageEl.OriginHeight;
28254             }
28255             
28256             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28257                 height = this.thumbEl.getWidth();
28258                 this.baseScale = height / this.imageEl.OriginHeight;
28259                 
28260                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28261                     width = this.thumbEl.getHeight();
28262                     this.baseScale = width / this.imageEl.OriginWidth;
28263                 }
28264             }
28265             
28266             return;
28267         }
28268         
28269         width = this.thumbEl.getWidth();
28270         this.baseScale = width / this.imageEl.OriginWidth;
28271         
28272         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28273             height = this.thumbEl.getHeight();
28274             this.baseScale = height / this.imageEl.OriginHeight;
28275         }
28276         
28277         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28278             
28279             height = this.thumbEl.getHeight();
28280             this.baseScale = height / this.imageEl.OriginHeight;
28281             
28282             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28283                 width = this.thumbEl.getWidth();
28284                 this.baseScale = width / this.imageEl.OriginWidth;
28285             }
28286             
28287         }
28288         
28289         return;
28290     },
28291     
28292     getScaleLevel : function()
28293     {
28294         return this.baseScale * Math.pow(1.1, this.scale);
28295     },
28296     
28297     onTouchStart : function(e)
28298     {
28299         if(!this.canvasLoaded){
28300             this.beforeSelectFile(e);
28301             return;
28302         }
28303         
28304         var touches = e.browserEvent.touches;
28305         
28306         if(!touches){
28307             return;
28308         }
28309         
28310         if(touches.length == 1){
28311             this.onMouseDown(e);
28312             return;
28313         }
28314         
28315         if(touches.length != 2){
28316             return;
28317         }
28318         
28319         var coords = [];
28320         
28321         for(var i = 0, finger; finger = touches[i]; i++){
28322             coords.push(finger.pageX, finger.pageY);
28323         }
28324         
28325         var x = Math.pow(coords[0] - coords[2], 2);
28326         var y = Math.pow(coords[1] - coords[3], 2);
28327         
28328         this.startDistance = Math.sqrt(x + y);
28329         
28330         this.startScale = this.scale;
28331         
28332         this.pinching = true;
28333         this.dragable = false;
28334         
28335     },
28336     
28337     onTouchMove : function(e)
28338     {
28339         if(!this.pinching && !this.dragable){
28340             return;
28341         }
28342         
28343         var touches = e.browserEvent.touches;
28344         
28345         if(!touches){
28346             return;
28347         }
28348         
28349         if(this.dragable){
28350             this.onMouseMove(e);
28351             return;
28352         }
28353         
28354         var coords = [];
28355         
28356         for(var i = 0, finger; finger = touches[i]; i++){
28357             coords.push(finger.pageX, finger.pageY);
28358         }
28359         
28360         var x = Math.pow(coords[0] - coords[2], 2);
28361         var y = Math.pow(coords[1] - coords[3], 2);
28362         
28363         this.endDistance = Math.sqrt(x + y);
28364         
28365         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28366         
28367         if(!this.zoomable()){
28368             this.scale = this.startScale;
28369             return;
28370         }
28371         
28372         this.draw();
28373         
28374     },
28375     
28376     onTouchEnd : function(e)
28377     {
28378         this.pinching = false;
28379         this.dragable = false;
28380         
28381     },
28382     
28383     process : function(file, crop)
28384     {
28385         if(this.loadMask){
28386             this.maskEl.mask(this.loadingText);
28387         }
28388         
28389         this.xhr = new XMLHttpRequest();
28390         
28391         file.xhr = this.xhr;
28392
28393         this.xhr.open(this.method, this.url, true);
28394         
28395         var headers = {
28396             "Accept": "application/json",
28397             "Cache-Control": "no-cache",
28398             "X-Requested-With": "XMLHttpRequest"
28399         };
28400         
28401         for (var headerName in headers) {
28402             var headerValue = headers[headerName];
28403             if (headerValue) {
28404                 this.xhr.setRequestHeader(headerName, headerValue);
28405             }
28406         }
28407         
28408         var _this = this;
28409         
28410         this.xhr.onload = function()
28411         {
28412             _this.xhrOnLoad(_this.xhr);
28413         }
28414         
28415         this.xhr.onerror = function()
28416         {
28417             _this.xhrOnError(_this.xhr);
28418         }
28419         
28420         var formData = new FormData();
28421
28422         formData.append('returnHTML', 'NO');
28423         
28424         if(crop){
28425             formData.append('crop', crop);
28426         }
28427         
28428         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28429             formData.append(this.paramName, file, file.name);
28430         }
28431         
28432         if(typeof(file.filename) != 'undefined'){
28433             formData.append('filename', file.filename);
28434         }
28435         
28436         if(typeof(file.mimetype) != 'undefined'){
28437             formData.append('mimetype', file.mimetype);
28438         }
28439         
28440         if(this.fireEvent('arrange', this, formData) != false){
28441             this.xhr.send(formData);
28442         };
28443     },
28444     
28445     xhrOnLoad : function(xhr)
28446     {
28447         if(this.loadMask){
28448             this.maskEl.unmask();
28449         }
28450         
28451         if (xhr.readyState !== 4) {
28452             this.fireEvent('exception', this, xhr);
28453             return;
28454         }
28455
28456         var response = Roo.decode(xhr.responseText);
28457         
28458         if(!response.success){
28459             this.fireEvent('exception', this, xhr);
28460             return;
28461         }
28462         
28463         var response = Roo.decode(xhr.responseText);
28464         
28465         this.fireEvent('upload', this, response);
28466         
28467     },
28468     
28469     xhrOnError : function()
28470     {
28471         if(this.loadMask){
28472             this.maskEl.unmask();
28473         }
28474         
28475         Roo.log('xhr on error');
28476         
28477         var response = Roo.decode(xhr.responseText);
28478           
28479         Roo.log(response);
28480         
28481     },
28482     
28483     prepare : function(file)
28484     {   
28485         if(this.loadMask){
28486             this.maskEl.mask(this.loadingText);
28487         }
28488         
28489         this.file = false;
28490         this.exif = {};
28491         
28492         if(typeof(file) === 'string'){
28493             this.loadCanvas(file);
28494             return;
28495         }
28496         
28497         if(!file || !this.urlAPI){
28498             return;
28499         }
28500         
28501         this.file = file;
28502         this.cropType = file.type;
28503         
28504         var _this = this;
28505         
28506         if(this.fireEvent('prepare', this, this.file) != false){
28507             
28508             var reader = new FileReader();
28509             
28510             reader.onload = function (e) {
28511                 if (e.target.error) {
28512                     Roo.log(e.target.error);
28513                     return;
28514                 }
28515                 
28516                 var buffer = e.target.result,
28517                     dataView = new DataView(buffer),
28518                     offset = 2,
28519                     maxOffset = dataView.byteLength - 4,
28520                     markerBytes,
28521                     markerLength;
28522                 
28523                 if (dataView.getUint16(0) === 0xffd8) {
28524                     while (offset < maxOffset) {
28525                         markerBytes = dataView.getUint16(offset);
28526                         
28527                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28528                             markerLength = dataView.getUint16(offset + 2) + 2;
28529                             if (offset + markerLength > dataView.byteLength) {
28530                                 Roo.log('Invalid meta data: Invalid segment size.');
28531                                 break;
28532                             }
28533                             
28534                             if(markerBytes == 0xffe1){
28535                                 _this.parseExifData(
28536                                     dataView,
28537                                     offset,
28538                                     markerLength
28539                                 );
28540                             }
28541                             
28542                             offset += markerLength;
28543                             
28544                             continue;
28545                         }
28546                         
28547                         break;
28548                     }
28549                     
28550                 }
28551                 
28552                 var url = _this.urlAPI.createObjectURL(_this.file);
28553                 
28554                 _this.loadCanvas(url);
28555                 
28556                 return;
28557             }
28558             
28559             reader.readAsArrayBuffer(this.file);
28560             
28561         }
28562         
28563     },
28564     
28565     parseExifData : function(dataView, offset, length)
28566     {
28567         var tiffOffset = offset + 10,
28568             littleEndian,
28569             dirOffset;
28570     
28571         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28572             // No Exif data, might be XMP data instead
28573             return;
28574         }
28575         
28576         // Check for the ASCII code for "Exif" (0x45786966):
28577         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28578             // No Exif data, might be XMP data instead
28579             return;
28580         }
28581         if (tiffOffset + 8 > dataView.byteLength) {
28582             Roo.log('Invalid Exif data: Invalid segment size.');
28583             return;
28584         }
28585         // Check for the two null bytes:
28586         if (dataView.getUint16(offset + 8) !== 0x0000) {
28587             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28588             return;
28589         }
28590         // Check the byte alignment:
28591         switch (dataView.getUint16(tiffOffset)) {
28592         case 0x4949:
28593             littleEndian = true;
28594             break;
28595         case 0x4D4D:
28596             littleEndian = false;
28597             break;
28598         default:
28599             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28600             return;
28601         }
28602         // Check for the TIFF tag marker (0x002A):
28603         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28604             Roo.log('Invalid Exif data: Missing TIFF marker.');
28605             return;
28606         }
28607         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28608         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28609         
28610         this.parseExifTags(
28611             dataView,
28612             tiffOffset,
28613             tiffOffset + dirOffset,
28614             littleEndian
28615         );
28616     },
28617     
28618     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28619     {
28620         var tagsNumber,
28621             dirEndOffset,
28622             i;
28623         if (dirOffset + 6 > dataView.byteLength) {
28624             Roo.log('Invalid Exif data: Invalid directory offset.');
28625             return;
28626         }
28627         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28628         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28629         if (dirEndOffset + 4 > dataView.byteLength) {
28630             Roo.log('Invalid Exif data: Invalid directory size.');
28631             return;
28632         }
28633         for (i = 0; i < tagsNumber; i += 1) {
28634             this.parseExifTag(
28635                 dataView,
28636                 tiffOffset,
28637                 dirOffset + 2 + 12 * i, // tag offset
28638                 littleEndian
28639             );
28640         }
28641         // Return the offset to the next directory:
28642         return dataView.getUint32(dirEndOffset, littleEndian);
28643     },
28644     
28645     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28646     {
28647         var tag = dataView.getUint16(offset, littleEndian);
28648         
28649         this.exif[tag] = this.getExifValue(
28650             dataView,
28651             tiffOffset,
28652             offset,
28653             dataView.getUint16(offset + 2, littleEndian), // tag type
28654             dataView.getUint32(offset + 4, littleEndian), // tag length
28655             littleEndian
28656         );
28657     },
28658     
28659     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28660     {
28661         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28662             tagSize,
28663             dataOffset,
28664             values,
28665             i,
28666             str,
28667             c;
28668     
28669         if (!tagType) {
28670             Roo.log('Invalid Exif data: Invalid tag type.');
28671             return;
28672         }
28673         
28674         tagSize = tagType.size * length;
28675         // Determine if the value is contained in the dataOffset bytes,
28676         // or if the value at the dataOffset is a pointer to the actual data:
28677         dataOffset = tagSize > 4 ?
28678                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28679         if (dataOffset + tagSize > dataView.byteLength) {
28680             Roo.log('Invalid Exif data: Invalid data offset.');
28681             return;
28682         }
28683         if (length === 1) {
28684             return tagType.getValue(dataView, dataOffset, littleEndian);
28685         }
28686         values = [];
28687         for (i = 0; i < length; i += 1) {
28688             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28689         }
28690         
28691         if (tagType.ascii) {
28692             str = '';
28693             // Concatenate the chars:
28694             for (i = 0; i < values.length; i += 1) {
28695                 c = values[i];
28696                 // Ignore the terminating NULL byte(s):
28697                 if (c === '\u0000') {
28698                     break;
28699                 }
28700                 str += c;
28701             }
28702             return str;
28703         }
28704         return values;
28705     }
28706     
28707 });
28708
28709 Roo.apply(Roo.bootstrap.UploadCropbox, {
28710     tags : {
28711         'Orientation': 0x0112
28712     },
28713     
28714     Orientation: {
28715             1: 0, //'top-left',
28716 //            2: 'top-right',
28717             3: 180, //'bottom-right',
28718 //            4: 'bottom-left',
28719 //            5: 'left-top',
28720             6: 90, //'right-top',
28721 //            7: 'right-bottom',
28722             8: 270 //'left-bottom'
28723     },
28724     
28725     exifTagTypes : {
28726         // byte, 8-bit unsigned int:
28727         1: {
28728             getValue: function (dataView, dataOffset) {
28729                 return dataView.getUint8(dataOffset);
28730             },
28731             size: 1
28732         },
28733         // ascii, 8-bit byte:
28734         2: {
28735             getValue: function (dataView, dataOffset) {
28736                 return String.fromCharCode(dataView.getUint8(dataOffset));
28737             },
28738             size: 1,
28739             ascii: true
28740         },
28741         // short, 16 bit int:
28742         3: {
28743             getValue: function (dataView, dataOffset, littleEndian) {
28744                 return dataView.getUint16(dataOffset, littleEndian);
28745             },
28746             size: 2
28747         },
28748         // long, 32 bit int:
28749         4: {
28750             getValue: function (dataView, dataOffset, littleEndian) {
28751                 return dataView.getUint32(dataOffset, littleEndian);
28752             },
28753             size: 4
28754         },
28755         // rational = two long values, first is numerator, second is denominator:
28756         5: {
28757             getValue: function (dataView, dataOffset, littleEndian) {
28758                 return dataView.getUint32(dataOffset, littleEndian) /
28759                     dataView.getUint32(dataOffset + 4, littleEndian);
28760             },
28761             size: 8
28762         },
28763         // slong, 32 bit signed int:
28764         9: {
28765             getValue: function (dataView, dataOffset, littleEndian) {
28766                 return dataView.getInt32(dataOffset, littleEndian);
28767             },
28768             size: 4
28769         },
28770         // srational, two slongs, first is numerator, second is denominator:
28771         10: {
28772             getValue: function (dataView, dataOffset, littleEndian) {
28773                 return dataView.getInt32(dataOffset, littleEndian) /
28774                     dataView.getInt32(dataOffset + 4, littleEndian);
28775             },
28776             size: 8
28777         }
28778     },
28779     
28780     footer : {
28781         STANDARD : [
28782             {
28783                 tag : 'div',
28784                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28785                 action : 'rotate-left',
28786                 cn : [
28787                     {
28788                         tag : 'button',
28789                         cls : 'btn btn-default',
28790                         html : '<i class="fa fa-undo"></i>'
28791                     }
28792                 ]
28793             },
28794             {
28795                 tag : 'div',
28796                 cls : 'btn-group roo-upload-cropbox-picture',
28797                 action : 'picture',
28798                 cn : [
28799                     {
28800                         tag : 'button',
28801                         cls : 'btn btn-default',
28802                         html : '<i class="fa fa-picture-o"></i>'
28803                     }
28804                 ]
28805             },
28806             {
28807                 tag : 'div',
28808                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28809                 action : 'rotate-right',
28810                 cn : [
28811                     {
28812                         tag : 'button',
28813                         cls : 'btn btn-default',
28814                         html : '<i class="fa fa-repeat"></i>'
28815                     }
28816                 ]
28817             }
28818         ],
28819         DOCUMENT : [
28820             {
28821                 tag : 'div',
28822                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28823                 action : 'rotate-left',
28824                 cn : [
28825                     {
28826                         tag : 'button',
28827                         cls : 'btn btn-default',
28828                         html : '<i class="fa fa-undo"></i>'
28829                     }
28830                 ]
28831             },
28832             {
28833                 tag : 'div',
28834                 cls : 'btn-group roo-upload-cropbox-download',
28835                 action : 'download',
28836                 cn : [
28837                     {
28838                         tag : 'button',
28839                         cls : 'btn btn-default',
28840                         html : '<i class="fa fa-download"></i>'
28841                     }
28842                 ]
28843             },
28844             {
28845                 tag : 'div',
28846                 cls : 'btn-group roo-upload-cropbox-crop',
28847                 action : 'crop',
28848                 cn : [
28849                     {
28850                         tag : 'button',
28851                         cls : 'btn btn-default',
28852                         html : '<i class="fa fa-crop"></i>'
28853                     }
28854                 ]
28855             },
28856             {
28857                 tag : 'div',
28858                 cls : 'btn-group roo-upload-cropbox-trash',
28859                 action : 'trash',
28860                 cn : [
28861                     {
28862                         tag : 'button',
28863                         cls : 'btn btn-default',
28864                         html : '<i class="fa fa-trash"></i>'
28865                     }
28866                 ]
28867             },
28868             {
28869                 tag : 'div',
28870                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28871                 action : 'rotate-right',
28872                 cn : [
28873                     {
28874                         tag : 'button',
28875                         cls : 'btn btn-default',
28876                         html : '<i class="fa fa-repeat"></i>'
28877                     }
28878                 ]
28879             }
28880         ],
28881         ROTATOR : [
28882             {
28883                 tag : 'div',
28884                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28885                 action : 'rotate-left',
28886                 cn : [
28887                     {
28888                         tag : 'button',
28889                         cls : 'btn btn-default',
28890                         html : '<i class="fa fa-undo"></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     }
28908 });
28909
28910 /*
28911 * Licence: LGPL
28912 */
28913
28914 /**
28915  * @class Roo.bootstrap.DocumentManager
28916  * @extends Roo.bootstrap.Component
28917  * Bootstrap DocumentManager class
28918  * @cfg {String} paramName default 'imageUpload'
28919  * @cfg {String} toolTipName default 'filename'
28920  * @cfg {String} method default POST
28921  * @cfg {String} url action url
28922  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28923  * @cfg {Boolean} multiple multiple upload default true
28924  * @cfg {Number} thumbSize default 300
28925  * @cfg {String} fieldLabel
28926  * @cfg {Number} labelWidth default 4
28927  * @cfg {String} labelAlign (left|top) default left
28928  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28929 * @cfg {Number} labellg set the width of label (1-12)
28930  * @cfg {Number} labelmd set the width of label (1-12)
28931  * @cfg {Number} labelsm set the width of label (1-12)
28932  * @cfg {Number} labelxs set the width of label (1-12)
28933  * 
28934  * @constructor
28935  * Create a new DocumentManager
28936  * @param {Object} config The config object
28937  */
28938
28939 Roo.bootstrap.DocumentManager = function(config){
28940     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28941     
28942     this.files = [];
28943     this.delegates = [];
28944     
28945     this.addEvents({
28946         /**
28947          * @event initial
28948          * Fire when initial the DocumentManager
28949          * @param {Roo.bootstrap.DocumentManager} this
28950          */
28951         "initial" : true,
28952         /**
28953          * @event inspect
28954          * inspect selected file
28955          * @param {Roo.bootstrap.DocumentManager} this
28956          * @param {File} file
28957          */
28958         "inspect" : true,
28959         /**
28960          * @event exception
28961          * Fire when xhr load exception
28962          * @param {Roo.bootstrap.DocumentManager} this
28963          * @param {XMLHttpRequest} xhr
28964          */
28965         "exception" : true,
28966         /**
28967          * @event afterupload
28968          * Fire when xhr load exception
28969          * @param {Roo.bootstrap.DocumentManager} this
28970          * @param {XMLHttpRequest} xhr
28971          */
28972         "afterupload" : true,
28973         /**
28974          * @event prepare
28975          * prepare the form data
28976          * @param {Roo.bootstrap.DocumentManager} this
28977          * @param {Object} formData
28978          */
28979         "prepare" : true,
28980         /**
28981          * @event remove
28982          * Fire when remove the file
28983          * @param {Roo.bootstrap.DocumentManager} this
28984          * @param {Object} file
28985          */
28986         "remove" : true,
28987         /**
28988          * @event refresh
28989          * Fire after refresh the file
28990          * @param {Roo.bootstrap.DocumentManager} this
28991          */
28992         "refresh" : true,
28993         /**
28994          * @event click
28995          * Fire after click the image
28996          * @param {Roo.bootstrap.DocumentManager} this
28997          * @param {Object} file
28998          */
28999         "click" : true,
29000         /**
29001          * @event edit
29002          * Fire when upload a image and editable set to true
29003          * @param {Roo.bootstrap.DocumentManager} this
29004          * @param {Object} file
29005          */
29006         "edit" : true,
29007         /**
29008          * @event beforeselectfile
29009          * Fire before select file
29010          * @param {Roo.bootstrap.DocumentManager} this
29011          */
29012         "beforeselectfile" : true,
29013         /**
29014          * @event process
29015          * Fire before process file
29016          * @param {Roo.bootstrap.DocumentManager} this
29017          * @param {Object} file
29018          */
29019         "process" : true,
29020         /**
29021          * @event previewrendered
29022          * Fire when preview rendered
29023          * @param {Roo.bootstrap.DocumentManager} this
29024          * @param {Object} file
29025          */
29026         "previewrendered" : true,
29027         /**
29028          */
29029         "previewResize" : true
29030         
29031     });
29032 };
29033
29034 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29035     
29036     boxes : 0,
29037     inputName : '',
29038     thumbSize : 300,
29039     multiple : true,
29040     files : false,
29041     method : 'POST',
29042     url : '',
29043     paramName : 'imageUpload',
29044     toolTipName : 'filename',
29045     fieldLabel : '',
29046     labelWidth : 4,
29047     labelAlign : 'left',
29048     editable : true,
29049     delegates : false,
29050     xhr : false, 
29051     
29052     labellg : 0,
29053     labelmd : 0,
29054     labelsm : 0,
29055     labelxs : 0,
29056     
29057     getAutoCreate : function()
29058     {   
29059         var managerWidget = {
29060             tag : 'div',
29061             cls : 'roo-document-manager',
29062             cn : [
29063                 {
29064                     tag : 'input',
29065                     cls : 'roo-document-manager-selector',
29066                     type : 'file'
29067                 },
29068                 {
29069                     tag : 'div',
29070                     cls : 'roo-document-manager-uploader',
29071                     cn : [
29072                         {
29073                             tag : 'div',
29074                             cls : 'roo-document-manager-upload-btn',
29075                             html : '<i class="fa fa-plus"></i>'
29076                         }
29077                     ]
29078                     
29079                 }
29080             ]
29081         };
29082         
29083         var content = [
29084             {
29085                 tag : 'div',
29086                 cls : 'column col-md-12',
29087                 cn : managerWidget
29088             }
29089         ];
29090         
29091         if(this.fieldLabel.length){
29092             
29093             content = [
29094                 {
29095                     tag : 'div',
29096                     cls : 'column col-md-12',
29097                     html : this.fieldLabel
29098                 },
29099                 {
29100                     tag : 'div',
29101                     cls : 'column col-md-12',
29102                     cn : managerWidget
29103                 }
29104             ];
29105
29106             if(this.labelAlign == 'left'){
29107                 content = [
29108                     {
29109                         tag : 'div',
29110                         cls : 'column',
29111                         html : this.fieldLabel
29112                     },
29113                     {
29114                         tag : 'div',
29115                         cls : 'column',
29116                         cn : managerWidget
29117                     }
29118                 ];
29119                 
29120                 if(this.labelWidth > 12){
29121                     content[0].style = "width: " + this.labelWidth + 'px';
29122                 }
29123
29124                 if(this.labelWidth < 13 && this.labelmd == 0){
29125                     this.labelmd = this.labelWidth;
29126                 }
29127
29128                 if(this.labellg > 0){
29129                     content[0].cls += ' col-lg-' + this.labellg;
29130                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29131                 }
29132
29133                 if(this.labelmd > 0){
29134                     content[0].cls += ' col-md-' + this.labelmd;
29135                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29136                 }
29137
29138                 if(this.labelsm > 0){
29139                     content[0].cls += ' col-sm-' + this.labelsm;
29140                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29141                 }
29142
29143                 if(this.labelxs > 0){
29144                     content[0].cls += ' col-xs-' + this.labelxs;
29145                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29146                 }
29147                 
29148             }
29149         }
29150         
29151         var cfg = {
29152             tag : 'div',
29153             cls : 'row clearfix',
29154             cn : content
29155         };
29156         
29157         return cfg;
29158         
29159     },
29160     
29161     initEvents : function()
29162     {
29163         this.managerEl = this.el.select('.roo-document-manager', true).first();
29164         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29165         
29166         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29167         this.selectorEl.hide();
29168         
29169         if(this.multiple){
29170             this.selectorEl.attr('multiple', 'multiple');
29171         }
29172         
29173         this.selectorEl.on('change', this.onFileSelected, this);
29174         
29175         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29176         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29177         
29178         this.uploader.on('click', this.onUploaderClick, this);
29179         
29180         this.renderProgressDialog();
29181         
29182         var _this = this;
29183         
29184         window.addEventListener("resize", function() { _this.refresh(); } );
29185         
29186         this.fireEvent('initial', this);
29187     },
29188     
29189     renderProgressDialog : function()
29190     {
29191         var _this = this;
29192         
29193         this.progressDialog = new Roo.bootstrap.Modal({
29194             cls : 'roo-document-manager-progress-dialog',
29195             allow_close : false,
29196             animate : false,
29197             title : '',
29198             buttons : [
29199                 {
29200                     name  :'cancel',
29201                     weight : 'danger',
29202                     html : 'Cancel'
29203                 }
29204             ], 
29205             listeners : { 
29206                 btnclick : function() {
29207                     _this.uploadCancel();
29208                     this.hide();
29209                 }
29210             }
29211         });
29212          
29213         this.progressDialog.render(Roo.get(document.body));
29214          
29215         this.progress = new Roo.bootstrap.Progress({
29216             cls : 'roo-document-manager-progress',
29217             active : true,
29218             striped : true
29219         });
29220         
29221         this.progress.render(this.progressDialog.getChildContainer());
29222         
29223         this.progressBar = new Roo.bootstrap.ProgressBar({
29224             cls : 'roo-document-manager-progress-bar',
29225             aria_valuenow : 0,
29226             aria_valuemin : 0,
29227             aria_valuemax : 12,
29228             panel : 'success'
29229         });
29230         
29231         this.progressBar.render(this.progress.getChildContainer());
29232     },
29233     
29234     onUploaderClick : function(e)
29235     {
29236         e.preventDefault();
29237      
29238         if(this.fireEvent('beforeselectfile', this) != false){
29239             this.selectorEl.dom.click();
29240         }
29241         
29242     },
29243     
29244     onFileSelected : function(e)
29245     {
29246         e.preventDefault();
29247         
29248         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29249             return;
29250         }
29251         
29252         Roo.each(this.selectorEl.dom.files, function(file){
29253             if(this.fireEvent('inspect', this, file) != false){
29254                 this.files.push(file);
29255             }
29256         }, this);
29257         
29258         this.queue();
29259         
29260     },
29261     
29262     queue : function()
29263     {
29264         this.selectorEl.dom.value = '';
29265         
29266         if(!this.files || !this.files.length){
29267             return;
29268         }
29269         
29270         if(this.boxes > 0 && this.files.length > this.boxes){
29271             this.files = this.files.slice(0, this.boxes);
29272         }
29273         
29274         this.uploader.show();
29275         
29276         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29277             this.uploader.hide();
29278         }
29279         
29280         var _this = this;
29281         
29282         var files = [];
29283         
29284         var docs = [];
29285         
29286         Roo.each(this.files, function(file){
29287             
29288             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29289                 var f = this.renderPreview(file);
29290                 files.push(f);
29291                 return;
29292             }
29293             
29294             if(file.type.indexOf('image') != -1){
29295                 this.delegates.push(
29296                     (function(){
29297                         _this.process(file);
29298                     }).createDelegate(this)
29299                 );
29300         
29301                 return;
29302             }
29303             
29304             docs.push(
29305                 (function(){
29306                     _this.process(file);
29307                 }).createDelegate(this)
29308             );
29309             
29310         }, this);
29311         
29312         this.files = files;
29313         
29314         this.delegates = this.delegates.concat(docs);
29315         
29316         if(!this.delegates.length){
29317             this.refresh();
29318             return;
29319         }
29320         
29321         this.progressBar.aria_valuemax = this.delegates.length;
29322         
29323         this.arrange();
29324         
29325         return;
29326     },
29327     
29328     arrange : function()
29329     {
29330         if(!this.delegates.length){
29331             this.progressDialog.hide();
29332             this.refresh();
29333             return;
29334         }
29335         
29336         var delegate = this.delegates.shift();
29337         
29338         this.progressDialog.show();
29339         
29340         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29341         
29342         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29343         
29344         delegate();
29345     },
29346     
29347     refresh : function()
29348     {
29349         this.uploader.show();
29350         
29351         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29352             this.uploader.hide();
29353         }
29354         
29355         Roo.isTouch ? this.closable(false) : this.closable(true);
29356         
29357         this.fireEvent('refresh', this);
29358     },
29359     
29360     onRemove : function(e, el, o)
29361     {
29362         e.preventDefault();
29363         
29364         this.fireEvent('remove', this, o);
29365         
29366     },
29367     
29368     remove : function(o)
29369     {
29370         var files = [];
29371         
29372         Roo.each(this.files, function(file){
29373             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29374                 files.push(file);
29375                 return;
29376             }
29377
29378             o.target.remove();
29379
29380         }, this);
29381         
29382         this.files = files;
29383         
29384         this.refresh();
29385     },
29386     
29387     clear : function()
29388     {
29389         Roo.each(this.files, function(file){
29390             if(!file.target){
29391                 return;
29392             }
29393             
29394             file.target.remove();
29395
29396         }, this);
29397         
29398         this.files = [];
29399         
29400         this.refresh();
29401     },
29402     
29403     onClick : function(e, el, o)
29404     {
29405         e.preventDefault();
29406         
29407         this.fireEvent('click', this, o);
29408         
29409     },
29410     
29411     closable : function(closable)
29412     {
29413         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29414             
29415             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29416             
29417             if(closable){
29418                 el.show();
29419                 return;
29420             }
29421             
29422             el.hide();
29423             
29424         }, this);
29425     },
29426     
29427     xhrOnLoad : function(xhr)
29428     {
29429         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29430             el.remove();
29431         }, this);
29432         
29433         if (xhr.readyState !== 4) {
29434             this.arrange();
29435             this.fireEvent('exception', this, xhr);
29436             return;
29437         }
29438
29439         var response = Roo.decode(xhr.responseText);
29440         
29441         if(!response.success){
29442             this.arrange();
29443             this.fireEvent('exception', this, xhr);
29444             return;
29445         }
29446         
29447         var file = this.renderPreview(response.data);
29448         
29449         this.files.push(file);
29450         
29451         this.arrange();
29452         
29453         this.fireEvent('afterupload', this, xhr);
29454         
29455     },
29456     
29457     xhrOnError : function(xhr)
29458     {
29459         Roo.log('xhr on error');
29460         
29461         var response = Roo.decode(xhr.responseText);
29462           
29463         Roo.log(response);
29464         
29465         this.arrange();
29466     },
29467     
29468     process : function(file)
29469     {
29470         if(this.fireEvent('process', this, file) !== false){
29471             if(this.editable && file.type.indexOf('image') != -1){
29472                 this.fireEvent('edit', this, file);
29473                 return;
29474             }
29475
29476             this.uploadStart(file, false);
29477
29478             return;
29479         }
29480         
29481     },
29482     
29483     uploadStart : function(file, crop)
29484     {
29485         this.xhr = new XMLHttpRequest();
29486         
29487         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29488             this.arrange();
29489             return;
29490         }
29491         
29492         file.xhr = this.xhr;
29493             
29494         this.managerEl.createChild({
29495             tag : 'div',
29496             cls : 'roo-document-manager-loading',
29497             cn : [
29498                 {
29499                     tag : 'div',
29500                     tooltip : file.name,
29501                     cls : 'roo-document-manager-thumb',
29502                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29503                 }
29504             ]
29505
29506         });
29507
29508         this.xhr.open(this.method, this.url, true);
29509         
29510         var headers = {
29511             "Accept": "application/json",
29512             "Cache-Control": "no-cache",
29513             "X-Requested-With": "XMLHttpRequest"
29514         };
29515         
29516         for (var headerName in headers) {
29517             var headerValue = headers[headerName];
29518             if (headerValue) {
29519                 this.xhr.setRequestHeader(headerName, headerValue);
29520             }
29521         }
29522         
29523         var _this = this;
29524         
29525         this.xhr.onload = function()
29526         {
29527             _this.xhrOnLoad(_this.xhr);
29528         }
29529         
29530         this.xhr.onerror = function()
29531         {
29532             _this.xhrOnError(_this.xhr);
29533         }
29534         
29535         var formData = new FormData();
29536
29537         formData.append('returnHTML', 'NO');
29538         
29539         if(crop){
29540             formData.append('crop', crop);
29541         }
29542         
29543         formData.append(this.paramName, file, file.name);
29544         
29545         var options = {
29546             file : file, 
29547             manually : false
29548         };
29549         
29550         if(this.fireEvent('prepare', this, formData, options) != false){
29551             
29552             if(options.manually){
29553                 return;
29554             }
29555             
29556             this.xhr.send(formData);
29557             return;
29558         };
29559         
29560         this.uploadCancel();
29561     },
29562     
29563     uploadCancel : function()
29564     {
29565         if (this.xhr) {
29566             this.xhr.abort();
29567         }
29568         
29569         this.delegates = [];
29570         
29571         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29572             el.remove();
29573         }, this);
29574         
29575         this.arrange();
29576     },
29577     
29578     renderPreview : function(file)
29579     {
29580         if(typeof(file.target) != 'undefined' && file.target){
29581             return file;
29582         }
29583         
29584         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29585         
29586         var previewEl = this.managerEl.createChild({
29587             tag : 'div',
29588             cls : 'roo-document-manager-preview',
29589             cn : [
29590                 {
29591                     tag : 'div',
29592                     tooltip : file[this.toolTipName],
29593                     cls : 'roo-document-manager-thumb',
29594                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29595                 },
29596                 {
29597                     tag : 'button',
29598                     cls : 'close',
29599                     html : '<i class="fa fa-times-circle"></i>'
29600                 }
29601             ]
29602         });
29603
29604         var close = previewEl.select('button.close', true).first();
29605
29606         close.on('click', this.onRemove, this, file);
29607
29608         file.target = previewEl;
29609
29610         var image = previewEl.select('img', true).first();
29611         
29612         var _this = this;
29613         
29614         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29615         
29616         image.on('click', this.onClick, this, file);
29617         
29618         this.fireEvent('previewrendered', this, file);
29619         
29620         return file;
29621         
29622     },
29623     
29624     onPreviewLoad : function(file, image)
29625     {
29626         if(typeof(file.target) == 'undefined' || !file.target){
29627             return;
29628         }
29629         
29630         var width = image.dom.naturalWidth || image.dom.width;
29631         var height = image.dom.naturalHeight || image.dom.height;
29632         
29633         if(!this.previewResize) {
29634             return;
29635         }
29636         
29637         if(width > height){
29638             file.target.addClass('wide');
29639             return;
29640         }
29641         
29642         file.target.addClass('tall');
29643         return;
29644         
29645     },
29646     
29647     uploadFromSource : function(file, crop)
29648     {
29649         this.xhr = new XMLHttpRequest();
29650         
29651         this.managerEl.createChild({
29652             tag : 'div',
29653             cls : 'roo-document-manager-loading',
29654             cn : [
29655                 {
29656                     tag : 'div',
29657                     tooltip : file.name,
29658                     cls : 'roo-document-manager-thumb',
29659                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29660                 }
29661             ]
29662
29663         });
29664
29665         this.xhr.open(this.method, this.url, true);
29666         
29667         var headers = {
29668             "Accept": "application/json",
29669             "Cache-Control": "no-cache",
29670             "X-Requested-With": "XMLHttpRequest"
29671         };
29672         
29673         for (var headerName in headers) {
29674             var headerValue = headers[headerName];
29675             if (headerValue) {
29676                 this.xhr.setRequestHeader(headerName, headerValue);
29677             }
29678         }
29679         
29680         var _this = this;
29681         
29682         this.xhr.onload = function()
29683         {
29684             _this.xhrOnLoad(_this.xhr);
29685         }
29686         
29687         this.xhr.onerror = function()
29688         {
29689             _this.xhrOnError(_this.xhr);
29690         }
29691         
29692         var formData = new FormData();
29693
29694         formData.append('returnHTML', 'NO');
29695         
29696         formData.append('crop', crop);
29697         
29698         if(typeof(file.filename) != 'undefined'){
29699             formData.append('filename', file.filename);
29700         }
29701         
29702         if(typeof(file.mimetype) != 'undefined'){
29703             formData.append('mimetype', file.mimetype);
29704         }
29705         
29706         Roo.log(formData);
29707         
29708         if(this.fireEvent('prepare', this, formData) != false){
29709             this.xhr.send(formData);
29710         };
29711     }
29712 });
29713
29714 /*
29715 * Licence: LGPL
29716 */
29717
29718 /**
29719  * @class Roo.bootstrap.DocumentViewer
29720  * @extends Roo.bootstrap.Component
29721  * Bootstrap DocumentViewer class
29722  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29723  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29724  * 
29725  * @constructor
29726  * Create a new DocumentViewer
29727  * @param {Object} config The config object
29728  */
29729
29730 Roo.bootstrap.DocumentViewer = function(config){
29731     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29732     
29733     this.addEvents({
29734         /**
29735          * @event initial
29736          * Fire after initEvent
29737          * @param {Roo.bootstrap.DocumentViewer} this
29738          */
29739         "initial" : true,
29740         /**
29741          * @event click
29742          * Fire after click
29743          * @param {Roo.bootstrap.DocumentViewer} this
29744          */
29745         "click" : true,
29746         /**
29747          * @event download
29748          * Fire after download button
29749          * @param {Roo.bootstrap.DocumentViewer} this
29750          */
29751         "download" : true,
29752         /**
29753          * @event trash
29754          * Fire after trash button
29755          * @param {Roo.bootstrap.DocumentViewer} this
29756          */
29757         "trash" : true
29758         
29759     });
29760 };
29761
29762 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29763     
29764     showDownload : true,
29765     
29766     showTrash : true,
29767     
29768     getAutoCreate : function()
29769     {
29770         var cfg = {
29771             tag : 'div',
29772             cls : 'roo-document-viewer',
29773             cn : [
29774                 {
29775                     tag : 'div',
29776                     cls : 'roo-document-viewer-body',
29777                     cn : [
29778                         {
29779                             tag : 'div',
29780                             cls : 'roo-document-viewer-thumb',
29781                             cn : [
29782                                 {
29783                                     tag : 'img',
29784                                     cls : 'roo-document-viewer-image'
29785                                 }
29786                             ]
29787                         }
29788                     ]
29789                 },
29790                 {
29791                     tag : 'div',
29792                     cls : 'roo-document-viewer-footer',
29793                     cn : {
29794                         tag : 'div',
29795                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29796                         cn : [
29797                             {
29798                                 tag : 'div',
29799                                 cls : 'btn-group roo-document-viewer-download',
29800                                 cn : [
29801                                     {
29802                                         tag : 'button',
29803                                         cls : 'btn btn-default',
29804                                         html : '<i class="fa fa-download"></i>'
29805                                     }
29806                                 ]
29807                             },
29808                             {
29809                                 tag : 'div',
29810                                 cls : 'btn-group roo-document-viewer-trash',
29811                                 cn : [
29812                                     {
29813                                         tag : 'button',
29814                                         cls : 'btn btn-default',
29815                                         html : '<i class="fa fa-trash"></i>'
29816                                     }
29817                                 ]
29818                             }
29819                         ]
29820                     }
29821                 }
29822             ]
29823         };
29824         
29825         return cfg;
29826     },
29827     
29828     initEvents : function()
29829     {
29830         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29831         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29832         
29833         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29834         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29835         
29836         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29837         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29838         
29839         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29840         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29841         
29842         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29843         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29844         
29845         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29846         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29847         
29848         this.bodyEl.on('click', this.onClick, this);
29849         this.downloadBtn.on('click', this.onDownload, this);
29850         this.trashBtn.on('click', this.onTrash, this);
29851         
29852         this.downloadBtn.hide();
29853         this.trashBtn.hide();
29854         
29855         if(this.showDownload){
29856             this.downloadBtn.show();
29857         }
29858         
29859         if(this.showTrash){
29860             this.trashBtn.show();
29861         }
29862         
29863         if(!this.showDownload && !this.showTrash) {
29864             this.footerEl.hide();
29865         }
29866         
29867     },
29868     
29869     initial : function()
29870     {
29871         this.fireEvent('initial', this);
29872         
29873     },
29874     
29875     onClick : function(e)
29876     {
29877         e.preventDefault();
29878         
29879         this.fireEvent('click', this);
29880     },
29881     
29882     onDownload : function(e)
29883     {
29884         e.preventDefault();
29885         
29886         this.fireEvent('download', this);
29887     },
29888     
29889     onTrash : function(e)
29890     {
29891         e.preventDefault();
29892         
29893         this.fireEvent('trash', this);
29894     }
29895     
29896 });
29897 /*
29898  * - LGPL
29899  *
29900  * nav progress bar
29901  * 
29902  */
29903
29904 /**
29905  * @class Roo.bootstrap.NavProgressBar
29906  * @extends Roo.bootstrap.Component
29907  * Bootstrap NavProgressBar class
29908  * 
29909  * @constructor
29910  * Create a new nav progress bar
29911  * @param {Object} config The config object
29912  */
29913
29914 Roo.bootstrap.NavProgressBar = function(config){
29915     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29916
29917     this.bullets = this.bullets || [];
29918    
29919 //    Roo.bootstrap.NavProgressBar.register(this);
29920      this.addEvents({
29921         /**
29922              * @event changed
29923              * Fires when the active item changes
29924              * @param {Roo.bootstrap.NavProgressBar} this
29925              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29926              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29927          */
29928         'changed': true
29929      });
29930     
29931 };
29932
29933 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29934     
29935     bullets : [],
29936     barItems : [],
29937     
29938     getAutoCreate : function()
29939     {
29940         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29941         
29942         cfg = {
29943             tag : 'div',
29944             cls : 'roo-navigation-bar-group',
29945             cn : [
29946                 {
29947                     tag : 'div',
29948                     cls : 'roo-navigation-top-bar'
29949                 },
29950                 {
29951                     tag : 'div',
29952                     cls : 'roo-navigation-bullets-bar',
29953                     cn : [
29954                         {
29955                             tag : 'ul',
29956                             cls : 'roo-navigation-bar'
29957                         }
29958                     ]
29959                 },
29960                 
29961                 {
29962                     tag : 'div',
29963                     cls : 'roo-navigation-bottom-bar'
29964                 }
29965             ]
29966             
29967         };
29968         
29969         return cfg;
29970         
29971     },
29972     
29973     initEvents: function() 
29974     {
29975         
29976     },
29977     
29978     onRender : function(ct, position) 
29979     {
29980         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29981         
29982         if(this.bullets.length){
29983             Roo.each(this.bullets, function(b){
29984                this.addItem(b);
29985             }, this);
29986         }
29987         
29988         this.format();
29989         
29990     },
29991     
29992     addItem : function(cfg)
29993     {
29994         var item = new Roo.bootstrap.NavProgressItem(cfg);
29995         
29996         item.parentId = this.id;
29997         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29998         
29999         if(cfg.html){
30000             var top = new Roo.bootstrap.Element({
30001                 tag : 'div',
30002                 cls : 'roo-navigation-bar-text'
30003             });
30004             
30005             var bottom = new Roo.bootstrap.Element({
30006                 tag : 'div',
30007                 cls : 'roo-navigation-bar-text'
30008             });
30009             
30010             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30011             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30012             
30013             var topText = new Roo.bootstrap.Element({
30014                 tag : 'span',
30015                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30016             });
30017             
30018             var bottomText = new Roo.bootstrap.Element({
30019                 tag : 'span',
30020                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30021             });
30022             
30023             topText.onRender(top.el, null);
30024             bottomText.onRender(bottom.el, null);
30025             
30026             item.topEl = top;
30027             item.bottomEl = bottom;
30028         }
30029         
30030         this.barItems.push(item);
30031         
30032         return item;
30033     },
30034     
30035     getActive : function()
30036     {
30037         var active = false;
30038         
30039         Roo.each(this.barItems, function(v){
30040             
30041             if (!v.isActive()) {
30042                 return;
30043             }
30044             
30045             active = v;
30046             return false;
30047             
30048         });
30049         
30050         return active;
30051     },
30052     
30053     setActiveItem : function(item)
30054     {
30055         var prev = false;
30056         
30057         Roo.each(this.barItems, function(v){
30058             if (v.rid == item.rid) {
30059                 return ;
30060             }
30061             
30062             if (v.isActive()) {
30063                 v.setActive(false);
30064                 prev = v;
30065             }
30066         });
30067
30068         item.setActive(true);
30069         
30070         this.fireEvent('changed', this, item, prev);
30071     },
30072     
30073     getBarItem: function(rid)
30074     {
30075         var ret = false;
30076         
30077         Roo.each(this.barItems, function(e) {
30078             if (e.rid != rid) {
30079                 return;
30080             }
30081             
30082             ret =  e;
30083             return false;
30084         });
30085         
30086         return ret;
30087     },
30088     
30089     indexOfItem : function(item)
30090     {
30091         var index = false;
30092         
30093         Roo.each(this.barItems, function(v, i){
30094             
30095             if (v.rid != item.rid) {
30096                 return;
30097             }
30098             
30099             index = i;
30100             return false
30101         });
30102         
30103         return index;
30104     },
30105     
30106     setActiveNext : function()
30107     {
30108         var i = this.indexOfItem(this.getActive());
30109         
30110         if (i > this.barItems.length) {
30111             return;
30112         }
30113         
30114         this.setActiveItem(this.barItems[i+1]);
30115     },
30116     
30117     setActivePrev : function()
30118     {
30119         var i = this.indexOfItem(this.getActive());
30120         
30121         if (i  < 1) {
30122             return;
30123         }
30124         
30125         this.setActiveItem(this.barItems[i-1]);
30126     },
30127     
30128     format : function()
30129     {
30130         if(!this.barItems.length){
30131             return;
30132         }
30133      
30134         var width = 100 / this.barItems.length;
30135         
30136         Roo.each(this.barItems, function(i){
30137             i.el.setStyle('width', width + '%');
30138             i.topEl.el.setStyle('width', width + '%');
30139             i.bottomEl.el.setStyle('width', width + '%');
30140         }, this);
30141         
30142     }
30143     
30144 });
30145 /*
30146  * - LGPL
30147  *
30148  * Nav Progress Item
30149  * 
30150  */
30151
30152 /**
30153  * @class Roo.bootstrap.NavProgressItem
30154  * @extends Roo.bootstrap.Component
30155  * Bootstrap NavProgressItem class
30156  * @cfg {String} rid the reference id
30157  * @cfg {Boolean} active (true|false) Is item active default false
30158  * @cfg {Boolean} disabled (true|false) Is item active default false
30159  * @cfg {String} html
30160  * @cfg {String} position (top|bottom) text position default bottom
30161  * @cfg {String} icon show icon instead of number
30162  * 
30163  * @constructor
30164  * Create a new NavProgressItem
30165  * @param {Object} config The config object
30166  */
30167 Roo.bootstrap.NavProgressItem = function(config){
30168     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30169     this.addEvents({
30170         // raw events
30171         /**
30172          * @event click
30173          * The raw click event for the entire grid.
30174          * @param {Roo.bootstrap.NavProgressItem} this
30175          * @param {Roo.EventObject} e
30176          */
30177         "click" : true
30178     });
30179    
30180 };
30181
30182 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30183     
30184     rid : '',
30185     active : false,
30186     disabled : false,
30187     html : '',
30188     position : 'bottom',
30189     icon : false,
30190     
30191     getAutoCreate : function()
30192     {
30193         var iconCls = 'roo-navigation-bar-item-icon';
30194         
30195         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30196         
30197         var cfg = {
30198             tag: 'li',
30199             cls: 'roo-navigation-bar-item',
30200             cn : [
30201                 {
30202                     tag : 'i',
30203                     cls : iconCls
30204                 }
30205             ]
30206         };
30207         
30208         if(this.active){
30209             cfg.cls += ' active';
30210         }
30211         if(this.disabled){
30212             cfg.cls += ' disabled';
30213         }
30214         
30215         return cfg;
30216     },
30217     
30218     disable : function()
30219     {
30220         this.setDisabled(true);
30221     },
30222     
30223     enable : function()
30224     {
30225         this.setDisabled(false);
30226     },
30227     
30228     initEvents: function() 
30229     {
30230         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30231         
30232         this.iconEl.on('click', this.onClick, this);
30233     },
30234     
30235     onClick : function(e)
30236     {
30237         e.preventDefault();
30238         
30239         if(this.disabled){
30240             return;
30241         }
30242         
30243         if(this.fireEvent('click', this, e) === false){
30244             return;
30245         };
30246         
30247         this.parent().setActiveItem(this);
30248     },
30249     
30250     isActive: function () 
30251     {
30252         return this.active;
30253     },
30254     
30255     setActive : function(state)
30256     {
30257         if(this.active == state){
30258             return;
30259         }
30260         
30261         this.active = state;
30262         
30263         if (state) {
30264             this.el.addClass('active');
30265             return;
30266         }
30267         
30268         this.el.removeClass('active');
30269         
30270         return;
30271     },
30272     
30273     setDisabled : function(state)
30274     {
30275         if(this.disabled == state){
30276             return;
30277         }
30278         
30279         this.disabled = state;
30280         
30281         if (state) {
30282             this.el.addClass('disabled');
30283             return;
30284         }
30285         
30286         this.el.removeClass('disabled');
30287     },
30288     
30289     tooltipEl : function()
30290     {
30291         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30292     }
30293 });
30294  
30295
30296  /*
30297  * - LGPL
30298  *
30299  * FieldLabel
30300  * 
30301  */
30302
30303 /**
30304  * @class Roo.bootstrap.FieldLabel
30305  * @extends Roo.bootstrap.Component
30306  * Bootstrap FieldLabel class
30307  * @cfg {String} html contents of the element
30308  * @cfg {String} tag tag of the element default label
30309  * @cfg {String} cls class of the element
30310  * @cfg {String} target label target 
30311  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30312  * @cfg {String} invalidClass default "text-warning"
30313  * @cfg {String} validClass default "text-success"
30314  * @cfg {String} iconTooltip default "This field is required"
30315  * @cfg {String} indicatorpos (left|right) default left
30316  * 
30317  * @constructor
30318  * Create a new FieldLabel
30319  * @param {Object} config The config object
30320  */
30321
30322 Roo.bootstrap.FieldLabel = function(config){
30323     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30324     
30325     this.addEvents({
30326             /**
30327              * @event invalid
30328              * Fires after the field has been marked as invalid.
30329              * @param {Roo.form.FieldLabel} this
30330              * @param {String} msg The validation message
30331              */
30332             invalid : true,
30333             /**
30334              * @event valid
30335              * Fires after the field has been validated with no errors.
30336              * @param {Roo.form.FieldLabel} this
30337              */
30338             valid : true
30339         });
30340 };
30341
30342 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30343     
30344     tag: 'label',
30345     cls: '',
30346     html: '',
30347     target: '',
30348     allowBlank : true,
30349     invalidClass : 'has-warning',
30350     validClass : 'has-success',
30351     iconTooltip : 'This field is required',
30352     indicatorpos : 'left',
30353     
30354     getAutoCreate : function(){
30355         
30356         var cls = "";
30357         if (!this.allowBlank) {
30358             cls  = "visible";
30359         }
30360         
30361         var cfg = {
30362             tag : this.tag,
30363             cls : 'roo-bootstrap-field-label ' + this.cls,
30364             for : this.target,
30365             cn : [
30366                 {
30367                     tag : 'i',
30368                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30369                     tooltip : this.iconTooltip
30370                 },
30371                 {
30372                     tag : 'span',
30373                     html : this.html
30374                 }
30375             ] 
30376         };
30377         
30378         if(this.indicatorpos == 'right'){
30379             var cfg = {
30380                 tag : this.tag,
30381                 cls : 'roo-bootstrap-field-label ' + this.cls,
30382                 for : this.target,
30383                 cn : [
30384                     {
30385                         tag : 'span',
30386                         html : this.html
30387                     },
30388                     {
30389                         tag : 'i',
30390                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30391                         tooltip : this.iconTooltip
30392                     }
30393                 ] 
30394             };
30395         }
30396         
30397         return cfg;
30398     },
30399     
30400     initEvents: function() 
30401     {
30402         Roo.bootstrap.Element.superclass.initEvents.call(this);
30403         
30404         this.indicator = this.indicatorEl();
30405         
30406         if(this.indicator){
30407             this.indicator.removeClass('visible');
30408             this.indicator.addClass('invisible');
30409         }
30410         
30411         Roo.bootstrap.FieldLabel.register(this);
30412     },
30413     
30414     indicatorEl : function()
30415     {
30416         var indicator = this.el.select('i.roo-required-indicator',true).first();
30417         
30418         if(!indicator){
30419             return false;
30420         }
30421         
30422         return indicator;
30423         
30424     },
30425     
30426     /**
30427      * Mark this field as valid
30428      */
30429     markValid : function()
30430     {
30431         if(this.indicator){
30432             this.indicator.removeClass('visible');
30433             this.indicator.addClass('invisible');
30434         }
30435         
30436         this.el.removeClass(this.invalidClass);
30437         
30438         this.el.addClass(this.validClass);
30439         
30440         this.fireEvent('valid', this);
30441     },
30442     
30443     /**
30444      * Mark this field as invalid
30445      * @param {String} msg The validation message
30446      */
30447     markInvalid : function(msg)
30448     {
30449         if(this.indicator){
30450             this.indicator.removeClass('invisible');
30451             this.indicator.addClass('visible');
30452         }
30453         
30454         this.el.removeClass(this.validClass);
30455         
30456         this.el.addClass(this.invalidClass);
30457         
30458         this.fireEvent('invalid', this, msg);
30459     }
30460     
30461    
30462 });
30463
30464 Roo.apply(Roo.bootstrap.FieldLabel, {
30465     
30466     groups: {},
30467     
30468      /**
30469     * register a FieldLabel Group
30470     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30471     */
30472     register : function(label)
30473     {
30474         if(this.groups.hasOwnProperty(label.target)){
30475             return;
30476         }
30477      
30478         this.groups[label.target] = label;
30479         
30480     },
30481     /**
30482     * fetch a FieldLabel Group based on the target
30483     * @param {string} target
30484     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30485     */
30486     get: function(target) {
30487         if (typeof(this.groups[target]) == 'undefined') {
30488             return false;
30489         }
30490         
30491         return this.groups[target] ;
30492     }
30493 });
30494
30495  
30496
30497  /*
30498  * - LGPL
30499  *
30500  * page DateSplitField.
30501  * 
30502  */
30503
30504
30505 /**
30506  * @class Roo.bootstrap.DateSplitField
30507  * @extends Roo.bootstrap.Component
30508  * Bootstrap DateSplitField class
30509  * @cfg {string} fieldLabel - the label associated
30510  * @cfg {Number} labelWidth set the width of label (0-12)
30511  * @cfg {String} labelAlign (top|left)
30512  * @cfg {Boolean} dayAllowBlank (true|false) default false
30513  * @cfg {Boolean} monthAllowBlank (true|false) default false
30514  * @cfg {Boolean} yearAllowBlank (true|false) default false
30515  * @cfg {string} dayPlaceholder 
30516  * @cfg {string} monthPlaceholder
30517  * @cfg {string} yearPlaceholder
30518  * @cfg {string} dayFormat default 'd'
30519  * @cfg {string} monthFormat default 'm'
30520  * @cfg {string} yearFormat default 'Y'
30521  * @cfg {Number} labellg set the width of label (1-12)
30522  * @cfg {Number} labelmd set the width of label (1-12)
30523  * @cfg {Number} labelsm set the width of label (1-12)
30524  * @cfg {Number} labelxs set the width of label (1-12)
30525
30526  *     
30527  * @constructor
30528  * Create a new DateSplitField
30529  * @param {Object} config The config object
30530  */
30531
30532 Roo.bootstrap.DateSplitField = function(config){
30533     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30534     
30535     this.addEvents({
30536         // raw events
30537          /**
30538          * @event years
30539          * getting the data of years
30540          * @param {Roo.bootstrap.DateSplitField} this
30541          * @param {Object} years
30542          */
30543         "years" : true,
30544         /**
30545          * @event days
30546          * getting the data of days
30547          * @param {Roo.bootstrap.DateSplitField} this
30548          * @param {Object} days
30549          */
30550         "days" : true,
30551         /**
30552          * @event invalid
30553          * Fires after the field has been marked as invalid.
30554          * @param {Roo.form.Field} this
30555          * @param {String} msg The validation message
30556          */
30557         invalid : true,
30558        /**
30559          * @event valid
30560          * Fires after the field has been validated with no errors.
30561          * @param {Roo.form.Field} this
30562          */
30563         valid : true
30564     });
30565 };
30566
30567 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30568     
30569     fieldLabel : '',
30570     labelAlign : 'top',
30571     labelWidth : 3,
30572     dayAllowBlank : false,
30573     monthAllowBlank : false,
30574     yearAllowBlank : false,
30575     dayPlaceholder : '',
30576     monthPlaceholder : '',
30577     yearPlaceholder : '',
30578     dayFormat : 'd',
30579     monthFormat : 'm',
30580     yearFormat : 'Y',
30581     isFormField : true,
30582     labellg : 0,
30583     labelmd : 0,
30584     labelsm : 0,
30585     labelxs : 0,
30586     
30587     getAutoCreate : function()
30588     {
30589         var cfg = {
30590             tag : 'div',
30591             cls : 'row roo-date-split-field-group',
30592             cn : [
30593                 {
30594                     tag : 'input',
30595                     type : 'hidden',
30596                     cls : 'form-hidden-field roo-date-split-field-group-value',
30597                     name : this.name
30598                 }
30599             ]
30600         };
30601         
30602         var labelCls = 'col-md-12';
30603         var contentCls = 'col-md-4';
30604         
30605         if(this.fieldLabel){
30606             
30607             var label = {
30608                 tag : 'div',
30609                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30610                 cn : [
30611                     {
30612                         tag : 'label',
30613                         html : this.fieldLabel
30614                     }
30615                 ]
30616             };
30617             
30618             if(this.labelAlign == 'left'){
30619             
30620                 if(this.labelWidth > 12){
30621                     label.style = "width: " + this.labelWidth + 'px';
30622                 }
30623
30624                 if(this.labelWidth < 13 && this.labelmd == 0){
30625                     this.labelmd = this.labelWidth;
30626                 }
30627
30628                 if(this.labellg > 0){
30629                     labelCls = ' col-lg-' + this.labellg;
30630                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30631                 }
30632
30633                 if(this.labelmd > 0){
30634                     labelCls = ' col-md-' + this.labelmd;
30635                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30636                 }
30637
30638                 if(this.labelsm > 0){
30639                     labelCls = ' col-sm-' + this.labelsm;
30640                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30641                 }
30642
30643                 if(this.labelxs > 0){
30644                     labelCls = ' col-xs-' + this.labelxs;
30645                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30646                 }
30647             }
30648             
30649             label.cls += ' ' + labelCls;
30650             
30651             cfg.cn.push(label);
30652         }
30653         
30654         Roo.each(['day', 'month', 'year'], function(t){
30655             cfg.cn.push({
30656                 tag : 'div',
30657                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30658             });
30659         }, this);
30660         
30661         return cfg;
30662     },
30663     
30664     inputEl: function ()
30665     {
30666         return this.el.select('.roo-date-split-field-group-value', true).first();
30667     },
30668     
30669     onRender : function(ct, position) 
30670     {
30671         var _this = this;
30672         
30673         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30674         
30675         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30676         
30677         this.dayField = new Roo.bootstrap.ComboBox({
30678             allowBlank : this.dayAllowBlank,
30679             alwaysQuery : true,
30680             displayField : 'value',
30681             editable : false,
30682             fieldLabel : '',
30683             forceSelection : true,
30684             mode : 'local',
30685             placeholder : this.dayPlaceholder,
30686             selectOnFocus : true,
30687             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30688             triggerAction : 'all',
30689             typeAhead : true,
30690             valueField : 'value',
30691             store : new Roo.data.SimpleStore({
30692                 data : (function() {    
30693                     var days = [];
30694                     _this.fireEvent('days', _this, days);
30695                     return days;
30696                 })(),
30697                 fields : [ 'value' ]
30698             }),
30699             listeners : {
30700                 select : function (_self, record, index)
30701                 {
30702                     _this.setValue(_this.getValue());
30703                 }
30704             }
30705         });
30706
30707         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30708         
30709         this.monthField = new Roo.bootstrap.MonthField({
30710             after : '<i class=\"fa fa-calendar\"></i>',
30711             allowBlank : this.monthAllowBlank,
30712             placeholder : this.monthPlaceholder,
30713             readOnly : true,
30714             listeners : {
30715                 render : function (_self)
30716                 {
30717                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30718                         e.preventDefault();
30719                         _self.focus();
30720                     });
30721                 },
30722                 select : function (_self, oldvalue, newvalue)
30723                 {
30724                     _this.setValue(_this.getValue());
30725                 }
30726             }
30727         });
30728         
30729         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30730         
30731         this.yearField = new Roo.bootstrap.ComboBox({
30732             allowBlank : this.yearAllowBlank,
30733             alwaysQuery : true,
30734             displayField : 'value',
30735             editable : false,
30736             fieldLabel : '',
30737             forceSelection : true,
30738             mode : 'local',
30739             placeholder : this.yearPlaceholder,
30740             selectOnFocus : true,
30741             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30742             triggerAction : 'all',
30743             typeAhead : true,
30744             valueField : 'value',
30745             store : new Roo.data.SimpleStore({
30746                 data : (function() {
30747                     var years = [];
30748                     _this.fireEvent('years', _this, years);
30749                     return years;
30750                 })(),
30751                 fields : [ 'value' ]
30752             }),
30753             listeners : {
30754                 select : function (_self, record, index)
30755                 {
30756                     _this.setValue(_this.getValue());
30757                 }
30758             }
30759         });
30760
30761         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30762     },
30763     
30764     setValue : function(v, format)
30765     {
30766         this.inputEl.dom.value = v;
30767         
30768         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30769         
30770         var d = Date.parseDate(v, f);
30771         
30772         if(!d){
30773             this.validate();
30774             return;
30775         }
30776         
30777         this.setDay(d.format(this.dayFormat));
30778         this.setMonth(d.format(this.monthFormat));
30779         this.setYear(d.format(this.yearFormat));
30780         
30781         this.validate();
30782         
30783         return;
30784     },
30785     
30786     setDay : function(v)
30787     {
30788         this.dayField.setValue(v);
30789         this.inputEl.dom.value = this.getValue();
30790         this.validate();
30791         return;
30792     },
30793     
30794     setMonth : function(v)
30795     {
30796         this.monthField.setValue(v, true);
30797         this.inputEl.dom.value = this.getValue();
30798         this.validate();
30799         return;
30800     },
30801     
30802     setYear : function(v)
30803     {
30804         this.yearField.setValue(v);
30805         this.inputEl.dom.value = this.getValue();
30806         this.validate();
30807         return;
30808     },
30809     
30810     getDay : function()
30811     {
30812         return this.dayField.getValue();
30813     },
30814     
30815     getMonth : function()
30816     {
30817         return this.monthField.getValue();
30818     },
30819     
30820     getYear : function()
30821     {
30822         return this.yearField.getValue();
30823     },
30824     
30825     getValue : function()
30826     {
30827         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30828         
30829         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30830         
30831         return date;
30832     },
30833     
30834     reset : function()
30835     {
30836         this.setDay('');
30837         this.setMonth('');
30838         this.setYear('');
30839         this.inputEl.dom.value = '';
30840         this.validate();
30841         return;
30842     },
30843     
30844     validate : function()
30845     {
30846         var d = this.dayField.validate();
30847         var m = this.monthField.validate();
30848         var y = this.yearField.validate();
30849         
30850         var valid = true;
30851         
30852         if(
30853                 (!this.dayAllowBlank && !d) ||
30854                 (!this.monthAllowBlank && !m) ||
30855                 (!this.yearAllowBlank && !y)
30856         ){
30857             valid = false;
30858         }
30859         
30860         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30861             return valid;
30862         }
30863         
30864         if(valid){
30865             this.markValid();
30866             return valid;
30867         }
30868         
30869         this.markInvalid();
30870         
30871         return valid;
30872     },
30873     
30874     markValid : function()
30875     {
30876         
30877         var label = this.el.select('label', true).first();
30878         var icon = this.el.select('i.fa-star', true).first();
30879
30880         if(label && icon){
30881             icon.remove();
30882         }
30883         
30884         this.fireEvent('valid', this);
30885     },
30886     
30887      /**
30888      * Mark this field as invalid
30889      * @param {String} msg The validation message
30890      */
30891     markInvalid : function(msg)
30892     {
30893         
30894         var label = this.el.select('label', true).first();
30895         var icon = this.el.select('i.fa-star', true).first();
30896
30897         if(label && !icon){
30898             this.el.select('.roo-date-split-field-label', true).createChild({
30899                 tag : 'i',
30900                 cls : 'text-danger fa fa-lg fa-star',
30901                 tooltip : 'This field is required',
30902                 style : 'margin-right:5px;'
30903             }, label, true);
30904         }
30905         
30906         this.fireEvent('invalid', this, msg);
30907     },
30908     
30909     clearInvalid : function()
30910     {
30911         var label = this.el.select('label', true).first();
30912         var icon = this.el.select('i.fa-star', true).first();
30913
30914         if(label && icon){
30915             icon.remove();
30916         }
30917         
30918         this.fireEvent('valid', this);
30919     },
30920     
30921     getName: function()
30922     {
30923         return this.name;
30924     }
30925     
30926 });
30927
30928  /**
30929  *
30930  * This is based on 
30931  * http://masonry.desandro.com
30932  *
30933  * The idea is to render all the bricks based on vertical width...
30934  *
30935  * The original code extends 'outlayer' - we might need to use that....
30936  * 
30937  */
30938
30939
30940 /**
30941  * @class Roo.bootstrap.LayoutMasonry
30942  * @extends Roo.bootstrap.Component
30943  * Bootstrap Layout Masonry class
30944  * 
30945  * @constructor
30946  * Create a new Element
30947  * @param {Object} config The config object
30948  */
30949
30950 Roo.bootstrap.LayoutMasonry = function(config){
30951     
30952     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30953     
30954     this.bricks = [];
30955     
30956     Roo.bootstrap.LayoutMasonry.register(this);
30957     
30958     this.addEvents({
30959         // raw events
30960         /**
30961          * @event layout
30962          * Fire after layout the items
30963          * @param {Roo.bootstrap.LayoutMasonry} this
30964          * @param {Roo.EventObject} e
30965          */
30966         "layout" : true
30967     });
30968     
30969 };
30970
30971 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30972     
30973     /**
30974      * @cfg {Boolean} isLayoutInstant = no animation?
30975      */   
30976     isLayoutInstant : false, // needed?
30977    
30978     /**
30979      * @cfg {Number} boxWidth  width of the columns
30980      */   
30981     boxWidth : 450,
30982     
30983       /**
30984      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30985      */   
30986     boxHeight : 0,
30987     
30988     /**
30989      * @cfg {Number} padWidth padding below box..
30990      */   
30991     padWidth : 10, 
30992     
30993     /**
30994      * @cfg {Number} gutter gutter width..
30995      */   
30996     gutter : 10,
30997     
30998      /**
30999      * @cfg {Number} maxCols maximum number of columns
31000      */   
31001     
31002     maxCols: 0,
31003     
31004     /**
31005      * @cfg {Boolean} isAutoInitial defalut true
31006      */   
31007     isAutoInitial : true, 
31008     
31009     containerWidth: 0,
31010     
31011     /**
31012      * @cfg {Boolean} isHorizontal defalut false
31013      */   
31014     isHorizontal : false, 
31015
31016     currentSize : null,
31017     
31018     tag: 'div',
31019     
31020     cls: '',
31021     
31022     bricks: null, //CompositeElement
31023     
31024     cols : 1,
31025     
31026     _isLayoutInited : false,
31027     
31028 //    isAlternative : false, // only use for vertical layout...
31029     
31030     /**
31031      * @cfg {Number} alternativePadWidth padding below box..
31032      */   
31033     alternativePadWidth : 50,
31034     
31035     selectedBrick : [],
31036     
31037     getAutoCreate : function(){
31038         
31039         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31040         
31041         var cfg = {
31042             tag: this.tag,
31043             cls: 'blog-masonary-wrapper ' + this.cls,
31044             cn : {
31045                 cls : 'mas-boxes masonary'
31046             }
31047         };
31048         
31049         return cfg;
31050     },
31051     
31052     getChildContainer: function( )
31053     {
31054         if (this.boxesEl) {
31055             return this.boxesEl;
31056         }
31057         
31058         this.boxesEl = this.el.select('.mas-boxes').first();
31059         
31060         return this.boxesEl;
31061     },
31062     
31063     
31064     initEvents : function()
31065     {
31066         var _this = this;
31067         
31068         if(this.isAutoInitial){
31069             Roo.log('hook children rendered');
31070             this.on('childrenrendered', function() {
31071                 Roo.log('children rendered');
31072                 _this.initial();
31073             } ,this);
31074         }
31075     },
31076     
31077     initial : function()
31078     {
31079         this.selectedBrick = [];
31080         
31081         this.currentSize = this.el.getBox(true);
31082         
31083         Roo.EventManager.onWindowResize(this.resize, this); 
31084
31085         if(!this.isAutoInitial){
31086             this.layout();
31087             return;
31088         }
31089         
31090         this.layout();
31091         
31092         return;
31093         //this.layout.defer(500,this);
31094         
31095     },
31096     
31097     resize : function()
31098     {
31099         var cs = this.el.getBox(true);
31100         
31101         if (
31102                 this.currentSize.width == cs.width && 
31103                 this.currentSize.x == cs.x && 
31104                 this.currentSize.height == cs.height && 
31105                 this.currentSize.y == cs.y 
31106         ) {
31107             Roo.log("no change in with or X or Y");
31108             return;
31109         }
31110         
31111         this.currentSize = cs;
31112         
31113         this.layout();
31114         
31115     },
31116     
31117     layout : function()
31118     {   
31119         this._resetLayout();
31120         
31121         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31122         
31123         this.layoutItems( isInstant );
31124       
31125         this._isLayoutInited = true;
31126         
31127         this.fireEvent('layout', this);
31128         
31129     },
31130     
31131     _resetLayout : function()
31132     {
31133         if(this.isHorizontal){
31134             this.horizontalMeasureColumns();
31135             return;
31136         }
31137         
31138         this.verticalMeasureColumns();
31139         
31140     },
31141     
31142     verticalMeasureColumns : function()
31143     {
31144         this.getContainerWidth();
31145         
31146 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31147 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31148 //            return;
31149 //        }
31150         
31151         var boxWidth = this.boxWidth + this.padWidth;
31152         
31153         if(this.containerWidth < this.boxWidth){
31154             boxWidth = this.containerWidth
31155         }
31156         
31157         var containerWidth = this.containerWidth;
31158         
31159         var cols = Math.floor(containerWidth / boxWidth);
31160         
31161         this.cols = Math.max( cols, 1 );
31162         
31163         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31164         
31165         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31166         
31167         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31168         
31169         this.colWidth = boxWidth + avail - this.padWidth;
31170         
31171         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31172         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31173     },
31174     
31175     horizontalMeasureColumns : function()
31176     {
31177         this.getContainerWidth();
31178         
31179         var boxWidth = this.boxWidth;
31180         
31181         if(this.containerWidth < boxWidth){
31182             boxWidth = this.containerWidth;
31183         }
31184         
31185         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31186         
31187         this.el.setHeight(boxWidth);
31188         
31189     },
31190     
31191     getContainerWidth : function()
31192     {
31193         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31194     },
31195     
31196     layoutItems : function( isInstant )
31197     {
31198         Roo.log(this.bricks);
31199         
31200         var items = Roo.apply([], this.bricks);
31201         
31202         if(this.isHorizontal){
31203             this._horizontalLayoutItems( items , isInstant );
31204             return;
31205         }
31206         
31207 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31208 //            this._verticalAlternativeLayoutItems( items , isInstant );
31209 //            return;
31210 //        }
31211         
31212         this._verticalLayoutItems( items , isInstant );
31213         
31214     },
31215     
31216     _verticalLayoutItems : function ( items , isInstant)
31217     {
31218         if ( !items || !items.length ) {
31219             return;
31220         }
31221         
31222         var standard = [
31223             ['xs', 'xs', 'xs', 'tall'],
31224             ['xs', 'xs', 'tall'],
31225             ['xs', 'xs', 'sm'],
31226             ['xs', 'xs', 'xs'],
31227             ['xs', 'tall'],
31228             ['xs', 'sm'],
31229             ['xs', 'xs'],
31230             ['xs'],
31231             
31232             ['sm', 'xs', 'xs'],
31233             ['sm', 'xs'],
31234             ['sm'],
31235             
31236             ['tall', 'xs', 'xs', 'xs'],
31237             ['tall', 'xs', 'xs'],
31238             ['tall', 'xs'],
31239             ['tall']
31240             
31241         ];
31242         
31243         var queue = [];
31244         
31245         var boxes = [];
31246         
31247         var box = [];
31248         
31249         Roo.each(items, function(item, k){
31250             
31251             switch (item.size) {
31252                 // these layouts take up a full box,
31253                 case 'md' :
31254                 case 'md-left' :
31255                 case 'md-right' :
31256                 case 'wide' :
31257                     
31258                     if(box.length){
31259                         boxes.push(box);
31260                         box = [];
31261                     }
31262                     
31263                     boxes.push([item]);
31264                     
31265                     break;
31266                     
31267                 case 'xs' :
31268                 case 'sm' :
31269                 case 'tall' :
31270                     
31271                     box.push(item);
31272                     
31273                     break;
31274                 default :
31275                     break;
31276                     
31277             }
31278             
31279         }, this);
31280         
31281         if(box.length){
31282             boxes.push(box);
31283             box = [];
31284         }
31285         
31286         var filterPattern = function(box, length)
31287         {
31288             if(!box.length){
31289                 return;
31290             }
31291             
31292             var match = false;
31293             
31294             var pattern = box.slice(0, length);
31295             
31296             var format = [];
31297             
31298             Roo.each(pattern, function(i){
31299                 format.push(i.size);
31300             }, this);
31301             
31302             Roo.each(standard, function(s){
31303                 
31304                 if(String(s) != String(format)){
31305                     return;
31306                 }
31307                 
31308                 match = true;
31309                 return false;
31310                 
31311             }, this);
31312             
31313             if(!match && length == 1){
31314                 return;
31315             }
31316             
31317             if(!match){
31318                 filterPattern(box, length - 1);
31319                 return;
31320             }
31321                 
31322             queue.push(pattern);
31323
31324             box = box.slice(length, box.length);
31325
31326             filterPattern(box, 4);
31327
31328             return;
31329             
31330         }
31331         
31332         Roo.each(boxes, function(box, k){
31333             
31334             if(!box.length){
31335                 return;
31336             }
31337             
31338             if(box.length == 1){
31339                 queue.push(box);
31340                 return;
31341             }
31342             
31343             filterPattern(box, 4);
31344             
31345         }, this);
31346         
31347         this._processVerticalLayoutQueue( queue, isInstant );
31348         
31349     },
31350     
31351 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31352 //    {
31353 //        if ( !items || !items.length ) {
31354 //            return;
31355 //        }
31356 //
31357 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31358 //        
31359 //    },
31360     
31361     _horizontalLayoutItems : function ( items , isInstant)
31362     {
31363         if ( !items || !items.length || items.length < 3) {
31364             return;
31365         }
31366         
31367         items.reverse();
31368         
31369         var eItems = items.slice(0, 3);
31370         
31371         items = items.slice(3, items.length);
31372         
31373         var standard = [
31374             ['xs', 'xs', 'xs', 'wide'],
31375             ['xs', 'xs', 'wide'],
31376             ['xs', 'xs', 'sm'],
31377             ['xs', 'xs', 'xs'],
31378             ['xs', 'wide'],
31379             ['xs', 'sm'],
31380             ['xs', 'xs'],
31381             ['xs'],
31382             
31383             ['sm', 'xs', 'xs'],
31384             ['sm', 'xs'],
31385             ['sm'],
31386             
31387             ['wide', 'xs', 'xs', 'xs'],
31388             ['wide', 'xs', 'xs'],
31389             ['wide', 'xs'],
31390             ['wide'],
31391             
31392             ['wide-thin']
31393         ];
31394         
31395         var queue = [];
31396         
31397         var boxes = [];
31398         
31399         var box = [];
31400         
31401         Roo.each(items, function(item, k){
31402             
31403             switch (item.size) {
31404                 case 'md' :
31405                 case 'md-left' :
31406                 case 'md-right' :
31407                 case 'tall' :
31408                     
31409                     if(box.length){
31410                         boxes.push(box);
31411                         box = [];
31412                     }
31413                     
31414                     boxes.push([item]);
31415                     
31416                     break;
31417                     
31418                 case 'xs' :
31419                 case 'sm' :
31420                 case 'wide' :
31421                 case 'wide-thin' :
31422                     
31423                     box.push(item);
31424                     
31425                     break;
31426                 default :
31427                     break;
31428                     
31429             }
31430             
31431         }, this);
31432         
31433         if(box.length){
31434             boxes.push(box);
31435             box = [];
31436         }
31437         
31438         var filterPattern = function(box, length)
31439         {
31440             if(!box.length){
31441                 return;
31442             }
31443             
31444             var match = false;
31445             
31446             var pattern = box.slice(0, length);
31447             
31448             var format = [];
31449             
31450             Roo.each(pattern, function(i){
31451                 format.push(i.size);
31452             }, this);
31453             
31454             Roo.each(standard, function(s){
31455                 
31456                 if(String(s) != String(format)){
31457                     return;
31458                 }
31459                 
31460                 match = true;
31461                 return false;
31462                 
31463             }, this);
31464             
31465             if(!match && length == 1){
31466                 return;
31467             }
31468             
31469             if(!match){
31470                 filterPattern(box, length - 1);
31471                 return;
31472             }
31473                 
31474             queue.push(pattern);
31475
31476             box = box.slice(length, box.length);
31477
31478             filterPattern(box, 4);
31479
31480             return;
31481             
31482         }
31483         
31484         Roo.each(boxes, function(box, k){
31485             
31486             if(!box.length){
31487                 return;
31488             }
31489             
31490             if(box.length == 1){
31491                 queue.push(box);
31492                 return;
31493             }
31494             
31495             filterPattern(box, 4);
31496             
31497         }, this);
31498         
31499         
31500         var prune = [];
31501         
31502         var pos = this.el.getBox(true);
31503         
31504         var minX = pos.x;
31505         
31506         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31507         
31508         var hit_end = false;
31509         
31510         Roo.each(queue, function(box){
31511             
31512             if(hit_end){
31513                 
31514                 Roo.each(box, function(b){
31515                 
31516                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31517                     b.el.hide();
31518
31519                 }, this);
31520
31521                 return;
31522             }
31523             
31524             var mx = 0;
31525             
31526             Roo.each(box, function(b){
31527                 
31528                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31529                 b.el.show();
31530
31531                 mx = Math.max(mx, b.x);
31532                 
31533             }, this);
31534             
31535             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31536             
31537             if(maxX < minX){
31538                 
31539                 Roo.each(box, function(b){
31540                 
31541                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31542                     b.el.hide();
31543                     
31544                 }, this);
31545                 
31546                 hit_end = true;
31547                 
31548                 return;
31549             }
31550             
31551             prune.push(box);
31552             
31553         }, this);
31554         
31555         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31556     },
31557     
31558     /** Sets position of item in DOM
31559     * @param {Element} item
31560     * @param {Number} x - horizontal position
31561     * @param {Number} y - vertical position
31562     * @param {Boolean} isInstant - disables transitions
31563     */
31564     _processVerticalLayoutQueue : function( queue, isInstant )
31565     {
31566         var pos = this.el.getBox(true);
31567         var x = pos.x;
31568         var y = pos.y;
31569         var maxY = [];
31570         
31571         for (var i = 0; i < this.cols; i++){
31572             maxY[i] = pos.y;
31573         }
31574         
31575         Roo.each(queue, function(box, k){
31576             
31577             var col = k % this.cols;
31578             
31579             Roo.each(box, function(b,kk){
31580                 
31581                 b.el.position('absolute');
31582                 
31583                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31584                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31585                 
31586                 if(b.size == 'md-left' || b.size == 'md-right'){
31587                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31588                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31589                 }
31590                 
31591                 b.el.setWidth(width);
31592                 b.el.setHeight(height);
31593                 // iframe?
31594                 b.el.select('iframe',true).setSize(width,height);
31595                 
31596             }, this);
31597             
31598             for (var i = 0; i < this.cols; i++){
31599                 
31600                 if(maxY[i] < maxY[col]){
31601                     col = i;
31602                     continue;
31603                 }
31604                 
31605                 col = Math.min(col, i);
31606                 
31607             }
31608             
31609             x = pos.x + col * (this.colWidth + this.padWidth);
31610             
31611             y = maxY[col];
31612             
31613             var positions = [];
31614             
31615             switch (box.length){
31616                 case 1 :
31617                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31618                     break;
31619                 case 2 :
31620                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31621                     break;
31622                 case 3 :
31623                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31624                     break;
31625                 case 4 :
31626                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31627                     break;
31628                 default :
31629                     break;
31630             }
31631             
31632             Roo.each(box, function(b,kk){
31633                 
31634                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31635                 
31636                 var sz = b.el.getSize();
31637                 
31638                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31639                 
31640             }, this);
31641             
31642         }, this);
31643         
31644         var mY = 0;
31645         
31646         for (var i = 0; i < this.cols; i++){
31647             mY = Math.max(mY, maxY[i]);
31648         }
31649         
31650         this.el.setHeight(mY - pos.y);
31651         
31652     },
31653     
31654 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31655 //    {
31656 //        var pos = this.el.getBox(true);
31657 //        var x = pos.x;
31658 //        var y = pos.y;
31659 //        var maxX = pos.right;
31660 //        
31661 //        var maxHeight = 0;
31662 //        
31663 //        Roo.each(items, function(item, k){
31664 //            
31665 //            var c = k % 2;
31666 //            
31667 //            item.el.position('absolute');
31668 //                
31669 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31670 //
31671 //            item.el.setWidth(width);
31672 //
31673 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31674 //
31675 //            item.el.setHeight(height);
31676 //            
31677 //            if(c == 0){
31678 //                item.el.setXY([x, y], isInstant ? false : true);
31679 //            } else {
31680 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31681 //            }
31682 //            
31683 //            y = y + height + this.alternativePadWidth;
31684 //            
31685 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31686 //            
31687 //        }, this);
31688 //        
31689 //        this.el.setHeight(maxHeight);
31690 //        
31691 //    },
31692     
31693     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31694     {
31695         var pos = this.el.getBox(true);
31696         
31697         var minX = pos.x;
31698         var minY = pos.y;
31699         
31700         var maxX = pos.right;
31701         
31702         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31703         
31704         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31705         
31706         Roo.each(queue, function(box, k){
31707             
31708             Roo.each(box, function(b, kk){
31709                 
31710                 b.el.position('absolute');
31711                 
31712                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31713                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31714                 
31715                 if(b.size == 'md-left' || b.size == 'md-right'){
31716                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31717                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31718                 }
31719                 
31720                 b.el.setWidth(width);
31721                 b.el.setHeight(height);
31722                 
31723             }, this);
31724             
31725             if(!box.length){
31726                 return;
31727             }
31728             
31729             var positions = [];
31730             
31731             switch (box.length){
31732                 case 1 :
31733                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31734                     break;
31735                 case 2 :
31736                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31737                     break;
31738                 case 3 :
31739                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31740                     break;
31741                 case 4 :
31742                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31743                     break;
31744                 default :
31745                     break;
31746             }
31747             
31748             Roo.each(box, function(b,kk){
31749                 
31750                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31751                 
31752                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31753                 
31754             }, this);
31755             
31756         }, this);
31757         
31758     },
31759     
31760     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31761     {
31762         Roo.each(eItems, function(b,k){
31763             
31764             b.size = (k == 0) ? 'sm' : 'xs';
31765             b.x = (k == 0) ? 2 : 1;
31766             b.y = (k == 0) ? 2 : 1;
31767             
31768             b.el.position('absolute');
31769             
31770             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31771                 
31772             b.el.setWidth(width);
31773             
31774             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31775             
31776             b.el.setHeight(height);
31777             
31778         }, this);
31779
31780         var positions = [];
31781         
31782         positions.push({
31783             x : maxX - this.unitWidth * 2 - this.gutter,
31784             y : minY
31785         });
31786         
31787         positions.push({
31788             x : maxX - this.unitWidth,
31789             y : minY + (this.unitWidth + this.gutter) * 2
31790         });
31791         
31792         positions.push({
31793             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31794             y : minY
31795         });
31796         
31797         Roo.each(eItems, function(b,k){
31798             
31799             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31800
31801         }, this);
31802         
31803     },
31804     
31805     getVerticalOneBoxColPositions : function(x, y, box)
31806     {
31807         var pos = [];
31808         
31809         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31810         
31811         if(box[0].size == 'md-left'){
31812             rand = 0;
31813         }
31814         
31815         if(box[0].size == 'md-right'){
31816             rand = 1;
31817         }
31818         
31819         pos.push({
31820             x : x + (this.unitWidth + this.gutter) * rand,
31821             y : y
31822         });
31823         
31824         return pos;
31825     },
31826     
31827     getVerticalTwoBoxColPositions : function(x, y, box)
31828     {
31829         var pos = [];
31830         
31831         if(box[0].size == 'xs'){
31832             
31833             pos.push({
31834                 x : x,
31835                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31836             });
31837
31838             pos.push({
31839                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31840                 y : y
31841             });
31842             
31843             return pos;
31844             
31845         }
31846         
31847         pos.push({
31848             x : x,
31849             y : y
31850         });
31851
31852         pos.push({
31853             x : x + (this.unitWidth + this.gutter) * 2,
31854             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31855         });
31856         
31857         return pos;
31858         
31859     },
31860     
31861     getVerticalThreeBoxColPositions : function(x, y, box)
31862     {
31863         var pos = [];
31864         
31865         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31866             
31867             pos.push({
31868                 x : x,
31869                 y : y
31870             });
31871
31872             pos.push({
31873                 x : x + (this.unitWidth + this.gutter) * 1,
31874                 y : y
31875             });
31876             
31877             pos.push({
31878                 x : x + (this.unitWidth + this.gutter) * 2,
31879                 y : y
31880             });
31881             
31882             return pos;
31883             
31884         }
31885         
31886         if(box[0].size == 'xs' && box[1].size == 'xs'){
31887             
31888             pos.push({
31889                 x : x,
31890                 y : y
31891             });
31892
31893             pos.push({
31894                 x : x,
31895                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31896             });
31897             
31898             pos.push({
31899                 x : x + (this.unitWidth + this.gutter) * 1,
31900                 y : y
31901             });
31902             
31903             return pos;
31904             
31905         }
31906         
31907         pos.push({
31908             x : x,
31909             y : y
31910         });
31911
31912         pos.push({
31913             x : x + (this.unitWidth + this.gutter) * 2,
31914             y : y
31915         });
31916
31917         pos.push({
31918             x : x + (this.unitWidth + this.gutter) * 2,
31919             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31920         });
31921             
31922         return pos;
31923         
31924     },
31925     
31926     getVerticalFourBoxColPositions : function(x, y, box)
31927     {
31928         var pos = [];
31929         
31930         if(box[0].size == 'xs'){
31931             
31932             pos.push({
31933                 x : x,
31934                 y : y
31935             });
31936
31937             pos.push({
31938                 x : x,
31939                 y : y + (this.unitHeight + this.gutter) * 1
31940             });
31941             
31942             pos.push({
31943                 x : x,
31944                 y : y + (this.unitHeight + this.gutter) * 2
31945             });
31946             
31947             pos.push({
31948                 x : x + (this.unitWidth + this.gutter) * 1,
31949                 y : y
31950             });
31951             
31952             return pos;
31953             
31954         }
31955         
31956         pos.push({
31957             x : x,
31958             y : y
31959         });
31960
31961         pos.push({
31962             x : x + (this.unitWidth + this.gutter) * 2,
31963             y : y
31964         });
31965
31966         pos.push({
31967             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31968             y : y + (this.unitHeight + this.gutter) * 1
31969         });
31970
31971         pos.push({
31972             x : x + (this.unitWidth + this.gutter) * 2,
31973             y : y + (this.unitWidth + this.gutter) * 2
31974         });
31975
31976         return pos;
31977         
31978     },
31979     
31980     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31981     {
31982         var pos = [];
31983         
31984         if(box[0].size == 'md-left'){
31985             pos.push({
31986                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31987                 y : minY
31988             });
31989             
31990             return pos;
31991         }
31992         
31993         if(box[0].size == 'md-right'){
31994             pos.push({
31995                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31996                 y : minY + (this.unitWidth + this.gutter) * 1
31997             });
31998             
31999             return pos;
32000         }
32001         
32002         var rand = Math.floor(Math.random() * (4 - box[0].y));
32003         
32004         pos.push({
32005             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32006             y : minY + (this.unitWidth + this.gutter) * rand
32007         });
32008         
32009         return pos;
32010         
32011     },
32012     
32013     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32014     {
32015         var pos = [];
32016         
32017         if(box[0].size == 'xs'){
32018             
32019             pos.push({
32020                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32021                 y : minY
32022             });
32023
32024             pos.push({
32025                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32026                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32027             });
32028             
32029             return pos;
32030             
32031         }
32032         
32033         pos.push({
32034             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32035             y : minY
32036         });
32037
32038         pos.push({
32039             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32040             y : minY + (this.unitWidth + this.gutter) * 2
32041         });
32042         
32043         return pos;
32044         
32045     },
32046     
32047     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32048     {
32049         var pos = [];
32050         
32051         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32052             
32053             pos.push({
32054                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32055                 y : minY
32056             });
32057
32058             pos.push({
32059                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32060                 y : minY + (this.unitWidth + this.gutter) * 1
32061             });
32062             
32063             pos.push({
32064                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32065                 y : minY + (this.unitWidth + this.gutter) * 2
32066             });
32067             
32068             return pos;
32069             
32070         }
32071         
32072         if(box[0].size == 'xs' && box[1].size == 'xs'){
32073             
32074             pos.push({
32075                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32076                 y : minY
32077             });
32078
32079             pos.push({
32080                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32081                 y : minY
32082             });
32083             
32084             pos.push({
32085                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32086                 y : minY + (this.unitWidth + this.gutter) * 1
32087             });
32088             
32089             return pos;
32090             
32091         }
32092         
32093         pos.push({
32094             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32095             y : minY
32096         });
32097
32098         pos.push({
32099             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32100             y : minY + (this.unitWidth + this.gutter) * 2
32101         });
32102
32103         pos.push({
32104             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32105             y : minY + (this.unitWidth + this.gutter) * 2
32106         });
32107             
32108         return pos;
32109         
32110     },
32111     
32112     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32113     {
32114         var pos = [];
32115         
32116         if(box[0].size == 'xs'){
32117             
32118             pos.push({
32119                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32120                 y : minY
32121             });
32122
32123             pos.push({
32124                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32125                 y : minY
32126             });
32127             
32128             pos.push({
32129                 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),
32130                 y : minY
32131             });
32132             
32133             pos.push({
32134                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32135                 y : minY + (this.unitWidth + this.gutter) * 1
32136             });
32137             
32138             return pos;
32139             
32140         }
32141         
32142         pos.push({
32143             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32144             y : minY
32145         });
32146         
32147         pos.push({
32148             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32149             y : minY + (this.unitWidth + this.gutter) * 2
32150         });
32151         
32152         pos.push({
32153             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32154             y : minY + (this.unitWidth + this.gutter) * 2
32155         });
32156         
32157         pos.push({
32158             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),
32159             y : minY + (this.unitWidth + this.gutter) * 2
32160         });
32161
32162         return pos;
32163         
32164     },
32165     
32166     /**
32167     * remove a Masonry Brick
32168     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32169     */
32170     removeBrick : function(brick_id)
32171     {
32172         if (!brick_id) {
32173             return;
32174         }
32175         
32176         for (var i = 0; i<this.bricks.length; i++) {
32177             if (this.bricks[i].id == brick_id) {
32178                 this.bricks.splice(i,1);
32179                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32180                 this.initial();
32181             }
32182         }
32183     },
32184     
32185     /**
32186     * adds a Masonry Brick
32187     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32188     */
32189     addBrick : function(cfg)
32190     {
32191         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32192         //this.register(cn);
32193         cn.parentId = this.id;
32194         cn.render(this.el);
32195         return cn;
32196     },
32197     
32198     /**
32199     * register a Masonry Brick
32200     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32201     */
32202     
32203     register : function(brick)
32204     {
32205         this.bricks.push(brick);
32206         brick.masonryId = this.id;
32207     },
32208     
32209     /**
32210     * clear all the Masonry Brick
32211     */
32212     clearAll : function()
32213     {
32214         this.bricks = [];
32215         //this.getChildContainer().dom.innerHTML = "";
32216         this.el.dom.innerHTML = '';
32217     },
32218     
32219     getSelected : function()
32220     {
32221         if (!this.selectedBrick) {
32222             return false;
32223         }
32224         
32225         return this.selectedBrick;
32226     }
32227 });
32228
32229 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32230     
32231     groups: {},
32232      /**
32233     * register a Masonry Layout
32234     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32235     */
32236     
32237     register : function(layout)
32238     {
32239         this.groups[layout.id] = layout;
32240     },
32241     /**
32242     * fetch a  Masonry Layout based on the masonry layout ID
32243     * @param {string} the masonry layout to add
32244     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32245     */
32246     
32247     get: function(layout_id) {
32248         if (typeof(this.groups[layout_id]) == 'undefined') {
32249             return false;
32250         }
32251         return this.groups[layout_id] ;
32252     }
32253     
32254     
32255     
32256 });
32257
32258  
32259
32260  /**
32261  *
32262  * This is based on 
32263  * http://masonry.desandro.com
32264  *
32265  * The idea is to render all the bricks based on vertical width...
32266  *
32267  * The original code extends 'outlayer' - we might need to use that....
32268  * 
32269  */
32270
32271
32272 /**
32273  * @class Roo.bootstrap.LayoutMasonryAuto
32274  * @extends Roo.bootstrap.Component
32275  * Bootstrap Layout Masonry class
32276  * 
32277  * @constructor
32278  * Create a new Element
32279  * @param {Object} config The config object
32280  */
32281
32282 Roo.bootstrap.LayoutMasonryAuto = function(config){
32283     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32284 };
32285
32286 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32287     
32288       /**
32289      * @cfg {Boolean} isFitWidth  - resize the width..
32290      */   
32291     isFitWidth : false,  // options..
32292     /**
32293      * @cfg {Boolean} isOriginLeft = left align?
32294      */   
32295     isOriginLeft : true,
32296     /**
32297      * @cfg {Boolean} isOriginTop = top align?
32298      */   
32299     isOriginTop : false,
32300     /**
32301      * @cfg {Boolean} isLayoutInstant = no animation?
32302      */   
32303     isLayoutInstant : false, // needed?
32304     /**
32305      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32306      */   
32307     isResizingContainer : true,
32308     /**
32309      * @cfg {Number} columnWidth  width of the columns 
32310      */   
32311     
32312     columnWidth : 0,
32313     
32314     /**
32315      * @cfg {Number} maxCols maximum number of columns
32316      */   
32317     
32318     maxCols: 0,
32319     /**
32320      * @cfg {Number} padHeight padding below box..
32321      */   
32322     
32323     padHeight : 10, 
32324     
32325     /**
32326      * @cfg {Boolean} isAutoInitial defalut true
32327      */   
32328     
32329     isAutoInitial : true, 
32330     
32331     // private?
32332     gutter : 0,
32333     
32334     containerWidth: 0,
32335     initialColumnWidth : 0,
32336     currentSize : null,
32337     
32338     colYs : null, // array.
32339     maxY : 0,
32340     padWidth: 10,
32341     
32342     
32343     tag: 'div',
32344     cls: '',
32345     bricks: null, //CompositeElement
32346     cols : 0, // array?
32347     // element : null, // wrapped now this.el
32348     _isLayoutInited : null, 
32349     
32350     
32351     getAutoCreate : function(){
32352         
32353         var cfg = {
32354             tag: this.tag,
32355             cls: 'blog-masonary-wrapper ' + this.cls,
32356             cn : {
32357                 cls : 'mas-boxes masonary'
32358             }
32359         };
32360         
32361         return cfg;
32362     },
32363     
32364     getChildContainer: function( )
32365     {
32366         if (this.boxesEl) {
32367             return this.boxesEl;
32368         }
32369         
32370         this.boxesEl = this.el.select('.mas-boxes').first();
32371         
32372         return this.boxesEl;
32373     },
32374     
32375     
32376     initEvents : function()
32377     {
32378         var _this = this;
32379         
32380         if(this.isAutoInitial){
32381             Roo.log('hook children rendered');
32382             this.on('childrenrendered', function() {
32383                 Roo.log('children rendered');
32384                 _this.initial();
32385             } ,this);
32386         }
32387         
32388     },
32389     
32390     initial : function()
32391     {
32392         this.reloadItems();
32393
32394         this.currentSize = this.el.getBox(true);
32395
32396         /// was window resize... - let's see if this works..
32397         Roo.EventManager.onWindowResize(this.resize, this); 
32398
32399         if(!this.isAutoInitial){
32400             this.layout();
32401             return;
32402         }
32403         
32404         this.layout.defer(500,this);
32405     },
32406     
32407     reloadItems: function()
32408     {
32409         this.bricks = this.el.select('.masonry-brick', true);
32410         
32411         this.bricks.each(function(b) {
32412             //Roo.log(b.getSize());
32413             if (!b.attr('originalwidth')) {
32414                 b.attr('originalwidth',  b.getSize().width);
32415             }
32416             
32417         });
32418         
32419         Roo.log(this.bricks.elements.length);
32420     },
32421     
32422     resize : function()
32423     {
32424         Roo.log('resize');
32425         var cs = this.el.getBox(true);
32426         
32427         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32428             Roo.log("no change in with or X");
32429             return;
32430         }
32431         this.currentSize = cs;
32432         this.layout();
32433     },
32434     
32435     layout : function()
32436     {
32437          Roo.log('layout');
32438         this._resetLayout();
32439         //this._manageStamps();
32440       
32441         // don't animate first layout
32442         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32443         this.layoutItems( isInstant );
32444       
32445         // flag for initalized
32446         this._isLayoutInited = true;
32447     },
32448     
32449     layoutItems : function( isInstant )
32450     {
32451         //var items = this._getItemsForLayout( this.items );
32452         // original code supports filtering layout items.. we just ignore it..
32453         
32454         this._layoutItems( this.bricks , isInstant );
32455       
32456         this._postLayout();
32457     },
32458     _layoutItems : function ( items , isInstant)
32459     {
32460        //this.fireEvent( 'layout', this, items );
32461     
32462
32463         if ( !items || !items.elements.length ) {
32464           // no items, emit event with empty array
32465             return;
32466         }
32467
32468         var queue = [];
32469         items.each(function(item) {
32470             Roo.log("layout item");
32471             Roo.log(item);
32472             // get x/y object from method
32473             var position = this._getItemLayoutPosition( item );
32474             // enqueue
32475             position.item = item;
32476             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32477             queue.push( position );
32478         }, this);
32479       
32480         this._processLayoutQueue( queue );
32481     },
32482     /** Sets position of item in DOM
32483     * @param {Element} item
32484     * @param {Number} x - horizontal position
32485     * @param {Number} y - vertical position
32486     * @param {Boolean} isInstant - disables transitions
32487     */
32488     _processLayoutQueue : function( queue )
32489     {
32490         for ( var i=0, len = queue.length; i < len; i++ ) {
32491             var obj = queue[i];
32492             obj.item.position('absolute');
32493             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32494         }
32495     },
32496       
32497     
32498     /**
32499     * Any logic you want to do after each layout,
32500     * i.e. size the container
32501     */
32502     _postLayout : function()
32503     {
32504         this.resizeContainer();
32505     },
32506     
32507     resizeContainer : function()
32508     {
32509         if ( !this.isResizingContainer ) {
32510             return;
32511         }
32512         var size = this._getContainerSize();
32513         if ( size ) {
32514             this.el.setSize(size.width,size.height);
32515             this.boxesEl.setSize(size.width,size.height);
32516         }
32517     },
32518     
32519     
32520     
32521     _resetLayout : function()
32522     {
32523         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32524         this.colWidth = this.el.getWidth();
32525         //this.gutter = this.el.getWidth(); 
32526         
32527         this.measureColumns();
32528
32529         // reset column Y
32530         var i = this.cols;
32531         this.colYs = [];
32532         while (i--) {
32533             this.colYs.push( 0 );
32534         }
32535     
32536         this.maxY = 0;
32537     },
32538
32539     measureColumns : function()
32540     {
32541         this.getContainerWidth();
32542       // if columnWidth is 0, default to outerWidth of first item
32543         if ( !this.columnWidth ) {
32544             var firstItem = this.bricks.first();
32545             Roo.log(firstItem);
32546             this.columnWidth  = this.containerWidth;
32547             if (firstItem && firstItem.attr('originalwidth') ) {
32548                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32549             }
32550             // columnWidth fall back to item of first element
32551             Roo.log("set column width?");
32552                         this.initialColumnWidth = this.columnWidth  ;
32553
32554             // if first elem has no width, default to size of container
32555             
32556         }
32557         
32558         
32559         if (this.initialColumnWidth) {
32560             this.columnWidth = this.initialColumnWidth;
32561         }
32562         
32563         
32564             
32565         // column width is fixed at the top - however if container width get's smaller we should
32566         // reduce it...
32567         
32568         // this bit calcs how man columns..
32569             
32570         var columnWidth = this.columnWidth += this.gutter;
32571       
32572         // calculate columns
32573         var containerWidth = this.containerWidth + this.gutter;
32574         
32575         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32576         // fix rounding errors, typically with gutters
32577         var excess = columnWidth - containerWidth % columnWidth;
32578         
32579         
32580         // if overshoot is less than a pixel, round up, otherwise floor it
32581         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32582         cols = Math[ mathMethod ]( cols );
32583         this.cols = Math.max( cols, 1 );
32584         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32585         
32586          // padding positioning..
32587         var totalColWidth = this.cols * this.columnWidth;
32588         var padavail = this.containerWidth - totalColWidth;
32589         // so for 2 columns - we need 3 'pads'
32590         
32591         var padNeeded = (1+this.cols) * this.padWidth;
32592         
32593         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32594         
32595         this.columnWidth += padExtra
32596         //this.padWidth = Math.floor(padavail /  ( this.cols));
32597         
32598         // adjust colum width so that padding is fixed??
32599         
32600         // we have 3 columns ... total = width * 3
32601         // we have X left over... that should be used by 
32602         
32603         //if (this.expandC) {
32604             
32605         //}
32606         
32607         
32608         
32609     },
32610     
32611     getContainerWidth : function()
32612     {
32613        /* // container is parent if fit width
32614         var container = this.isFitWidth ? this.element.parentNode : this.element;
32615         // check that this.size and size are there
32616         // IE8 triggers resize on body size change, so they might not be
32617         
32618         var size = getSize( container );  //FIXME
32619         this.containerWidth = size && size.innerWidth; //FIXME
32620         */
32621          
32622         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32623         
32624     },
32625     
32626     _getItemLayoutPosition : function( item )  // what is item?
32627     {
32628         // we resize the item to our columnWidth..
32629       
32630         item.setWidth(this.columnWidth);
32631         item.autoBoxAdjust  = false;
32632         
32633         var sz = item.getSize();
32634  
32635         // how many columns does this brick span
32636         var remainder = this.containerWidth % this.columnWidth;
32637         
32638         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32639         // round if off by 1 pixel, otherwise use ceil
32640         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32641         colSpan = Math.min( colSpan, this.cols );
32642         
32643         // normally this should be '1' as we dont' currently allow multi width columns..
32644         
32645         var colGroup = this._getColGroup( colSpan );
32646         // get the minimum Y value from the columns
32647         var minimumY = Math.min.apply( Math, colGroup );
32648         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32649         
32650         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32651          
32652         // position the brick
32653         var position = {
32654             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32655             y: this.currentSize.y + minimumY + this.padHeight
32656         };
32657         
32658         Roo.log(position);
32659         // apply setHeight to necessary columns
32660         var setHeight = minimumY + sz.height + this.padHeight;
32661         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32662         
32663         var setSpan = this.cols + 1 - colGroup.length;
32664         for ( var i = 0; i < setSpan; i++ ) {
32665           this.colYs[ shortColIndex + i ] = setHeight ;
32666         }
32667       
32668         return position;
32669     },
32670     
32671     /**
32672      * @param {Number} colSpan - number of columns the element spans
32673      * @returns {Array} colGroup
32674      */
32675     _getColGroup : function( colSpan )
32676     {
32677         if ( colSpan < 2 ) {
32678           // if brick spans only one column, use all the column Ys
32679           return this.colYs;
32680         }
32681       
32682         var colGroup = [];
32683         // how many different places could this brick fit horizontally
32684         var groupCount = this.cols + 1 - colSpan;
32685         // for each group potential horizontal position
32686         for ( var i = 0; i < groupCount; i++ ) {
32687           // make an array of colY values for that one group
32688           var groupColYs = this.colYs.slice( i, i + colSpan );
32689           // and get the max value of the array
32690           colGroup[i] = Math.max.apply( Math, groupColYs );
32691         }
32692         return colGroup;
32693     },
32694     /*
32695     _manageStamp : function( stamp )
32696     {
32697         var stampSize =  stamp.getSize();
32698         var offset = stamp.getBox();
32699         // get the columns that this stamp affects
32700         var firstX = this.isOriginLeft ? offset.x : offset.right;
32701         var lastX = firstX + stampSize.width;
32702         var firstCol = Math.floor( firstX / this.columnWidth );
32703         firstCol = Math.max( 0, firstCol );
32704         
32705         var lastCol = Math.floor( lastX / this.columnWidth );
32706         // lastCol should not go over if multiple of columnWidth #425
32707         lastCol -= lastX % this.columnWidth ? 0 : 1;
32708         lastCol = Math.min( this.cols - 1, lastCol );
32709         
32710         // set colYs to bottom of the stamp
32711         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32712             stampSize.height;
32713             
32714         for ( var i = firstCol; i <= lastCol; i++ ) {
32715           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32716         }
32717     },
32718     */
32719     
32720     _getContainerSize : function()
32721     {
32722         this.maxY = Math.max.apply( Math, this.colYs );
32723         var size = {
32724             height: this.maxY
32725         };
32726       
32727         if ( this.isFitWidth ) {
32728             size.width = this._getContainerFitWidth();
32729         }
32730       
32731         return size;
32732     },
32733     
32734     _getContainerFitWidth : function()
32735     {
32736         var unusedCols = 0;
32737         // count unused columns
32738         var i = this.cols;
32739         while ( --i ) {
32740           if ( this.colYs[i] !== 0 ) {
32741             break;
32742           }
32743           unusedCols++;
32744         }
32745         // fit container to columns that have been used
32746         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32747     },
32748     
32749     needsResizeLayout : function()
32750     {
32751         var previousWidth = this.containerWidth;
32752         this.getContainerWidth();
32753         return previousWidth !== this.containerWidth;
32754     }
32755  
32756 });
32757
32758  
32759
32760  /*
32761  * - LGPL
32762  *
32763  * element
32764  * 
32765  */
32766
32767 /**
32768  * @class Roo.bootstrap.MasonryBrick
32769  * @extends Roo.bootstrap.Component
32770  * Bootstrap MasonryBrick class
32771  * 
32772  * @constructor
32773  * Create a new MasonryBrick
32774  * @param {Object} config The config object
32775  */
32776
32777 Roo.bootstrap.MasonryBrick = function(config){
32778     
32779     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32780     
32781     Roo.bootstrap.MasonryBrick.register(this);
32782     
32783     this.addEvents({
32784         // raw events
32785         /**
32786          * @event click
32787          * When a MasonryBrick is clcik
32788          * @param {Roo.bootstrap.MasonryBrick} this
32789          * @param {Roo.EventObject} e
32790          */
32791         "click" : true
32792     });
32793 };
32794
32795 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32796     
32797     /**
32798      * @cfg {String} title
32799      */   
32800     title : '',
32801     /**
32802      * @cfg {String} html
32803      */   
32804     html : '',
32805     /**
32806      * @cfg {String} bgimage
32807      */   
32808     bgimage : '',
32809     /**
32810      * @cfg {String} videourl
32811      */   
32812     videourl : '',
32813     /**
32814      * @cfg {String} cls
32815      */   
32816     cls : '',
32817     /**
32818      * @cfg {String} href
32819      */   
32820     href : '',
32821     /**
32822      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32823      */   
32824     size : 'xs',
32825     
32826     /**
32827      * @cfg {String} placetitle (center|bottom)
32828      */   
32829     placetitle : '',
32830     
32831     /**
32832      * @cfg {Boolean} isFitContainer defalut true
32833      */   
32834     isFitContainer : true, 
32835     
32836     /**
32837      * @cfg {Boolean} preventDefault defalut false
32838      */   
32839     preventDefault : false, 
32840     
32841     /**
32842      * @cfg {Boolean} inverse defalut false
32843      */   
32844     maskInverse : false, 
32845     
32846     getAutoCreate : function()
32847     {
32848         if(!this.isFitContainer){
32849             return this.getSplitAutoCreate();
32850         }
32851         
32852         var cls = 'masonry-brick masonry-brick-full';
32853         
32854         if(this.href.length){
32855             cls += ' masonry-brick-link';
32856         }
32857         
32858         if(this.bgimage.length){
32859             cls += ' masonry-brick-image';
32860         }
32861         
32862         if(this.maskInverse){
32863             cls += ' mask-inverse';
32864         }
32865         
32866         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32867             cls += ' enable-mask';
32868         }
32869         
32870         if(this.size){
32871             cls += ' masonry-' + this.size + '-brick';
32872         }
32873         
32874         if(this.placetitle.length){
32875             
32876             switch (this.placetitle) {
32877                 case 'center' :
32878                     cls += ' masonry-center-title';
32879                     break;
32880                 case 'bottom' :
32881                     cls += ' masonry-bottom-title';
32882                     break;
32883                 default:
32884                     break;
32885             }
32886             
32887         } else {
32888             if(!this.html.length && !this.bgimage.length){
32889                 cls += ' masonry-center-title';
32890             }
32891
32892             if(!this.html.length && this.bgimage.length){
32893                 cls += ' masonry-bottom-title';
32894             }
32895         }
32896         
32897         if(this.cls){
32898             cls += ' ' + this.cls;
32899         }
32900         
32901         var cfg = {
32902             tag: (this.href.length) ? 'a' : 'div',
32903             cls: cls,
32904             cn: [
32905                 {
32906                     tag: 'div',
32907                     cls: 'masonry-brick-mask'
32908                 },
32909                 {
32910                     tag: 'div',
32911                     cls: 'masonry-brick-paragraph',
32912                     cn: []
32913                 }
32914             ]
32915         };
32916         
32917         if(this.href.length){
32918             cfg.href = this.href;
32919         }
32920         
32921         var cn = cfg.cn[1].cn;
32922         
32923         if(this.title.length){
32924             cn.push({
32925                 tag: 'h4',
32926                 cls: 'masonry-brick-title',
32927                 html: this.title
32928             });
32929         }
32930         
32931         if(this.html.length){
32932             cn.push({
32933                 tag: 'p',
32934                 cls: 'masonry-brick-text',
32935                 html: this.html
32936             });
32937         }
32938         
32939         if (!this.title.length && !this.html.length) {
32940             cfg.cn[1].cls += ' hide';
32941         }
32942         
32943         if(this.bgimage.length){
32944             cfg.cn.push({
32945                 tag: 'img',
32946                 cls: 'masonry-brick-image-view',
32947                 src: this.bgimage
32948             });
32949         }
32950         
32951         if(this.videourl.length){
32952             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32953             // youtube support only?
32954             cfg.cn.push({
32955                 tag: 'iframe',
32956                 cls: 'masonry-brick-image-view',
32957                 src: vurl,
32958                 frameborder : 0,
32959                 allowfullscreen : true
32960             });
32961         }
32962         
32963         return cfg;
32964         
32965     },
32966     
32967     getSplitAutoCreate : function()
32968     {
32969         var cls = 'masonry-brick masonry-brick-split';
32970         
32971         if(this.href.length){
32972             cls += ' masonry-brick-link';
32973         }
32974         
32975         if(this.bgimage.length){
32976             cls += ' masonry-brick-image';
32977         }
32978         
32979         if(this.size){
32980             cls += ' masonry-' + this.size + '-brick';
32981         }
32982         
32983         switch (this.placetitle) {
32984             case 'center' :
32985                 cls += ' masonry-center-title';
32986                 break;
32987             case 'bottom' :
32988                 cls += ' masonry-bottom-title';
32989                 break;
32990             default:
32991                 if(!this.bgimage.length){
32992                     cls += ' masonry-center-title';
32993                 }
32994
32995                 if(this.bgimage.length){
32996                     cls += ' masonry-bottom-title';
32997                 }
32998                 break;
32999         }
33000         
33001         if(this.cls){
33002             cls += ' ' + this.cls;
33003         }
33004         
33005         var cfg = {
33006             tag: (this.href.length) ? 'a' : 'div',
33007             cls: cls,
33008             cn: [
33009                 {
33010                     tag: 'div',
33011                     cls: 'masonry-brick-split-head',
33012                     cn: [
33013                         {
33014                             tag: 'div',
33015                             cls: 'masonry-brick-paragraph',
33016                             cn: []
33017                         }
33018                     ]
33019                 },
33020                 {
33021                     tag: 'div',
33022                     cls: 'masonry-brick-split-body',
33023                     cn: []
33024                 }
33025             ]
33026         };
33027         
33028         if(this.href.length){
33029             cfg.href = this.href;
33030         }
33031         
33032         if(this.title.length){
33033             cfg.cn[0].cn[0].cn.push({
33034                 tag: 'h4',
33035                 cls: 'masonry-brick-title',
33036                 html: this.title
33037             });
33038         }
33039         
33040         if(this.html.length){
33041             cfg.cn[1].cn.push({
33042                 tag: 'p',
33043                 cls: 'masonry-brick-text',
33044                 html: this.html
33045             });
33046         }
33047
33048         if(this.bgimage.length){
33049             cfg.cn[0].cn.push({
33050                 tag: 'img',
33051                 cls: 'masonry-brick-image-view',
33052                 src: this.bgimage
33053             });
33054         }
33055         
33056         if(this.videourl.length){
33057             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33058             // youtube support only?
33059             cfg.cn[0].cn.cn.push({
33060                 tag: 'iframe',
33061                 cls: 'masonry-brick-image-view',
33062                 src: vurl,
33063                 frameborder : 0,
33064                 allowfullscreen : true
33065             });
33066         }
33067         
33068         return cfg;
33069     },
33070     
33071     initEvents: function() 
33072     {
33073         switch (this.size) {
33074             case 'xs' :
33075                 this.x = 1;
33076                 this.y = 1;
33077                 break;
33078             case 'sm' :
33079                 this.x = 2;
33080                 this.y = 2;
33081                 break;
33082             case 'md' :
33083             case 'md-left' :
33084             case 'md-right' :
33085                 this.x = 3;
33086                 this.y = 3;
33087                 break;
33088             case 'tall' :
33089                 this.x = 2;
33090                 this.y = 3;
33091                 break;
33092             case 'wide' :
33093                 this.x = 3;
33094                 this.y = 2;
33095                 break;
33096             case 'wide-thin' :
33097                 this.x = 3;
33098                 this.y = 1;
33099                 break;
33100                         
33101             default :
33102                 break;
33103         }
33104         
33105         if(Roo.isTouch){
33106             this.el.on('touchstart', this.onTouchStart, this);
33107             this.el.on('touchmove', this.onTouchMove, this);
33108             this.el.on('touchend', this.onTouchEnd, this);
33109             this.el.on('contextmenu', this.onContextMenu, this);
33110         } else {
33111             this.el.on('mouseenter'  ,this.enter, this);
33112             this.el.on('mouseleave', this.leave, this);
33113             this.el.on('click', this.onClick, this);
33114         }
33115         
33116         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33117             this.parent().bricks.push(this);   
33118         }
33119         
33120     },
33121     
33122     onClick: function(e, el)
33123     {
33124         var time = this.endTimer - this.startTimer;
33125         // Roo.log(e.preventDefault());
33126         if(Roo.isTouch){
33127             if(time > 1000){
33128                 e.preventDefault();
33129                 return;
33130             }
33131         }
33132         
33133         if(!this.preventDefault){
33134             return;
33135         }
33136         
33137         e.preventDefault();
33138         
33139         if (this.activeClass != '') {
33140             this.selectBrick();
33141         }
33142         
33143         this.fireEvent('click', this, e);
33144     },
33145     
33146     enter: function(e, el)
33147     {
33148         e.preventDefault();
33149         
33150         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33151             return;
33152         }
33153         
33154         if(this.bgimage.length && this.html.length){
33155             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33156         }
33157     },
33158     
33159     leave: function(e, el)
33160     {
33161         e.preventDefault();
33162         
33163         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33164             return;
33165         }
33166         
33167         if(this.bgimage.length && this.html.length){
33168             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33169         }
33170     },
33171     
33172     onTouchStart: function(e, el)
33173     {
33174 //        e.preventDefault();
33175         
33176         this.touchmoved = false;
33177         
33178         if(!this.isFitContainer){
33179             return;
33180         }
33181         
33182         if(!this.bgimage.length || !this.html.length){
33183             return;
33184         }
33185         
33186         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33187         
33188         this.timer = new Date().getTime();
33189         
33190     },
33191     
33192     onTouchMove: function(e, el)
33193     {
33194         this.touchmoved = true;
33195     },
33196     
33197     onContextMenu : function(e,el)
33198     {
33199         e.preventDefault();
33200         e.stopPropagation();
33201         return false;
33202     },
33203     
33204     onTouchEnd: function(e, el)
33205     {
33206 //        e.preventDefault();
33207         
33208         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33209         
33210             this.leave(e,el);
33211             
33212             return;
33213         }
33214         
33215         if(!this.bgimage.length || !this.html.length){
33216             
33217             if(this.href.length){
33218                 window.location.href = this.href;
33219             }
33220             
33221             return;
33222         }
33223         
33224         if(!this.isFitContainer){
33225             return;
33226         }
33227         
33228         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33229         
33230         window.location.href = this.href;
33231     },
33232     
33233     //selection on single brick only
33234     selectBrick : function() {
33235         
33236         if (!this.parentId) {
33237             return;
33238         }
33239         
33240         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33241         var index = m.selectedBrick.indexOf(this.id);
33242         
33243         if ( index > -1) {
33244             m.selectedBrick.splice(index,1);
33245             this.el.removeClass(this.activeClass);
33246             return;
33247         }
33248         
33249         for(var i = 0; i < m.selectedBrick.length; i++) {
33250             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33251             b.el.removeClass(b.activeClass);
33252         }
33253         
33254         m.selectedBrick = [];
33255         
33256         m.selectedBrick.push(this.id);
33257         this.el.addClass(this.activeClass);
33258         return;
33259     },
33260     
33261     isSelected : function(){
33262         return this.el.hasClass(this.activeClass);
33263         
33264     }
33265 });
33266
33267 Roo.apply(Roo.bootstrap.MasonryBrick, {
33268     
33269     //groups: {},
33270     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33271      /**
33272     * register a Masonry Brick
33273     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33274     */
33275     
33276     register : function(brick)
33277     {
33278         //this.groups[brick.id] = brick;
33279         this.groups.add(brick.id, brick);
33280     },
33281     /**
33282     * fetch a  masonry brick based on the masonry brick ID
33283     * @param {string} the masonry brick to add
33284     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33285     */
33286     
33287     get: function(brick_id) 
33288     {
33289         // if (typeof(this.groups[brick_id]) == 'undefined') {
33290         //     return false;
33291         // }
33292         // return this.groups[brick_id] ;
33293         
33294         if(this.groups.key(brick_id)) {
33295             return this.groups.key(brick_id);
33296         }
33297         
33298         return false;
33299     }
33300     
33301     
33302     
33303 });
33304
33305  /*
33306  * - LGPL
33307  *
33308  * element
33309  * 
33310  */
33311
33312 /**
33313  * @class Roo.bootstrap.Brick
33314  * @extends Roo.bootstrap.Component
33315  * Bootstrap Brick class
33316  * 
33317  * @constructor
33318  * Create a new Brick
33319  * @param {Object} config The config object
33320  */
33321
33322 Roo.bootstrap.Brick = function(config){
33323     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33324     
33325     this.addEvents({
33326         // raw events
33327         /**
33328          * @event click
33329          * When a Brick is click
33330          * @param {Roo.bootstrap.Brick} this
33331          * @param {Roo.EventObject} e
33332          */
33333         "click" : true
33334     });
33335 };
33336
33337 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33338     
33339     /**
33340      * @cfg {String} title
33341      */   
33342     title : '',
33343     /**
33344      * @cfg {String} html
33345      */   
33346     html : '',
33347     /**
33348      * @cfg {String} bgimage
33349      */   
33350     bgimage : '',
33351     /**
33352      * @cfg {String} cls
33353      */   
33354     cls : '',
33355     /**
33356      * @cfg {String} href
33357      */   
33358     href : '',
33359     /**
33360      * @cfg {String} video
33361      */   
33362     video : '',
33363     /**
33364      * @cfg {Boolean} square
33365      */   
33366     square : true,
33367     
33368     getAutoCreate : function()
33369     {
33370         var cls = 'roo-brick';
33371         
33372         if(this.href.length){
33373             cls += ' roo-brick-link';
33374         }
33375         
33376         if(this.bgimage.length){
33377             cls += ' roo-brick-image';
33378         }
33379         
33380         if(!this.html.length && !this.bgimage.length){
33381             cls += ' roo-brick-center-title';
33382         }
33383         
33384         if(!this.html.length && this.bgimage.length){
33385             cls += ' roo-brick-bottom-title';
33386         }
33387         
33388         if(this.cls){
33389             cls += ' ' + this.cls;
33390         }
33391         
33392         var cfg = {
33393             tag: (this.href.length) ? 'a' : 'div',
33394             cls: cls,
33395             cn: [
33396                 {
33397                     tag: 'div',
33398                     cls: 'roo-brick-paragraph',
33399                     cn: []
33400                 }
33401             ]
33402         };
33403         
33404         if(this.href.length){
33405             cfg.href = this.href;
33406         }
33407         
33408         var cn = cfg.cn[0].cn;
33409         
33410         if(this.title.length){
33411             cn.push({
33412                 tag: 'h4',
33413                 cls: 'roo-brick-title',
33414                 html: this.title
33415             });
33416         }
33417         
33418         if(this.html.length){
33419             cn.push({
33420                 tag: 'p',
33421                 cls: 'roo-brick-text',
33422                 html: this.html
33423             });
33424         } else {
33425             cn.cls += ' hide';
33426         }
33427         
33428         if(this.bgimage.length){
33429             cfg.cn.push({
33430                 tag: 'img',
33431                 cls: 'roo-brick-image-view',
33432                 src: this.bgimage
33433             });
33434         }
33435         
33436         return cfg;
33437     },
33438     
33439     initEvents: function() 
33440     {
33441         if(this.title.length || this.html.length){
33442             this.el.on('mouseenter'  ,this.enter, this);
33443             this.el.on('mouseleave', this.leave, this);
33444         }
33445         
33446         Roo.EventManager.onWindowResize(this.resize, this); 
33447         
33448         if(this.bgimage.length){
33449             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33450             this.imageEl.on('load', this.onImageLoad, this);
33451             return;
33452         }
33453         
33454         this.resize();
33455     },
33456     
33457     onImageLoad : function()
33458     {
33459         this.resize();
33460     },
33461     
33462     resize : function()
33463     {
33464         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33465         
33466         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33467         
33468         if(this.bgimage.length){
33469             var image = this.el.select('.roo-brick-image-view', true).first();
33470             
33471             image.setWidth(paragraph.getWidth());
33472             
33473             if(this.square){
33474                 image.setHeight(paragraph.getWidth());
33475             }
33476             
33477             this.el.setHeight(image.getHeight());
33478             paragraph.setHeight(image.getHeight());
33479             
33480         }
33481         
33482     },
33483     
33484     enter: function(e, el)
33485     {
33486         e.preventDefault();
33487         
33488         if(this.bgimage.length){
33489             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33490             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33491         }
33492     },
33493     
33494     leave: function(e, el)
33495     {
33496         e.preventDefault();
33497         
33498         if(this.bgimage.length){
33499             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33500             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33501         }
33502     }
33503     
33504 });
33505
33506  
33507
33508  /*
33509  * - LGPL
33510  *
33511  * Number field 
33512  */
33513
33514 /**
33515  * @class Roo.bootstrap.NumberField
33516  * @extends Roo.bootstrap.Input
33517  * Bootstrap NumberField class
33518  * 
33519  * 
33520  * 
33521  * 
33522  * @constructor
33523  * Create a new NumberField
33524  * @param {Object} config The config object
33525  */
33526
33527 Roo.bootstrap.NumberField = function(config){
33528     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33529 };
33530
33531 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33532     
33533     /**
33534      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33535      */
33536     allowDecimals : true,
33537     /**
33538      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33539      */
33540     decimalSeparator : ".",
33541     /**
33542      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33543      */
33544     decimalPrecision : 2,
33545     /**
33546      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33547      */
33548     allowNegative : true,
33549     
33550     /**
33551      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33552      */
33553     allowZero: true,
33554     /**
33555      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33556      */
33557     minValue : Number.NEGATIVE_INFINITY,
33558     /**
33559      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33560      */
33561     maxValue : Number.MAX_VALUE,
33562     /**
33563      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33564      */
33565     minText : "The minimum value for this field is {0}",
33566     /**
33567      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33568      */
33569     maxText : "The maximum value for this field is {0}",
33570     /**
33571      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33572      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33573      */
33574     nanText : "{0} is not a valid number",
33575     /**
33576      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33577      */
33578     thousandsDelimiter : false,
33579     /**
33580      * @cfg {String} valueAlign alignment of value
33581      */
33582     valueAlign : "left",
33583
33584     getAutoCreate : function()
33585     {
33586         var hiddenInput = {
33587             tag: 'input',
33588             type: 'hidden',
33589             id: Roo.id(),
33590             cls: 'hidden-number-input'
33591         };
33592         
33593         if (this.name) {
33594             hiddenInput.name = this.name;
33595         }
33596         
33597         this.name = '';
33598         
33599         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33600         
33601         this.name = hiddenInput.name;
33602         
33603         if(cfg.cn.length > 0) {
33604             cfg.cn.push(hiddenInput);
33605         }
33606         
33607         return cfg;
33608     },
33609
33610     // private
33611     initEvents : function()
33612     {   
33613         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33614         
33615         var allowed = "0123456789";
33616         
33617         if(this.allowDecimals){
33618             allowed += this.decimalSeparator;
33619         }
33620         
33621         if(this.allowNegative){
33622             allowed += "-";
33623         }
33624         
33625         if(this.thousandsDelimiter) {
33626             allowed += ",";
33627         }
33628         
33629         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33630         
33631         var keyPress = function(e){
33632             
33633             var k = e.getKey();
33634             
33635             var c = e.getCharCode();
33636             
33637             if(
33638                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33639                     allowed.indexOf(String.fromCharCode(c)) === -1
33640             ){
33641                 e.stopEvent();
33642                 return;
33643             }
33644             
33645             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33646                 return;
33647             }
33648             
33649             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33650                 e.stopEvent();
33651             }
33652         };
33653         
33654         this.el.on("keypress", keyPress, this);
33655     },
33656     
33657     validateValue : function(value)
33658     {
33659         
33660         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33661             return false;
33662         }
33663         
33664         var num = this.parseValue(value);
33665         
33666         if(isNaN(num)){
33667             this.markInvalid(String.format(this.nanText, value));
33668             return false;
33669         }
33670         
33671         if(num < this.minValue){
33672             this.markInvalid(String.format(this.minText, this.minValue));
33673             return false;
33674         }
33675         
33676         if(num > this.maxValue){
33677             this.markInvalid(String.format(this.maxText, this.maxValue));
33678             return false;
33679         }
33680         
33681         return true;
33682     },
33683
33684     getValue : function()
33685     {
33686         var v = this.hiddenEl().getValue();
33687         
33688         return this.fixPrecision(this.parseValue(v));
33689     },
33690
33691     parseValue : function(value)
33692     {
33693         if(this.thousandsDelimiter) {
33694             value += "";
33695             r = new RegExp(",", "g");
33696             value = value.replace(r, "");
33697         }
33698         
33699         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33700         return isNaN(value) ? '' : value;
33701     },
33702
33703     fixPrecision : function(value)
33704     {
33705         if(this.thousandsDelimiter) {
33706             value += "";
33707             r = new RegExp(",", "g");
33708             value = value.replace(r, "");
33709         }
33710         
33711         var nan = isNaN(value);
33712         
33713         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33714             return nan ? '' : value;
33715         }
33716         return parseFloat(value).toFixed(this.decimalPrecision);
33717     },
33718
33719     setValue : function(v)
33720     {
33721         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33722         
33723         this.value = v;
33724         
33725         if(this.rendered){
33726             
33727             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33728             
33729             this.inputEl().dom.value = (v == '') ? '' :
33730                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33731             
33732             if(!this.allowZero && v === '0') {
33733                 this.hiddenEl().dom.value = '';
33734                 this.inputEl().dom.value = '';
33735             }
33736             
33737             this.validate();
33738         }
33739     },
33740
33741     decimalPrecisionFcn : function(v)
33742     {
33743         return Math.floor(v);
33744     },
33745
33746     beforeBlur : function()
33747     {
33748         var v = this.parseValue(this.getRawValue());
33749         
33750         if(v || v === 0 || v === ''){
33751             this.setValue(v);
33752         }
33753     },
33754     
33755     hiddenEl : function()
33756     {
33757         return this.el.select('input.hidden-number-input',true).first();
33758     }
33759     
33760 });
33761
33762  
33763
33764 /*
33765 * Licence: LGPL
33766 */
33767
33768 /**
33769  * @class Roo.bootstrap.DocumentSlider
33770  * @extends Roo.bootstrap.Component
33771  * Bootstrap DocumentSlider class
33772  * 
33773  * @constructor
33774  * Create a new DocumentViewer
33775  * @param {Object} config The config object
33776  */
33777
33778 Roo.bootstrap.DocumentSlider = function(config){
33779     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33780     
33781     this.files = [];
33782     
33783     this.addEvents({
33784         /**
33785          * @event initial
33786          * Fire after initEvent
33787          * @param {Roo.bootstrap.DocumentSlider} this
33788          */
33789         "initial" : true,
33790         /**
33791          * @event update
33792          * Fire after update
33793          * @param {Roo.bootstrap.DocumentSlider} this
33794          */
33795         "update" : true,
33796         /**
33797          * @event click
33798          * Fire after click
33799          * @param {Roo.bootstrap.DocumentSlider} this
33800          */
33801         "click" : true
33802     });
33803 };
33804
33805 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33806     
33807     files : false,
33808     
33809     indicator : 0,
33810     
33811     getAutoCreate : function()
33812     {
33813         var cfg = {
33814             tag : 'div',
33815             cls : 'roo-document-slider',
33816             cn : [
33817                 {
33818                     tag : 'div',
33819                     cls : 'roo-document-slider-header',
33820                     cn : [
33821                         {
33822                             tag : 'div',
33823                             cls : 'roo-document-slider-header-title'
33824                         }
33825                     ]
33826                 },
33827                 {
33828                     tag : 'div',
33829                     cls : 'roo-document-slider-body',
33830                     cn : [
33831                         {
33832                             tag : 'div',
33833                             cls : 'roo-document-slider-prev',
33834                             cn : [
33835                                 {
33836                                     tag : 'i',
33837                                     cls : 'fa fa-chevron-left'
33838                                 }
33839                             ]
33840                         },
33841                         {
33842                             tag : 'div',
33843                             cls : 'roo-document-slider-thumb',
33844                             cn : [
33845                                 {
33846                                     tag : 'img',
33847                                     cls : 'roo-document-slider-image'
33848                                 }
33849                             ]
33850                         },
33851                         {
33852                             tag : 'div',
33853                             cls : 'roo-document-slider-next',
33854                             cn : [
33855                                 {
33856                                     tag : 'i',
33857                                     cls : 'fa fa-chevron-right'
33858                                 }
33859                             ]
33860                         }
33861                     ]
33862                 }
33863             ]
33864         };
33865         
33866         return cfg;
33867     },
33868     
33869     initEvents : function()
33870     {
33871         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33872         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33873         
33874         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33875         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33876         
33877         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33878         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33879         
33880         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33881         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33882         
33883         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33884         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33885         
33886         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33887         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33888         
33889         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33890         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33891         
33892         this.thumbEl.on('click', this.onClick, this);
33893         
33894         this.prevIndicator.on('click', this.prev, this);
33895         
33896         this.nextIndicator.on('click', this.next, this);
33897         
33898     },
33899     
33900     initial : function()
33901     {
33902         if(this.files.length){
33903             this.indicator = 1;
33904             this.update()
33905         }
33906         
33907         this.fireEvent('initial', this);
33908     },
33909     
33910     update : function()
33911     {
33912         this.imageEl.attr('src', this.files[this.indicator - 1]);
33913         
33914         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33915         
33916         this.prevIndicator.show();
33917         
33918         if(this.indicator == 1){
33919             this.prevIndicator.hide();
33920         }
33921         
33922         this.nextIndicator.show();
33923         
33924         if(this.indicator == this.files.length){
33925             this.nextIndicator.hide();
33926         }
33927         
33928         this.thumbEl.scrollTo('top');
33929         
33930         this.fireEvent('update', this);
33931     },
33932     
33933     onClick : function(e)
33934     {
33935         e.preventDefault();
33936         
33937         this.fireEvent('click', this);
33938     },
33939     
33940     prev : function(e)
33941     {
33942         e.preventDefault();
33943         
33944         this.indicator = Math.max(1, this.indicator - 1);
33945         
33946         this.update();
33947     },
33948     
33949     next : function(e)
33950     {
33951         e.preventDefault();
33952         
33953         this.indicator = Math.min(this.files.length, this.indicator + 1);
33954         
33955         this.update();
33956     }
33957 });
33958 /*
33959  * - LGPL
33960  *
33961  * RadioSet
33962  *
33963  *
33964  */
33965
33966 /**
33967  * @class Roo.bootstrap.RadioSet
33968  * @extends Roo.bootstrap.Input
33969  * Bootstrap RadioSet class
33970  * @cfg {String} indicatorpos (left|right) default left
33971  * @cfg {Boolean} inline (true|false) inline the element (default true)
33972  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33973  * @constructor
33974  * Create a new RadioSet
33975  * @param {Object} config The config object
33976  */
33977
33978 Roo.bootstrap.RadioSet = function(config){
33979     
33980     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33981     
33982     this.radioes = [];
33983     
33984     Roo.bootstrap.RadioSet.register(this);
33985     
33986     this.addEvents({
33987         /**
33988         * @event check
33989         * Fires when the element is checked or unchecked.
33990         * @param {Roo.bootstrap.RadioSet} this This radio
33991         * @param {Roo.bootstrap.Radio} item The checked item
33992         */
33993        check : true,
33994        /**
33995         * @event click
33996         * Fires when the element is click.
33997         * @param {Roo.bootstrap.RadioSet} this This radio set
33998         * @param {Roo.bootstrap.Radio} item The checked item
33999         * @param {Roo.EventObject} e The event object
34000         */
34001        click : true
34002     });
34003     
34004 };
34005
34006 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34007
34008     radioes : false,
34009     
34010     inline : true,
34011     
34012     weight : '',
34013     
34014     indicatorpos : 'left',
34015     
34016     getAutoCreate : function()
34017     {
34018         var label = {
34019             tag : 'label',
34020             cls : 'roo-radio-set-label',
34021             cn : [
34022                 {
34023                     tag : 'span',
34024                     html : this.fieldLabel
34025                 }
34026             ]
34027         };
34028         
34029         if(this.indicatorpos == 'left'){
34030             label.cn.unshift({
34031                 tag : 'i',
34032                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34033                 tooltip : 'This field is required'
34034             });
34035         } else {
34036             label.cn.push({
34037                 tag : 'i',
34038                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34039                 tooltip : 'This field is required'
34040             });
34041         }
34042         
34043         var items = {
34044             tag : 'div',
34045             cls : 'roo-radio-set-items'
34046         };
34047         
34048         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34049         
34050         if (align === 'left' && this.fieldLabel.length) {
34051             
34052             items = {
34053                 cls : "roo-radio-set-right", 
34054                 cn: [
34055                     items
34056                 ]
34057             };
34058             
34059             if(this.labelWidth > 12){
34060                 label.style = "width: " + this.labelWidth + 'px';
34061             }
34062             
34063             if(this.labelWidth < 13 && this.labelmd == 0){
34064                 this.labelmd = this.labelWidth;
34065             }
34066             
34067             if(this.labellg > 0){
34068                 label.cls += ' col-lg-' + this.labellg;
34069                 items.cls += ' col-lg-' + (12 - this.labellg);
34070             }
34071             
34072             if(this.labelmd > 0){
34073                 label.cls += ' col-md-' + this.labelmd;
34074                 items.cls += ' col-md-' + (12 - this.labelmd);
34075             }
34076             
34077             if(this.labelsm > 0){
34078                 label.cls += ' col-sm-' + this.labelsm;
34079                 items.cls += ' col-sm-' + (12 - this.labelsm);
34080             }
34081             
34082             if(this.labelxs > 0){
34083                 label.cls += ' col-xs-' + this.labelxs;
34084                 items.cls += ' col-xs-' + (12 - this.labelxs);
34085             }
34086         }
34087         
34088         var cfg = {
34089             tag : 'div',
34090             cls : 'roo-radio-set',
34091             cn : [
34092                 {
34093                     tag : 'input',
34094                     cls : 'roo-radio-set-input',
34095                     type : 'hidden',
34096                     name : this.name,
34097                     value : this.value ? this.value :  ''
34098                 },
34099                 label,
34100                 items
34101             ]
34102         };
34103         
34104         if(this.weight.length){
34105             cfg.cls += ' roo-radio-' + this.weight;
34106         }
34107         
34108         if(this.inline) {
34109             cfg.cls += ' roo-radio-set-inline';
34110         }
34111         
34112         var settings=this;
34113         ['xs','sm','md','lg'].map(function(size){
34114             if (settings[size]) {
34115                 cfg.cls += ' col-' + size + '-' + settings[size];
34116             }
34117         });
34118         
34119         return cfg;
34120         
34121     },
34122
34123     initEvents : function()
34124     {
34125         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34126         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34127         
34128         if(!this.fieldLabel.length){
34129             this.labelEl.hide();
34130         }
34131         
34132         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34133         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34134         
34135         this.indicator = this.indicatorEl();
34136         
34137         if(this.indicator){
34138             this.indicator.addClass('invisible');
34139         }
34140         
34141         this.originalValue = this.getValue();
34142         
34143     },
34144     
34145     inputEl: function ()
34146     {
34147         return this.el.select('.roo-radio-set-input', true).first();
34148     },
34149     
34150     getChildContainer : function()
34151     {
34152         return this.itemsEl;
34153     },
34154     
34155     register : function(item)
34156     {
34157         this.radioes.push(item);
34158         
34159     },
34160     
34161     validate : function()
34162     {   
34163         if(this.getVisibilityEl().hasClass('hidden')){
34164             return true;
34165         }
34166         
34167         var valid = false;
34168         
34169         Roo.each(this.radioes, function(i){
34170             if(!i.checked){
34171                 return;
34172             }
34173             
34174             valid = true;
34175             return false;
34176         });
34177         
34178         if(this.allowBlank) {
34179             return true;
34180         }
34181         
34182         if(this.disabled || valid){
34183             this.markValid();
34184             return true;
34185         }
34186         
34187         this.markInvalid();
34188         return false;
34189         
34190     },
34191     
34192     markValid : function()
34193     {
34194         if(this.labelEl.isVisible(true)){
34195             this.indicatorEl().removeClass('visible');
34196             this.indicatorEl().addClass('invisible');
34197         }
34198         
34199         this.el.removeClass([this.invalidClass, this.validClass]);
34200         this.el.addClass(this.validClass);
34201         
34202         this.fireEvent('valid', this);
34203     },
34204     
34205     markInvalid : function(msg)
34206     {
34207         if(this.allowBlank || this.disabled){
34208             return;
34209         }
34210         
34211         if(this.labelEl.isVisible(true)){
34212             this.indicatorEl().removeClass('invisible');
34213             this.indicatorEl().addClass('visible');
34214         }
34215         
34216         this.el.removeClass([this.invalidClass, this.validClass]);
34217         this.el.addClass(this.invalidClass);
34218         
34219         this.fireEvent('invalid', this, msg);
34220         
34221     },
34222     
34223     setValue : function(v, suppressEvent)
34224     {   
34225         if(this.value === v){
34226             return;
34227         }
34228         
34229         this.value = v;
34230         
34231         if(this.rendered){
34232             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34233         }
34234         
34235         Roo.each(this.radioes, function(i){
34236             i.checked = false;
34237             i.el.removeClass('checked');
34238         });
34239         
34240         Roo.each(this.radioes, function(i){
34241             
34242             if(i.value === v || i.value.toString() === v.toString()){
34243                 i.checked = true;
34244                 i.el.addClass('checked');
34245                 
34246                 if(suppressEvent !== true){
34247                     this.fireEvent('check', this, i);
34248                 }
34249                 
34250                 return false;
34251             }
34252             
34253         }, this);
34254         
34255         this.validate();
34256     },
34257     
34258     clearInvalid : function(){
34259         
34260         if(!this.el || this.preventMark){
34261             return;
34262         }
34263         
34264         this.el.removeClass([this.invalidClass]);
34265         
34266         this.fireEvent('valid', this);
34267     }
34268     
34269 });
34270
34271 Roo.apply(Roo.bootstrap.RadioSet, {
34272     
34273     groups: {},
34274     
34275     register : function(set)
34276     {
34277         this.groups[set.name] = set;
34278     },
34279     
34280     get: function(name) 
34281     {
34282         if (typeof(this.groups[name]) == 'undefined') {
34283             return false;
34284         }
34285         
34286         return this.groups[name] ;
34287     }
34288     
34289 });
34290 /*
34291  * Based on:
34292  * Ext JS Library 1.1.1
34293  * Copyright(c) 2006-2007, Ext JS, LLC.
34294  *
34295  * Originally Released Under LGPL - original licence link has changed is not relivant.
34296  *
34297  * Fork - LGPL
34298  * <script type="text/javascript">
34299  */
34300
34301
34302 /**
34303  * @class Roo.bootstrap.SplitBar
34304  * @extends Roo.util.Observable
34305  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34306  * <br><br>
34307  * Usage:
34308  * <pre><code>
34309 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34310                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34311 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34312 split.minSize = 100;
34313 split.maxSize = 600;
34314 split.animate = true;
34315 split.on('moved', splitterMoved);
34316 </code></pre>
34317  * @constructor
34318  * Create a new SplitBar
34319  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34320  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34321  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34322  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34323                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34324                         position of the SplitBar).
34325  */
34326 Roo.bootstrap.SplitBar = function(cfg){
34327     
34328     /** @private */
34329     
34330     //{
34331     //  dragElement : elm
34332     //  resizingElement: el,
34333         // optional..
34334     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34335     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34336         // existingProxy ???
34337     //}
34338     
34339     this.el = Roo.get(cfg.dragElement, true);
34340     this.el.dom.unselectable = "on";
34341     /** @private */
34342     this.resizingEl = Roo.get(cfg.resizingElement, true);
34343
34344     /**
34345      * @private
34346      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34347      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34348      * @type Number
34349      */
34350     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34351     
34352     /**
34353      * The minimum size of the resizing element. (Defaults to 0)
34354      * @type Number
34355      */
34356     this.minSize = 0;
34357     
34358     /**
34359      * The maximum size of the resizing element. (Defaults to 2000)
34360      * @type Number
34361      */
34362     this.maxSize = 2000;
34363     
34364     /**
34365      * Whether to animate the transition to the new size
34366      * @type Boolean
34367      */
34368     this.animate = false;
34369     
34370     /**
34371      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34372      * @type Boolean
34373      */
34374     this.useShim = false;
34375     
34376     /** @private */
34377     this.shim = null;
34378     
34379     if(!cfg.existingProxy){
34380         /** @private */
34381         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34382     }else{
34383         this.proxy = Roo.get(cfg.existingProxy).dom;
34384     }
34385     /** @private */
34386     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34387     
34388     /** @private */
34389     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34390     
34391     /** @private */
34392     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34393     
34394     /** @private */
34395     this.dragSpecs = {};
34396     
34397     /**
34398      * @private The adapter to use to positon and resize elements
34399      */
34400     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34401     this.adapter.init(this);
34402     
34403     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34404         /** @private */
34405         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34406         this.el.addClass("roo-splitbar-h");
34407     }else{
34408         /** @private */
34409         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34410         this.el.addClass("roo-splitbar-v");
34411     }
34412     
34413     this.addEvents({
34414         /**
34415          * @event resize
34416          * Fires when the splitter is moved (alias for {@link #event-moved})
34417          * @param {Roo.bootstrap.SplitBar} this
34418          * @param {Number} newSize the new width or height
34419          */
34420         "resize" : true,
34421         /**
34422          * @event moved
34423          * Fires when the splitter is moved
34424          * @param {Roo.bootstrap.SplitBar} this
34425          * @param {Number} newSize the new width or height
34426          */
34427         "moved" : true,
34428         /**
34429          * @event beforeresize
34430          * Fires before the splitter is dragged
34431          * @param {Roo.bootstrap.SplitBar} this
34432          */
34433         "beforeresize" : true,
34434
34435         "beforeapply" : true
34436     });
34437
34438     Roo.util.Observable.call(this);
34439 };
34440
34441 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34442     onStartProxyDrag : function(x, y){
34443         this.fireEvent("beforeresize", this);
34444         if(!this.overlay){
34445             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34446             o.unselectable();
34447             o.enableDisplayMode("block");
34448             // all splitbars share the same overlay
34449             Roo.bootstrap.SplitBar.prototype.overlay = o;
34450         }
34451         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34452         this.overlay.show();
34453         Roo.get(this.proxy).setDisplayed("block");
34454         var size = this.adapter.getElementSize(this);
34455         this.activeMinSize = this.getMinimumSize();;
34456         this.activeMaxSize = this.getMaximumSize();;
34457         var c1 = size - this.activeMinSize;
34458         var c2 = Math.max(this.activeMaxSize - size, 0);
34459         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34460             this.dd.resetConstraints();
34461             this.dd.setXConstraint(
34462                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34463                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34464             );
34465             this.dd.setYConstraint(0, 0);
34466         }else{
34467             this.dd.resetConstraints();
34468             this.dd.setXConstraint(0, 0);
34469             this.dd.setYConstraint(
34470                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34471                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34472             );
34473          }
34474         this.dragSpecs.startSize = size;
34475         this.dragSpecs.startPoint = [x, y];
34476         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34477     },
34478     
34479     /** 
34480      * @private Called after the drag operation by the DDProxy
34481      */
34482     onEndProxyDrag : function(e){
34483         Roo.get(this.proxy).setDisplayed(false);
34484         var endPoint = Roo.lib.Event.getXY(e);
34485         if(this.overlay){
34486             this.overlay.hide();
34487         }
34488         var newSize;
34489         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34490             newSize = this.dragSpecs.startSize + 
34491                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34492                     endPoint[0] - this.dragSpecs.startPoint[0] :
34493                     this.dragSpecs.startPoint[0] - endPoint[0]
34494                 );
34495         }else{
34496             newSize = this.dragSpecs.startSize + 
34497                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34498                     endPoint[1] - this.dragSpecs.startPoint[1] :
34499                     this.dragSpecs.startPoint[1] - endPoint[1]
34500                 );
34501         }
34502         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34503         if(newSize != this.dragSpecs.startSize){
34504             if(this.fireEvent('beforeapply', this, newSize) !== false){
34505                 this.adapter.setElementSize(this, newSize);
34506                 this.fireEvent("moved", this, newSize);
34507                 this.fireEvent("resize", this, newSize);
34508             }
34509         }
34510     },
34511     
34512     /**
34513      * Get the adapter this SplitBar uses
34514      * @return The adapter object
34515      */
34516     getAdapter : function(){
34517         return this.adapter;
34518     },
34519     
34520     /**
34521      * Set the adapter this SplitBar uses
34522      * @param {Object} adapter A SplitBar adapter object
34523      */
34524     setAdapter : function(adapter){
34525         this.adapter = adapter;
34526         this.adapter.init(this);
34527     },
34528     
34529     /**
34530      * Gets the minimum size for the resizing element
34531      * @return {Number} The minimum size
34532      */
34533     getMinimumSize : function(){
34534         return this.minSize;
34535     },
34536     
34537     /**
34538      * Sets the minimum size for the resizing element
34539      * @param {Number} minSize The minimum size
34540      */
34541     setMinimumSize : function(minSize){
34542         this.minSize = minSize;
34543     },
34544     
34545     /**
34546      * Gets the maximum size for the resizing element
34547      * @return {Number} The maximum size
34548      */
34549     getMaximumSize : function(){
34550         return this.maxSize;
34551     },
34552     
34553     /**
34554      * Sets the maximum size for the resizing element
34555      * @param {Number} maxSize The maximum size
34556      */
34557     setMaximumSize : function(maxSize){
34558         this.maxSize = maxSize;
34559     },
34560     
34561     /**
34562      * Sets the initialize size for the resizing element
34563      * @param {Number} size The initial size
34564      */
34565     setCurrentSize : function(size){
34566         var oldAnimate = this.animate;
34567         this.animate = false;
34568         this.adapter.setElementSize(this, size);
34569         this.animate = oldAnimate;
34570     },
34571     
34572     /**
34573      * Destroy this splitbar. 
34574      * @param {Boolean} removeEl True to remove the element
34575      */
34576     destroy : function(removeEl){
34577         if(this.shim){
34578             this.shim.remove();
34579         }
34580         this.dd.unreg();
34581         this.proxy.parentNode.removeChild(this.proxy);
34582         if(removeEl){
34583             this.el.remove();
34584         }
34585     }
34586 });
34587
34588 /**
34589  * @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.
34590  */
34591 Roo.bootstrap.SplitBar.createProxy = function(dir){
34592     var proxy = new Roo.Element(document.createElement("div"));
34593     proxy.unselectable();
34594     var cls = 'roo-splitbar-proxy';
34595     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34596     document.body.appendChild(proxy.dom);
34597     return proxy.dom;
34598 };
34599
34600 /** 
34601  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34602  * Default Adapter. It assumes the splitter and resizing element are not positioned
34603  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34604  */
34605 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34606 };
34607
34608 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34609     // do nothing for now
34610     init : function(s){
34611     
34612     },
34613     /**
34614      * Called before drag operations to get the current size of the resizing element. 
34615      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34616      */
34617      getElementSize : function(s){
34618         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34619             return s.resizingEl.getWidth();
34620         }else{
34621             return s.resizingEl.getHeight();
34622         }
34623     },
34624     
34625     /**
34626      * Called after drag operations to set the size of the resizing element.
34627      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34628      * @param {Number} newSize The new size to set
34629      * @param {Function} onComplete A function to be invoked when resizing is complete
34630      */
34631     setElementSize : function(s, newSize, onComplete){
34632         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34633             if(!s.animate){
34634                 s.resizingEl.setWidth(newSize);
34635                 if(onComplete){
34636                     onComplete(s, newSize);
34637                 }
34638             }else{
34639                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34640             }
34641         }else{
34642             
34643             if(!s.animate){
34644                 s.resizingEl.setHeight(newSize);
34645                 if(onComplete){
34646                     onComplete(s, newSize);
34647                 }
34648             }else{
34649                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34650             }
34651         }
34652     }
34653 };
34654
34655 /** 
34656  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34657  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34658  * Adapter that  moves the splitter element to align with the resized sizing element. 
34659  * Used with an absolute positioned SplitBar.
34660  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34661  * document.body, make sure you assign an id to the body element.
34662  */
34663 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34664     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34665     this.container = Roo.get(container);
34666 };
34667
34668 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34669     init : function(s){
34670         this.basic.init(s);
34671     },
34672     
34673     getElementSize : function(s){
34674         return this.basic.getElementSize(s);
34675     },
34676     
34677     setElementSize : function(s, newSize, onComplete){
34678         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34679     },
34680     
34681     moveSplitter : function(s){
34682         var yes = Roo.bootstrap.SplitBar;
34683         switch(s.placement){
34684             case yes.LEFT:
34685                 s.el.setX(s.resizingEl.getRight());
34686                 break;
34687             case yes.RIGHT:
34688                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34689                 break;
34690             case yes.TOP:
34691                 s.el.setY(s.resizingEl.getBottom());
34692                 break;
34693             case yes.BOTTOM:
34694                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34695                 break;
34696         }
34697     }
34698 };
34699
34700 /**
34701  * Orientation constant - Create a vertical SplitBar
34702  * @static
34703  * @type Number
34704  */
34705 Roo.bootstrap.SplitBar.VERTICAL = 1;
34706
34707 /**
34708  * Orientation constant - Create a horizontal SplitBar
34709  * @static
34710  * @type Number
34711  */
34712 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34713
34714 /**
34715  * Placement constant - The resizing element is to the left of the splitter element
34716  * @static
34717  * @type Number
34718  */
34719 Roo.bootstrap.SplitBar.LEFT = 1;
34720
34721 /**
34722  * Placement constant - The resizing element is to the right of the splitter element
34723  * @static
34724  * @type Number
34725  */
34726 Roo.bootstrap.SplitBar.RIGHT = 2;
34727
34728 /**
34729  * Placement constant - The resizing element is positioned above the splitter element
34730  * @static
34731  * @type Number
34732  */
34733 Roo.bootstrap.SplitBar.TOP = 3;
34734
34735 /**
34736  * Placement constant - The resizing element is positioned under splitter element
34737  * @static
34738  * @type Number
34739  */
34740 Roo.bootstrap.SplitBar.BOTTOM = 4;
34741 Roo.namespace("Roo.bootstrap.layout");/*
34742  * Based on:
34743  * Ext JS Library 1.1.1
34744  * Copyright(c) 2006-2007, Ext JS, LLC.
34745  *
34746  * Originally Released Under LGPL - original licence link has changed is not relivant.
34747  *
34748  * Fork - LGPL
34749  * <script type="text/javascript">
34750  */
34751
34752 /**
34753  * @class Roo.bootstrap.layout.Manager
34754  * @extends Roo.bootstrap.Component
34755  * Base class for layout managers.
34756  */
34757 Roo.bootstrap.layout.Manager = function(config)
34758 {
34759     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34760
34761
34762
34763
34764
34765     /** false to disable window resize monitoring @type Boolean */
34766     this.monitorWindowResize = true;
34767     this.regions = {};
34768     this.addEvents({
34769         /**
34770          * @event layout
34771          * Fires when a layout is performed.
34772          * @param {Roo.LayoutManager} this
34773          */
34774         "layout" : true,
34775         /**
34776          * @event regionresized
34777          * Fires when the user resizes a region.
34778          * @param {Roo.LayoutRegion} region The resized region
34779          * @param {Number} newSize The new size (width for east/west, height for north/south)
34780          */
34781         "regionresized" : true,
34782         /**
34783          * @event regioncollapsed
34784          * Fires when a region is collapsed.
34785          * @param {Roo.LayoutRegion} region The collapsed region
34786          */
34787         "regioncollapsed" : true,
34788         /**
34789          * @event regionexpanded
34790          * Fires when a region is expanded.
34791          * @param {Roo.LayoutRegion} region The expanded region
34792          */
34793         "regionexpanded" : true
34794     });
34795     this.updating = false;
34796
34797     if (config.el) {
34798         this.el = Roo.get(config.el);
34799         this.initEvents();
34800     }
34801
34802 };
34803
34804 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34805
34806
34807     regions : null,
34808
34809     monitorWindowResize : true,
34810
34811
34812     updating : false,
34813
34814
34815     onRender : function(ct, position)
34816     {
34817         if(!this.el){
34818             this.el = Roo.get(ct);
34819             this.initEvents();
34820         }
34821         //this.fireEvent('render',this);
34822     },
34823
34824
34825     initEvents: function()
34826     {
34827
34828
34829         // ie scrollbar fix
34830         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34831             document.body.scroll = "no";
34832         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34833             this.el.position('relative');
34834         }
34835         this.id = this.el.id;
34836         this.el.addClass("roo-layout-container");
34837         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34838         if(this.el.dom != document.body ) {
34839             this.el.on('resize', this.layout,this);
34840             this.el.on('show', this.layout,this);
34841         }
34842
34843     },
34844
34845     /**
34846      * Returns true if this layout is currently being updated
34847      * @return {Boolean}
34848      */
34849     isUpdating : function(){
34850         return this.updating;
34851     },
34852
34853     /**
34854      * Suspend the LayoutManager from doing auto-layouts while
34855      * making multiple add or remove calls
34856      */
34857     beginUpdate : function(){
34858         this.updating = true;
34859     },
34860
34861     /**
34862      * Restore auto-layouts and optionally disable the manager from performing a layout
34863      * @param {Boolean} noLayout true to disable a layout update
34864      */
34865     endUpdate : function(noLayout){
34866         this.updating = false;
34867         if(!noLayout){
34868             this.layout();
34869         }
34870     },
34871
34872     layout: function(){
34873         // abstract...
34874     },
34875
34876     onRegionResized : function(region, newSize){
34877         this.fireEvent("regionresized", region, newSize);
34878         this.layout();
34879     },
34880
34881     onRegionCollapsed : function(region){
34882         this.fireEvent("regioncollapsed", region);
34883     },
34884
34885     onRegionExpanded : function(region){
34886         this.fireEvent("regionexpanded", region);
34887     },
34888
34889     /**
34890      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34891      * performs box-model adjustments.
34892      * @return {Object} The size as an object {width: (the width), height: (the height)}
34893      */
34894     getViewSize : function()
34895     {
34896         var size;
34897         if(this.el.dom != document.body){
34898             size = this.el.getSize();
34899         }else{
34900             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34901         }
34902         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34903         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34904         return size;
34905     },
34906
34907     /**
34908      * Returns the Element this layout is bound to.
34909      * @return {Roo.Element}
34910      */
34911     getEl : function(){
34912         return this.el;
34913     },
34914
34915     /**
34916      * Returns the specified region.
34917      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34918      * @return {Roo.LayoutRegion}
34919      */
34920     getRegion : function(target){
34921         return this.regions[target.toLowerCase()];
34922     },
34923
34924     onWindowResize : function(){
34925         if(this.monitorWindowResize){
34926             this.layout();
34927         }
34928     }
34929 });
34930 /*
34931  * Based on:
34932  * Ext JS Library 1.1.1
34933  * Copyright(c) 2006-2007, Ext JS, LLC.
34934  *
34935  * Originally Released Under LGPL - original licence link has changed is not relivant.
34936  *
34937  * Fork - LGPL
34938  * <script type="text/javascript">
34939  */
34940 /**
34941  * @class Roo.bootstrap.layout.Border
34942  * @extends Roo.bootstrap.layout.Manager
34943  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34944  * please see: examples/bootstrap/nested.html<br><br>
34945  
34946 <b>The container the layout is rendered into can be either the body element or any other element.
34947 If it is not the body element, the container needs to either be an absolute positioned element,
34948 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34949 the container size if it is not the body element.</b>
34950
34951 * @constructor
34952 * Create a new Border
34953 * @param {Object} config Configuration options
34954  */
34955 Roo.bootstrap.layout.Border = function(config){
34956     config = config || {};
34957     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34958     
34959     
34960     
34961     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34962         if(config[region]){
34963             config[region].region = region;
34964             this.addRegion(config[region]);
34965         }
34966     },this);
34967     
34968 };
34969
34970 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34971
34972 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34973     /**
34974      * Creates and adds a new region if it doesn't already exist.
34975      * @param {String} target The target region key (north, south, east, west or center).
34976      * @param {Object} config The regions config object
34977      * @return {BorderLayoutRegion} The new region
34978      */
34979     addRegion : function(config)
34980     {
34981         if(!this.regions[config.region]){
34982             var r = this.factory(config);
34983             this.bindRegion(r);
34984         }
34985         return this.regions[config.region];
34986     },
34987
34988     // private (kinda)
34989     bindRegion : function(r){
34990         this.regions[r.config.region] = r;
34991         
34992         r.on("visibilitychange",    this.layout, this);
34993         r.on("paneladded",          this.layout, this);
34994         r.on("panelremoved",        this.layout, this);
34995         r.on("invalidated",         this.layout, this);
34996         r.on("resized",             this.onRegionResized, this);
34997         r.on("collapsed",           this.onRegionCollapsed, this);
34998         r.on("expanded",            this.onRegionExpanded, this);
34999     },
35000
35001     /**
35002      * Performs a layout update.
35003      */
35004     layout : function()
35005     {
35006         if(this.updating) {
35007             return;
35008         }
35009         
35010         // render all the rebions if they have not been done alreayd?
35011         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35012             if(this.regions[region] && !this.regions[region].bodyEl){
35013                 this.regions[region].onRender(this.el)
35014             }
35015         },this);
35016         
35017         var size = this.getViewSize();
35018         var w = size.width;
35019         var h = size.height;
35020         var centerW = w;
35021         var centerH = h;
35022         var centerY = 0;
35023         var centerX = 0;
35024         //var x = 0, y = 0;
35025
35026         var rs = this.regions;
35027         var north = rs["north"];
35028         var south = rs["south"]; 
35029         var west = rs["west"];
35030         var east = rs["east"];
35031         var center = rs["center"];
35032         //if(this.hideOnLayout){ // not supported anymore
35033             //c.el.setStyle("display", "none");
35034         //}
35035         if(north && north.isVisible()){
35036             var b = north.getBox();
35037             var m = north.getMargins();
35038             b.width = w - (m.left+m.right);
35039             b.x = m.left;
35040             b.y = m.top;
35041             centerY = b.height + b.y + m.bottom;
35042             centerH -= centerY;
35043             north.updateBox(this.safeBox(b));
35044         }
35045         if(south && south.isVisible()){
35046             var b = south.getBox();
35047             var m = south.getMargins();
35048             b.width = w - (m.left+m.right);
35049             b.x = m.left;
35050             var totalHeight = (b.height + m.top + m.bottom);
35051             b.y = h - totalHeight + m.top;
35052             centerH -= totalHeight;
35053             south.updateBox(this.safeBox(b));
35054         }
35055         if(west && west.isVisible()){
35056             var b = west.getBox();
35057             var m = west.getMargins();
35058             b.height = centerH - (m.top+m.bottom);
35059             b.x = m.left;
35060             b.y = centerY + m.top;
35061             var totalWidth = (b.width + m.left + m.right);
35062             centerX += totalWidth;
35063             centerW -= totalWidth;
35064             west.updateBox(this.safeBox(b));
35065         }
35066         if(east && east.isVisible()){
35067             var b = east.getBox();
35068             var m = east.getMargins();
35069             b.height = centerH - (m.top+m.bottom);
35070             var totalWidth = (b.width + m.left + m.right);
35071             b.x = w - totalWidth + m.left;
35072             b.y = centerY + m.top;
35073             centerW -= totalWidth;
35074             east.updateBox(this.safeBox(b));
35075         }
35076         if(center){
35077             var m = center.getMargins();
35078             var centerBox = {
35079                 x: centerX + m.left,
35080                 y: centerY + m.top,
35081                 width: centerW - (m.left+m.right),
35082                 height: centerH - (m.top+m.bottom)
35083             };
35084             //if(this.hideOnLayout){
35085                 //center.el.setStyle("display", "block");
35086             //}
35087             center.updateBox(this.safeBox(centerBox));
35088         }
35089         this.el.repaint();
35090         this.fireEvent("layout", this);
35091     },
35092
35093     // private
35094     safeBox : function(box){
35095         box.width = Math.max(0, box.width);
35096         box.height = Math.max(0, box.height);
35097         return box;
35098     },
35099
35100     /**
35101      * Adds a ContentPanel (or subclass) to this layout.
35102      * @param {String} target The target region key (north, south, east, west or center).
35103      * @param {Roo.ContentPanel} panel The panel to add
35104      * @return {Roo.ContentPanel} The added panel
35105      */
35106     add : function(target, panel){
35107          
35108         target = target.toLowerCase();
35109         return this.regions[target].add(panel);
35110     },
35111
35112     /**
35113      * Remove a ContentPanel (or subclass) to this layout.
35114      * @param {String} target The target region key (north, south, east, west or center).
35115      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35116      * @return {Roo.ContentPanel} The removed panel
35117      */
35118     remove : function(target, panel){
35119         target = target.toLowerCase();
35120         return this.regions[target].remove(panel);
35121     },
35122
35123     /**
35124      * Searches all regions for a panel with the specified id
35125      * @param {String} panelId
35126      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35127      */
35128     findPanel : function(panelId){
35129         var rs = this.regions;
35130         for(var target in rs){
35131             if(typeof rs[target] != "function"){
35132                 var p = rs[target].getPanel(panelId);
35133                 if(p){
35134                     return p;
35135                 }
35136             }
35137         }
35138         return null;
35139     },
35140
35141     /**
35142      * Searches all regions for a panel with the specified id and activates (shows) it.
35143      * @param {String/ContentPanel} panelId The panels id or the panel itself
35144      * @return {Roo.ContentPanel} The shown panel or null
35145      */
35146     showPanel : function(panelId) {
35147       var rs = this.regions;
35148       for(var target in rs){
35149          var r = rs[target];
35150          if(typeof r != "function"){
35151             if(r.hasPanel(panelId)){
35152                return r.showPanel(panelId);
35153             }
35154          }
35155       }
35156       return null;
35157    },
35158
35159    /**
35160      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35161      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35162      */
35163    /*
35164     restoreState : function(provider){
35165         if(!provider){
35166             provider = Roo.state.Manager;
35167         }
35168         var sm = new Roo.LayoutStateManager();
35169         sm.init(this, provider);
35170     },
35171 */
35172  
35173  
35174     /**
35175      * Adds a xtype elements to the layout.
35176      * <pre><code>
35177
35178 layout.addxtype({
35179        xtype : 'ContentPanel',
35180        region: 'west',
35181        items: [ .... ]
35182    }
35183 );
35184
35185 layout.addxtype({
35186         xtype : 'NestedLayoutPanel',
35187         region: 'west',
35188         layout: {
35189            center: { },
35190            west: { }   
35191         },
35192         items : [ ... list of content panels or nested layout panels.. ]
35193    }
35194 );
35195 </code></pre>
35196      * @param {Object} cfg Xtype definition of item to add.
35197      */
35198     addxtype : function(cfg)
35199     {
35200         // basically accepts a pannel...
35201         // can accept a layout region..!?!?
35202         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35203         
35204         
35205         // theory?  children can only be panels??
35206         
35207         //if (!cfg.xtype.match(/Panel$/)) {
35208         //    return false;
35209         //}
35210         var ret = false;
35211         
35212         if (typeof(cfg.region) == 'undefined') {
35213             Roo.log("Failed to add Panel, region was not set");
35214             Roo.log(cfg);
35215             return false;
35216         }
35217         var region = cfg.region;
35218         delete cfg.region;
35219         
35220           
35221         var xitems = [];
35222         if (cfg.items) {
35223             xitems = cfg.items;
35224             delete cfg.items;
35225         }
35226         var nb = false;
35227         
35228         switch(cfg.xtype) 
35229         {
35230             case 'Content':  // ContentPanel (el, cfg)
35231             case 'Scroll':  // ContentPanel (el, cfg)
35232             case 'View': 
35233                 cfg.autoCreate = true;
35234                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35235                 //} else {
35236                 //    var el = this.el.createChild();
35237                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35238                 //}
35239                 
35240                 this.add(region, ret);
35241                 break;
35242             
35243             /*
35244             case 'TreePanel': // our new panel!
35245                 cfg.el = this.el.createChild();
35246                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35247                 this.add(region, ret);
35248                 break;
35249             */
35250             
35251             case 'Nest': 
35252                 // create a new Layout (which is  a Border Layout...
35253                 
35254                 var clayout = cfg.layout;
35255                 clayout.el  = this.el.createChild();
35256                 clayout.items   = clayout.items  || [];
35257                 
35258                 delete cfg.layout;
35259                 
35260                 // replace this exitems with the clayout ones..
35261                 xitems = clayout.items;
35262                  
35263                 // force background off if it's in center...
35264                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35265                     cfg.background = false;
35266                 }
35267                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35268                 
35269                 
35270                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35271                 //console.log('adding nested layout panel '  + cfg.toSource());
35272                 this.add(region, ret);
35273                 nb = {}; /// find first...
35274                 break;
35275             
35276             case 'Grid':
35277                 
35278                 // needs grid and region
35279                 
35280                 //var el = this.getRegion(region).el.createChild();
35281                 /*
35282                  *var el = this.el.createChild();
35283                 // create the grid first...
35284                 cfg.grid.container = el;
35285                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35286                 */
35287                 
35288                 if (region == 'center' && this.active ) {
35289                     cfg.background = false;
35290                 }
35291                 
35292                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35293                 
35294                 this.add(region, ret);
35295                 /*
35296                 if (cfg.background) {
35297                     // render grid on panel activation (if panel background)
35298                     ret.on('activate', function(gp) {
35299                         if (!gp.grid.rendered) {
35300                     //        gp.grid.render(el);
35301                         }
35302                     });
35303                 } else {
35304                   //  cfg.grid.render(el);
35305                 }
35306                 */
35307                 break;
35308            
35309            
35310             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35311                 // it was the old xcomponent building that caused this before.
35312                 // espeically if border is the top element in the tree.
35313                 ret = this;
35314                 break; 
35315                 
35316                     
35317                 
35318                 
35319                 
35320             default:
35321                 /*
35322                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35323                     
35324                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35325                     this.add(region, ret);
35326                 } else {
35327                 */
35328                     Roo.log(cfg);
35329                     throw "Can not add '" + cfg.xtype + "' to Border";
35330                     return null;
35331              
35332                                 
35333              
35334         }
35335         this.beginUpdate();
35336         // add children..
35337         var region = '';
35338         var abn = {};
35339         Roo.each(xitems, function(i)  {
35340             region = nb && i.region ? i.region : false;
35341             
35342             var add = ret.addxtype(i);
35343            
35344             if (region) {
35345                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35346                 if (!i.background) {
35347                     abn[region] = nb[region] ;
35348                 }
35349             }
35350             
35351         });
35352         this.endUpdate();
35353
35354         // make the last non-background panel active..
35355         //if (nb) { Roo.log(abn); }
35356         if (nb) {
35357             
35358             for(var r in abn) {
35359                 region = this.getRegion(r);
35360                 if (region) {
35361                     // tried using nb[r], but it does not work..
35362                      
35363                     region.showPanel(abn[r]);
35364                    
35365                 }
35366             }
35367         }
35368         return ret;
35369         
35370     },
35371     
35372     
35373 // private
35374     factory : function(cfg)
35375     {
35376         
35377         var validRegions = Roo.bootstrap.layout.Border.regions;
35378
35379         var target = cfg.region;
35380         cfg.mgr = this;
35381         
35382         var r = Roo.bootstrap.layout;
35383         Roo.log(target);
35384         switch(target){
35385             case "north":
35386                 return new r.North(cfg);
35387             case "south":
35388                 return new r.South(cfg);
35389             case "east":
35390                 return new r.East(cfg);
35391             case "west":
35392                 return new r.West(cfg);
35393             case "center":
35394                 return new r.Center(cfg);
35395         }
35396         throw 'Layout region "'+target+'" not supported.';
35397     }
35398     
35399     
35400 });
35401  /*
35402  * Based on:
35403  * Ext JS Library 1.1.1
35404  * Copyright(c) 2006-2007, Ext JS, LLC.
35405  *
35406  * Originally Released Under LGPL - original licence link has changed is not relivant.
35407  *
35408  * Fork - LGPL
35409  * <script type="text/javascript">
35410  */
35411  
35412 /**
35413  * @class Roo.bootstrap.layout.Basic
35414  * @extends Roo.util.Observable
35415  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35416  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35417  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35418  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35419  * @cfg {string}   region  the region that it inhabits..
35420  * @cfg {bool}   skipConfig skip config?
35421  * 
35422
35423  */
35424 Roo.bootstrap.layout.Basic = function(config){
35425     
35426     this.mgr = config.mgr;
35427     
35428     this.position = config.region;
35429     
35430     var skipConfig = config.skipConfig;
35431     
35432     this.events = {
35433         /**
35434          * @scope Roo.BasicLayoutRegion
35435          */
35436         
35437         /**
35438          * @event beforeremove
35439          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35440          * @param {Roo.LayoutRegion} this
35441          * @param {Roo.ContentPanel} panel The panel
35442          * @param {Object} e The cancel event object
35443          */
35444         "beforeremove" : true,
35445         /**
35446          * @event invalidated
35447          * Fires when the layout for this region is changed.
35448          * @param {Roo.LayoutRegion} this
35449          */
35450         "invalidated" : true,
35451         /**
35452          * @event visibilitychange
35453          * Fires when this region is shown or hidden 
35454          * @param {Roo.LayoutRegion} this
35455          * @param {Boolean} visibility true or false
35456          */
35457         "visibilitychange" : true,
35458         /**
35459          * @event paneladded
35460          * Fires when a panel is added. 
35461          * @param {Roo.LayoutRegion} this
35462          * @param {Roo.ContentPanel} panel The panel
35463          */
35464         "paneladded" : true,
35465         /**
35466          * @event panelremoved
35467          * Fires when a panel is removed. 
35468          * @param {Roo.LayoutRegion} this
35469          * @param {Roo.ContentPanel} panel The panel
35470          */
35471         "panelremoved" : true,
35472         /**
35473          * @event beforecollapse
35474          * Fires when this region before collapse.
35475          * @param {Roo.LayoutRegion} this
35476          */
35477         "beforecollapse" : true,
35478         /**
35479          * @event collapsed
35480          * Fires when this region is collapsed.
35481          * @param {Roo.LayoutRegion} this
35482          */
35483         "collapsed" : true,
35484         /**
35485          * @event expanded
35486          * Fires when this region is expanded.
35487          * @param {Roo.LayoutRegion} this
35488          */
35489         "expanded" : true,
35490         /**
35491          * @event slideshow
35492          * Fires when this region is slid into view.
35493          * @param {Roo.LayoutRegion} this
35494          */
35495         "slideshow" : true,
35496         /**
35497          * @event slidehide
35498          * Fires when this region slides out of view. 
35499          * @param {Roo.LayoutRegion} this
35500          */
35501         "slidehide" : true,
35502         /**
35503          * @event panelactivated
35504          * Fires when a panel is activated. 
35505          * @param {Roo.LayoutRegion} this
35506          * @param {Roo.ContentPanel} panel The activated panel
35507          */
35508         "panelactivated" : true,
35509         /**
35510          * @event resized
35511          * Fires when the user resizes this region. 
35512          * @param {Roo.LayoutRegion} this
35513          * @param {Number} newSize The new size (width for east/west, height for north/south)
35514          */
35515         "resized" : true
35516     };
35517     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35518     this.panels = new Roo.util.MixedCollection();
35519     this.panels.getKey = this.getPanelId.createDelegate(this);
35520     this.box = null;
35521     this.activePanel = null;
35522     // ensure listeners are added...
35523     
35524     if (config.listeners || config.events) {
35525         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35526             listeners : config.listeners || {},
35527             events : config.events || {}
35528         });
35529     }
35530     
35531     if(skipConfig !== true){
35532         this.applyConfig(config);
35533     }
35534 };
35535
35536 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35537 {
35538     getPanelId : function(p){
35539         return p.getId();
35540     },
35541     
35542     applyConfig : function(config){
35543         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35544         this.config = config;
35545         
35546     },
35547     
35548     /**
35549      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35550      * the width, for horizontal (north, south) the height.
35551      * @param {Number} newSize The new width or height
35552      */
35553     resizeTo : function(newSize){
35554         var el = this.el ? this.el :
35555                  (this.activePanel ? this.activePanel.getEl() : null);
35556         if(el){
35557             switch(this.position){
35558                 case "east":
35559                 case "west":
35560                     el.setWidth(newSize);
35561                     this.fireEvent("resized", this, newSize);
35562                 break;
35563                 case "north":
35564                 case "south":
35565                     el.setHeight(newSize);
35566                     this.fireEvent("resized", this, newSize);
35567                 break;                
35568             }
35569         }
35570     },
35571     
35572     getBox : function(){
35573         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35574     },
35575     
35576     getMargins : function(){
35577         return this.margins;
35578     },
35579     
35580     updateBox : function(box){
35581         this.box = box;
35582         var el = this.activePanel.getEl();
35583         el.dom.style.left = box.x + "px";
35584         el.dom.style.top = box.y + "px";
35585         this.activePanel.setSize(box.width, box.height);
35586     },
35587     
35588     /**
35589      * Returns the container element for this region.
35590      * @return {Roo.Element}
35591      */
35592     getEl : function(){
35593         return this.activePanel;
35594     },
35595     
35596     /**
35597      * Returns true if this region is currently visible.
35598      * @return {Boolean}
35599      */
35600     isVisible : function(){
35601         return this.activePanel ? true : false;
35602     },
35603     
35604     setActivePanel : function(panel){
35605         panel = this.getPanel(panel);
35606         if(this.activePanel && this.activePanel != panel){
35607             this.activePanel.setActiveState(false);
35608             this.activePanel.getEl().setLeftTop(-10000,-10000);
35609         }
35610         this.activePanel = panel;
35611         panel.setActiveState(true);
35612         if(this.box){
35613             panel.setSize(this.box.width, this.box.height);
35614         }
35615         this.fireEvent("panelactivated", this, panel);
35616         this.fireEvent("invalidated");
35617     },
35618     
35619     /**
35620      * Show the specified panel.
35621      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35622      * @return {Roo.ContentPanel} The shown panel or null
35623      */
35624     showPanel : function(panel){
35625         panel = this.getPanel(panel);
35626         if(panel){
35627             this.setActivePanel(panel);
35628         }
35629         return panel;
35630     },
35631     
35632     /**
35633      * Get the active panel for this region.
35634      * @return {Roo.ContentPanel} The active panel or null
35635      */
35636     getActivePanel : function(){
35637         return this.activePanel;
35638     },
35639     
35640     /**
35641      * Add the passed ContentPanel(s)
35642      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35643      * @return {Roo.ContentPanel} The panel added (if only one was added)
35644      */
35645     add : function(panel){
35646         if(arguments.length > 1){
35647             for(var i = 0, len = arguments.length; i < len; i++) {
35648                 this.add(arguments[i]);
35649             }
35650             return null;
35651         }
35652         if(this.hasPanel(panel)){
35653             this.showPanel(panel);
35654             return panel;
35655         }
35656         var el = panel.getEl();
35657         if(el.dom.parentNode != this.mgr.el.dom){
35658             this.mgr.el.dom.appendChild(el.dom);
35659         }
35660         if(panel.setRegion){
35661             panel.setRegion(this);
35662         }
35663         this.panels.add(panel);
35664         el.setStyle("position", "absolute");
35665         if(!panel.background){
35666             this.setActivePanel(panel);
35667             if(this.config.initialSize && this.panels.getCount()==1){
35668                 this.resizeTo(this.config.initialSize);
35669             }
35670         }
35671         this.fireEvent("paneladded", this, panel);
35672         return panel;
35673     },
35674     
35675     /**
35676      * Returns true if the panel is in this region.
35677      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35678      * @return {Boolean}
35679      */
35680     hasPanel : function(panel){
35681         if(typeof panel == "object"){ // must be panel obj
35682             panel = panel.getId();
35683         }
35684         return this.getPanel(panel) ? true : false;
35685     },
35686     
35687     /**
35688      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35689      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35690      * @param {Boolean} preservePanel Overrides the config preservePanel option
35691      * @return {Roo.ContentPanel} The panel that was removed
35692      */
35693     remove : function(panel, preservePanel){
35694         panel = this.getPanel(panel);
35695         if(!panel){
35696             return null;
35697         }
35698         var e = {};
35699         this.fireEvent("beforeremove", this, panel, e);
35700         if(e.cancel === true){
35701             return null;
35702         }
35703         var panelId = panel.getId();
35704         this.panels.removeKey(panelId);
35705         return panel;
35706     },
35707     
35708     /**
35709      * Returns the panel specified or null if it's not in this region.
35710      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35711      * @return {Roo.ContentPanel}
35712      */
35713     getPanel : function(id){
35714         if(typeof id == "object"){ // must be panel obj
35715             return id;
35716         }
35717         return this.panels.get(id);
35718     },
35719     
35720     /**
35721      * Returns this regions position (north/south/east/west/center).
35722      * @return {String} 
35723      */
35724     getPosition: function(){
35725         return this.position;    
35726     }
35727 });/*
35728  * Based on:
35729  * Ext JS Library 1.1.1
35730  * Copyright(c) 2006-2007, Ext JS, LLC.
35731  *
35732  * Originally Released Under LGPL - original licence link has changed is not relivant.
35733  *
35734  * Fork - LGPL
35735  * <script type="text/javascript">
35736  */
35737  
35738 /**
35739  * @class Roo.bootstrap.layout.Region
35740  * @extends Roo.bootstrap.layout.Basic
35741  * This class represents a region in a layout manager.
35742  
35743  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35744  * @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})
35745  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35746  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35747  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35748  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35749  * @cfg {String}    title           The title for the region (overrides panel titles)
35750  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35751  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35752  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35753  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35754  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35755  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35756  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35757  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35758  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35759  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35760
35761  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35762  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35763  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35764  * @cfg {Number}    width           For East/West panels
35765  * @cfg {Number}    height          For North/South panels
35766  * @cfg {Boolean}   split           To show the splitter
35767  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35768  * 
35769  * @cfg {string}   cls             Extra CSS classes to add to region
35770  * 
35771  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35772  * @cfg {string}   region  the region that it inhabits..
35773  *
35774
35775  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35776  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35777
35778  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35779  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35780  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35781  */
35782 Roo.bootstrap.layout.Region = function(config)
35783 {
35784     this.applyConfig(config);
35785
35786     var mgr = config.mgr;
35787     var pos = config.region;
35788     config.skipConfig = true;
35789     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35790     
35791     if (mgr.el) {
35792         this.onRender(mgr.el);   
35793     }
35794      
35795     this.visible = true;
35796     this.collapsed = false;
35797     this.unrendered_panels = [];
35798 };
35799
35800 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35801
35802     position: '', // set by wrapper (eg. north/south etc..)
35803     unrendered_panels : null,  // unrendered panels.
35804     createBody : function(){
35805         /** This region's body element 
35806         * @type Roo.Element */
35807         this.bodyEl = this.el.createChild({
35808                 tag: "div",
35809                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35810         });
35811     },
35812
35813     onRender: function(ctr, pos)
35814     {
35815         var dh = Roo.DomHelper;
35816         /** This region's container element 
35817         * @type Roo.Element */
35818         this.el = dh.append(ctr.dom, {
35819                 tag: "div",
35820                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35821             }, true);
35822         /** This region's title element 
35823         * @type Roo.Element */
35824     
35825         this.titleEl = dh.append(this.el.dom,
35826             {
35827                     tag: "div",
35828                     unselectable: "on",
35829                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35830                     children:[
35831                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35832                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35833                     ]}, true);
35834         
35835         this.titleEl.enableDisplayMode();
35836         /** This region's title text element 
35837         * @type HTMLElement */
35838         this.titleTextEl = this.titleEl.dom.firstChild;
35839         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35840         /*
35841         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35842         this.closeBtn.enableDisplayMode();
35843         this.closeBtn.on("click", this.closeClicked, this);
35844         this.closeBtn.hide();
35845     */
35846         this.createBody(this.config);
35847         if(this.config.hideWhenEmpty){
35848             this.hide();
35849             this.on("paneladded", this.validateVisibility, this);
35850             this.on("panelremoved", this.validateVisibility, this);
35851         }
35852         if(this.autoScroll){
35853             this.bodyEl.setStyle("overflow", "auto");
35854         }else{
35855             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35856         }
35857         //if(c.titlebar !== false){
35858             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35859                 this.titleEl.hide();
35860             }else{
35861                 this.titleEl.show();
35862                 if(this.config.title){
35863                     this.titleTextEl.innerHTML = this.config.title;
35864                 }
35865             }
35866         //}
35867         if(this.config.collapsed){
35868             this.collapse(true);
35869         }
35870         if(this.config.hidden){
35871             this.hide();
35872         }
35873         
35874         if (this.unrendered_panels && this.unrendered_panels.length) {
35875             for (var i =0;i< this.unrendered_panels.length; i++) {
35876                 this.add(this.unrendered_panels[i]);
35877             }
35878             this.unrendered_panels = null;
35879             
35880         }
35881         
35882     },
35883     
35884     applyConfig : function(c)
35885     {
35886         /*
35887          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35888             var dh = Roo.DomHelper;
35889             if(c.titlebar !== false){
35890                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35891                 this.collapseBtn.on("click", this.collapse, this);
35892                 this.collapseBtn.enableDisplayMode();
35893                 /*
35894                 if(c.showPin === true || this.showPin){
35895                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35896                     this.stickBtn.enableDisplayMode();
35897                     this.stickBtn.on("click", this.expand, this);
35898                     this.stickBtn.hide();
35899                 }
35900                 
35901             }
35902             */
35903             /** This region's collapsed element
35904             * @type Roo.Element */
35905             /*
35906              *
35907             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35908                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35909             ]}, true);
35910             
35911             if(c.floatable !== false){
35912                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35913                this.collapsedEl.on("click", this.collapseClick, this);
35914             }
35915
35916             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35917                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35918                    id: "message", unselectable: "on", style:{"float":"left"}});
35919                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35920              }
35921             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35922             this.expandBtn.on("click", this.expand, this);
35923             
35924         }
35925         
35926         if(this.collapseBtn){
35927             this.collapseBtn.setVisible(c.collapsible == true);
35928         }
35929         
35930         this.cmargins = c.cmargins || this.cmargins ||
35931                          (this.position == "west" || this.position == "east" ?
35932                              {top: 0, left: 2, right:2, bottom: 0} :
35933                              {top: 2, left: 0, right:0, bottom: 2});
35934         */
35935         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35936         
35937         
35938         this.bottomTabs = c.tabPosition != "top";
35939         
35940         this.autoScroll = c.autoScroll || false;
35941         
35942         
35943        
35944         
35945         this.duration = c.duration || .30;
35946         this.slideDuration = c.slideDuration || .45;
35947         this.config = c;
35948        
35949     },
35950     /**
35951      * Returns true if this region is currently visible.
35952      * @return {Boolean}
35953      */
35954     isVisible : function(){
35955         return this.visible;
35956     },
35957
35958     /**
35959      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35960      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35961      */
35962     //setCollapsedTitle : function(title){
35963     //    title = title || "&#160;";
35964      //   if(this.collapsedTitleTextEl){
35965       //      this.collapsedTitleTextEl.innerHTML = title;
35966        // }
35967     //},
35968
35969     getBox : function(){
35970         var b;
35971       //  if(!this.collapsed){
35972             b = this.el.getBox(false, true);
35973        // }else{
35974           //  b = this.collapsedEl.getBox(false, true);
35975         //}
35976         return b;
35977     },
35978
35979     getMargins : function(){
35980         return this.margins;
35981         //return this.collapsed ? this.cmargins : this.margins;
35982     },
35983 /*
35984     highlight : function(){
35985         this.el.addClass("x-layout-panel-dragover");
35986     },
35987
35988     unhighlight : function(){
35989         this.el.removeClass("x-layout-panel-dragover");
35990     },
35991 */
35992     updateBox : function(box)
35993     {
35994         if (!this.bodyEl) {
35995             return; // not rendered yet..
35996         }
35997         
35998         this.box = box;
35999         if(!this.collapsed){
36000             this.el.dom.style.left = box.x + "px";
36001             this.el.dom.style.top = box.y + "px";
36002             this.updateBody(box.width, box.height);
36003         }else{
36004             this.collapsedEl.dom.style.left = box.x + "px";
36005             this.collapsedEl.dom.style.top = box.y + "px";
36006             this.collapsedEl.setSize(box.width, box.height);
36007         }
36008         if(this.tabs){
36009             this.tabs.autoSizeTabs();
36010         }
36011     },
36012
36013     updateBody : function(w, h)
36014     {
36015         if(w !== null){
36016             this.el.setWidth(w);
36017             w -= this.el.getBorderWidth("rl");
36018             if(this.config.adjustments){
36019                 w += this.config.adjustments[0];
36020             }
36021         }
36022         if(h !== null && h > 0){
36023             this.el.setHeight(h);
36024             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36025             h -= this.el.getBorderWidth("tb");
36026             if(this.config.adjustments){
36027                 h += this.config.adjustments[1];
36028             }
36029             this.bodyEl.setHeight(h);
36030             if(this.tabs){
36031                 h = this.tabs.syncHeight(h);
36032             }
36033         }
36034         if(this.panelSize){
36035             w = w !== null ? w : this.panelSize.width;
36036             h = h !== null ? h : this.panelSize.height;
36037         }
36038         if(this.activePanel){
36039             var el = this.activePanel.getEl();
36040             w = w !== null ? w : el.getWidth();
36041             h = h !== null ? h : el.getHeight();
36042             this.panelSize = {width: w, height: h};
36043             this.activePanel.setSize(w, h);
36044         }
36045         if(Roo.isIE && this.tabs){
36046             this.tabs.el.repaint();
36047         }
36048     },
36049
36050     /**
36051      * Returns the container element for this region.
36052      * @return {Roo.Element}
36053      */
36054     getEl : function(){
36055         return this.el;
36056     },
36057
36058     /**
36059      * Hides this region.
36060      */
36061     hide : function(){
36062         //if(!this.collapsed){
36063             this.el.dom.style.left = "-2000px";
36064             this.el.hide();
36065         //}else{
36066          //   this.collapsedEl.dom.style.left = "-2000px";
36067          //   this.collapsedEl.hide();
36068        // }
36069         this.visible = false;
36070         this.fireEvent("visibilitychange", this, false);
36071     },
36072
36073     /**
36074      * Shows this region if it was previously hidden.
36075      */
36076     show : function(){
36077         //if(!this.collapsed){
36078             this.el.show();
36079         //}else{
36080         //    this.collapsedEl.show();
36081        // }
36082         this.visible = true;
36083         this.fireEvent("visibilitychange", this, true);
36084     },
36085 /*
36086     closeClicked : function(){
36087         if(this.activePanel){
36088             this.remove(this.activePanel);
36089         }
36090     },
36091
36092     collapseClick : function(e){
36093         if(this.isSlid){
36094            e.stopPropagation();
36095            this.slideIn();
36096         }else{
36097            e.stopPropagation();
36098            this.slideOut();
36099         }
36100     },
36101 */
36102     /**
36103      * Collapses this region.
36104      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36105      */
36106     /*
36107     collapse : function(skipAnim, skipCheck = false){
36108         if(this.collapsed) {
36109             return;
36110         }
36111         
36112         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36113             
36114             this.collapsed = true;
36115             if(this.split){
36116                 this.split.el.hide();
36117             }
36118             if(this.config.animate && skipAnim !== true){
36119                 this.fireEvent("invalidated", this);
36120                 this.animateCollapse();
36121             }else{
36122                 this.el.setLocation(-20000,-20000);
36123                 this.el.hide();
36124                 this.collapsedEl.show();
36125                 this.fireEvent("collapsed", this);
36126                 this.fireEvent("invalidated", this);
36127             }
36128         }
36129         
36130     },
36131 */
36132     animateCollapse : function(){
36133         // overridden
36134     },
36135
36136     /**
36137      * Expands this region if it was previously collapsed.
36138      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36139      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36140      */
36141     /*
36142     expand : function(e, skipAnim){
36143         if(e) {
36144             e.stopPropagation();
36145         }
36146         if(!this.collapsed || this.el.hasActiveFx()) {
36147             return;
36148         }
36149         if(this.isSlid){
36150             this.afterSlideIn();
36151             skipAnim = true;
36152         }
36153         this.collapsed = false;
36154         if(this.config.animate && skipAnim !== true){
36155             this.animateExpand();
36156         }else{
36157             this.el.show();
36158             if(this.split){
36159                 this.split.el.show();
36160             }
36161             this.collapsedEl.setLocation(-2000,-2000);
36162             this.collapsedEl.hide();
36163             this.fireEvent("invalidated", this);
36164             this.fireEvent("expanded", this);
36165         }
36166     },
36167 */
36168     animateExpand : function(){
36169         // overridden
36170     },
36171
36172     initTabs : function()
36173     {
36174         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36175         
36176         var ts = new Roo.bootstrap.panel.Tabs({
36177                 el: this.bodyEl.dom,
36178                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36179                 disableTooltips: this.config.disableTabTips,
36180                 toolbar : this.config.toolbar
36181             });
36182         
36183         if(this.config.hideTabs){
36184             ts.stripWrap.setDisplayed(false);
36185         }
36186         this.tabs = ts;
36187         ts.resizeTabs = this.config.resizeTabs === true;
36188         ts.minTabWidth = this.config.minTabWidth || 40;
36189         ts.maxTabWidth = this.config.maxTabWidth || 250;
36190         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36191         ts.monitorResize = false;
36192         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36193         ts.bodyEl.addClass('roo-layout-tabs-body');
36194         this.panels.each(this.initPanelAsTab, this);
36195     },
36196
36197     initPanelAsTab : function(panel){
36198         var ti = this.tabs.addTab(
36199             panel.getEl().id,
36200             panel.getTitle(),
36201             null,
36202             this.config.closeOnTab && panel.isClosable(),
36203             panel.tpl
36204         );
36205         if(panel.tabTip !== undefined){
36206             ti.setTooltip(panel.tabTip);
36207         }
36208         ti.on("activate", function(){
36209               this.setActivePanel(panel);
36210         }, this);
36211         
36212         if(this.config.closeOnTab){
36213             ti.on("beforeclose", function(t, e){
36214                 e.cancel = true;
36215                 this.remove(panel);
36216             }, this);
36217         }
36218         
36219         panel.tabItem = ti;
36220         
36221         return ti;
36222     },
36223
36224     updatePanelTitle : function(panel, title)
36225     {
36226         if(this.activePanel == panel){
36227             this.updateTitle(title);
36228         }
36229         if(this.tabs){
36230             var ti = this.tabs.getTab(panel.getEl().id);
36231             ti.setText(title);
36232             if(panel.tabTip !== undefined){
36233                 ti.setTooltip(panel.tabTip);
36234             }
36235         }
36236     },
36237
36238     updateTitle : function(title){
36239         if(this.titleTextEl && !this.config.title){
36240             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36241         }
36242     },
36243
36244     setActivePanel : function(panel)
36245     {
36246         panel = this.getPanel(panel);
36247         if(this.activePanel && this.activePanel != panel){
36248             if(this.activePanel.setActiveState(false) === false){
36249                 return;
36250             }
36251         }
36252         this.activePanel = panel;
36253         panel.setActiveState(true);
36254         if(this.panelSize){
36255             panel.setSize(this.panelSize.width, this.panelSize.height);
36256         }
36257         if(this.closeBtn){
36258             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36259         }
36260         this.updateTitle(panel.getTitle());
36261         if(this.tabs){
36262             this.fireEvent("invalidated", this);
36263         }
36264         this.fireEvent("panelactivated", this, panel);
36265     },
36266
36267     /**
36268      * Shows the specified panel.
36269      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36270      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36271      */
36272     showPanel : function(panel)
36273     {
36274         panel = this.getPanel(panel);
36275         if(panel){
36276             if(this.tabs){
36277                 var tab = this.tabs.getTab(panel.getEl().id);
36278                 if(tab.isHidden()){
36279                     this.tabs.unhideTab(tab.id);
36280                 }
36281                 tab.activate();
36282             }else{
36283                 this.setActivePanel(panel);
36284             }
36285         }
36286         return panel;
36287     },
36288
36289     /**
36290      * Get the active panel for this region.
36291      * @return {Roo.ContentPanel} The active panel or null
36292      */
36293     getActivePanel : function(){
36294         return this.activePanel;
36295     },
36296
36297     validateVisibility : function(){
36298         if(this.panels.getCount() < 1){
36299             this.updateTitle("&#160;");
36300             this.closeBtn.hide();
36301             this.hide();
36302         }else{
36303             if(!this.isVisible()){
36304                 this.show();
36305             }
36306         }
36307     },
36308
36309     /**
36310      * Adds the passed ContentPanel(s) to this region.
36311      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36312      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36313      */
36314     add : function(panel)
36315     {
36316         if(arguments.length > 1){
36317             for(var i = 0, len = arguments.length; i < len; i++) {
36318                 this.add(arguments[i]);
36319             }
36320             return null;
36321         }
36322         
36323         // if we have not been rendered yet, then we can not really do much of this..
36324         if (!this.bodyEl) {
36325             this.unrendered_panels.push(panel);
36326             return panel;
36327         }
36328         
36329         
36330         
36331         
36332         if(this.hasPanel(panel)){
36333             this.showPanel(panel);
36334             return panel;
36335         }
36336         panel.setRegion(this);
36337         this.panels.add(panel);
36338        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36339             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36340             // and hide them... ???
36341             this.bodyEl.dom.appendChild(panel.getEl().dom);
36342             if(panel.background !== true){
36343                 this.setActivePanel(panel);
36344             }
36345             this.fireEvent("paneladded", this, panel);
36346             return panel;
36347         }
36348         */
36349         if(!this.tabs){
36350             this.initTabs();
36351         }else{
36352             this.initPanelAsTab(panel);
36353         }
36354         
36355         
36356         if(panel.background !== true){
36357             this.tabs.activate(panel.getEl().id);
36358         }
36359         this.fireEvent("paneladded", this, panel);
36360         return panel;
36361     },
36362
36363     /**
36364      * Hides the tab for the specified panel.
36365      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36366      */
36367     hidePanel : function(panel){
36368         if(this.tabs && (panel = this.getPanel(panel))){
36369             this.tabs.hideTab(panel.getEl().id);
36370         }
36371     },
36372
36373     /**
36374      * Unhides the tab for a previously hidden panel.
36375      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36376      */
36377     unhidePanel : function(panel){
36378         if(this.tabs && (panel = this.getPanel(panel))){
36379             this.tabs.unhideTab(panel.getEl().id);
36380         }
36381     },
36382
36383     clearPanels : function(){
36384         while(this.panels.getCount() > 0){
36385              this.remove(this.panels.first());
36386         }
36387     },
36388
36389     /**
36390      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36391      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36392      * @param {Boolean} preservePanel Overrides the config preservePanel option
36393      * @return {Roo.ContentPanel} The panel that was removed
36394      */
36395     remove : function(panel, preservePanel)
36396     {
36397         panel = this.getPanel(panel);
36398         if(!panel){
36399             return null;
36400         }
36401         var e = {};
36402         this.fireEvent("beforeremove", this, panel, e);
36403         if(e.cancel === true){
36404             return null;
36405         }
36406         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36407         var panelId = panel.getId();
36408         this.panels.removeKey(panelId);
36409         if(preservePanel){
36410             document.body.appendChild(panel.getEl().dom);
36411         }
36412         if(this.tabs){
36413             this.tabs.removeTab(panel.getEl().id);
36414         }else if (!preservePanel){
36415             this.bodyEl.dom.removeChild(panel.getEl().dom);
36416         }
36417         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36418             var p = this.panels.first();
36419             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36420             tempEl.appendChild(p.getEl().dom);
36421             this.bodyEl.update("");
36422             this.bodyEl.dom.appendChild(p.getEl().dom);
36423             tempEl = null;
36424             this.updateTitle(p.getTitle());
36425             this.tabs = null;
36426             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36427             this.setActivePanel(p);
36428         }
36429         panel.setRegion(null);
36430         if(this.activePanel == panel){
36431             this.activePanel = null;
36432         }
36433         if(this.config.autoDestroy !== false && preservePanel !== true){
36434             try{panel.destroy();}catch(e){}
36435         }
36436         this.fireEvent("panelremoved", this, panel);
36437         return panel;
36438     },
36439
36440     /**
36441      * Returns the TabPanel component used by this region
36442      * @return {Roo.TabPanel}
36443      */
36444     getTabs : function(){
36445         return this.tabs;
36446     },
36447
36448     createTool : function(parentEl, className){
36449         var btn = Roo.DomHelper.append(parentEl, {
36450             tag: "div",
36451             cls: "x-layout-tools-button",
36452             children: [ {
36453                 tag: "div",
36454                 cls: "roo-layout-tools-button-inner " + className,
36455                 html: "&#160;"
36456             }]
36457         }, true);
36458         btn.addClassOnOver("roo-layout-tools-button-over");
36459         return btn;
36460     }
36461 });/*
36462  * Based on:
36463  * Ext JS Library 1.1.1
36464  * Copyright(c) 2006-2007, Ext JS, LLC.
36465  *
36466  * Originally Released Under LGPL - original licence link has changed is not relivant.
36467  *
36468  * Fork - LGPL
36469  * <script type="text/javascript">
36470  */
36471  
36472
36473
36474 /**
36475  * @class Roo.SplitLayoutRegion
36476  * @extends Roo.LayoutRegion
36477  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36478  */
36479 Roo.bootstrap.layout.Split = function(config){
36480     this.cursor = config.cursor;
36481     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36482 };
36483
36484 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36485 {
36486     splitTip : "Drag to resize.",
36487     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36488     useSplitTips : false,
36489
36490     applyConfig : function(config){
36491         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36492     },
36493     
36494     onRender : function(ctr,pos) {
36495         
36496         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36497         if(!this.config.split){
36498             return;
36499         }
36500         if(!this.split){
36501             
36502             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36503                             tag: "div",
36504                             id: this.el.id + "-split",
36505                             cls: "roo-layout-split roo-layout-split-"+this.position,
36506                             html: "&#160;"
36507             });
36508             /** The SplitBar for this region 
36509             * @type Roo.SplitBar */
36510             // does not exist yet...
36511             Roo.log([this.position, this.orientation]);
36512             
36513             this.split = new Roo.bootstrap.SplitBar({
36514                 dragElement : splitEl,
36515                 resizingElement: this.el,
36516                 orientation : this.orientation
36517             });
36518             
36519             this.split.on("moved", this.onSplitMove, this);
36520             this.split.useShim = this.config.useShim === true;
36521             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36522             if(this.useSplitTips){
36523                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36524             }
36525             //if(config.collapsible){
36526             //    this.split.el.on("dblclick", this.collapse,  this);
36527             //}
36528         }
36529         if(typeof this.config.minSize != "undefined"){
36530             this.split.minSize = this.config.minSize;
36531         }
36532         if(typeof this.config.maxSize != "undefined"){
36533             this.split.maxSize = this.config.maxSize;
36534         }
36535         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36536             this.hideSplitter();
36537         }
36538         
36539     },
36540
36541     getHMaxSize : function(){
36542          var cmax = this.config.maxSize || 10000;
36543          var center = this.mgr.getRegion("center");
36544          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36545     },
36546
36547     getVMaxSize : function(){
36548          var cmax = this.config.maxSize || 10000;
36549          var center = this.mgr.getRegion("center");
36550          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36551     },
36552
36553     onSplitMove : function(split, newSize){
36554         this.fireEvent("resized", this, newSize);
36555     },
36556     
36557     /** 
36558      * Returns the {@link Roo.SplitBar} for this region.
36559      * @return {Roo.SplitBar}
36560      */
36561     getSplitBar : function(){
36562         return this.split;
36563     },
36564     
36565     hide : function(){
36566         this.hideSplitter();
36567         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36568     },
36569
36570     hideSplitter : function(){
36571         if(this.split){
36572             this.split.el.setLocation(-2000,-2000);
36573             this.split.el.hide();
36574         }
36575     },
36576
36577     show : function(){
36578         if(this.split){
36579             this.split.el.show();
36580         }
36581         Roo.bootstrap.layout.Split.superclass.show.call(this);
36582     },
36583     
36584     beforeSlide: function(){
36585         if(Roo.isGecko){// firefox overflow auto bug workaround
36586             this.bodyEl.clip();
36587             if(this.tabs) {
36588                 this.tabs.bodyEl.clip();
36589             }
36590             if(this.activePanel){
36591                 this.activePanel.getEl().clip();
36592                 
36593                 if(this.activePanel.beforeSlide){
36594                     this.activePanel.beforeSlide();
36595                 }
36596             }
36597         }
36598     },
36599     
36600     afterSlide : function(){
36601         if(Roo.isGecko){// firefox overflow auto bug workaround
36602             this.bodyEl.unclip();
36603             if(this.tabs) {
36604                 this.tabs.bodyEl.unclip();
36605             }
36606             if(this.activePanel){
36607                 this.activePanel.getEl().unclip();
36608                 if(this.activePanel.afterSlide){
36609                     this.activePanel.afterSlide();
36610                 }
36611             }
36612         }
36613     },
36614
36615     initAutoHide : function(){
36616         if(this.autoHide !== false){
36617             if(!this.autoHideHd){
36618                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36619                 this.autoHideHd = {
36620                     "mouseout": function(e){
36621                         if(!e.within(this.el, true)){
36622                             st.delay(500);
36623                         }
36624                     },
36625                     "mouseover" : function(e){
36626                         st.cancel();
36627                     },
36628                     scope : this
36629                 };
36630             }
36631             this.el.on(this.autoHideHd);
36632         }
36633     },
36634
36635     clearAutoHide : function(){
36636         if(this.autoHide !== false){
36637             this.el.un("mouseout", this.autoHideHd.mouseout);
36638             this.el.un("mouseover", this.autoHideHd.mouseover);
36639         }
36640     },
36641
36642     clearMonitor : function(){
36643         Roo.get(document).un("click", this.slideInIf, this);
36644     },
36645
36646     // these names are backwards but not changed for compat
36647     slideOut : function(){
36648         if(this.isSlid || this.el.hasActiveFx()){
36649             return;
36650         }
36651         this.isSlid = true;
36652         if(this.collapseBtn){
36653             this.collapseBtn.hide();
36654         }
36655         this.closeBtnState = this.closeBtn.getStyle('display');
36656         this.closeBtn.hide();
36657         if(this.stickBtn){
36658             this.stickBtn.show();
36659         }
36660         this.el.show();
36661         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36662         this.beforeSlide();
36663         this.el.setStyle("z-index", 10001);
36664         this.el.slideIn(this.getSlideAnchor(), {
36665             callback: function(){
36666                 this.afterSlide();
36667                 this.initAutoHide();
36668                 Roo.get(document).on("click", this.slideInIf, this);
36669                 this.fireEvent("slideshow", this);
36670             },
36671             scope: this,
36672             block: true
36673         });
36674     },
36675
36676     afterSlideIn : function(){
36677         this.clearAutoHide();
36678         this.isSlid = false;
36679         this.clearMonitor();
36680         this.el.setStyle("z-index", "");
36681         if(this.collapseBtn){
36682             this.collapseBtn.show();
36683         }
36684         this.closeBtn.setStyle('display', this.closeBtnState);
36685         if(this.stickBtn){
36686             this.stickBtn.hide();
36687         }
36688         this.fireEvent("slidehide", this);
36689     },
36690
36691     slideIn : function(cb){
36692         if(!this.isSlid || this.el.hasActiveFx()){
36693             Roo.callback(cb);
36694             return;
36695         }
36696         this.isSlid = false;
36697         this.beforeSlide();
36698         this.el.slideOut(this.getSlideAnchor(), {
36699             callback: function(){
36700                 this.el.setLeftTop(-10000, -10000);
36701                 this.afterSlide();
36702                 this.afterSlideIn();
36703                 Roo.callback(cb);
36704             },
36705             scope: this,
36706             block: true
36707         });
36708     },
36709     
36710     slideInIf : function(e){
36711         if(!e.within(this.el)){
36712             this.slideIn();
36713         }
36714     },
36715
36716     animateCollapse : function(){
36717         this.beforeSlide();
36718         this.el.setStyle("z-index", 20000);
36719         var anchor = this.getSlideAnchor();
36720         this.el.slideOut(anchor, {
36721             callback : function(){
36722                 this.el.setStyle("z-index", "");
36723                 this.collapsedEl.slideIn(anchor, {duration:.3});
36724                 this.afterSlide();
36725                 this.el.setLocation(-10000,-10000);
36726                 this.el.hide();
36727                 this.fireEvent("collapsed", this);
36728             },
36729             scope: this,
36730             block: true
36731         });
36732     },
36733
36734     animateExpand : function(){
36735         this.beforeSlide();
36736         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36737         this.el.setStyle("z-index", 20000);
36738         this.collapsedEl.hide({
36739             duration:.1
36740         });
36741         this.el.slideIn(this.getSlideAnchor(), {
36742             callback : function(){
36743                 this.el.setStyle("z-index", "");
36744                 this.afterSlide();
36745                 if(this.split){
36746                     this.split.el.show();
36747                 }
36748                 this.fireEvent("invalidated", this);
36749                 this.fireEvent("expanded", this);
36750             },
36751             scope: this,
36752             block: true
36753         });
36754     },
36755
36756     anchors : {
36757         "west" : "left",
36758         "east" : "right",
36759         "north" : "top",
36760         "south" : "bottom"
36761     },
36762
36763     sanchors : {
36764         "west" : "l",
36765         "east" : "r",
36766         "north" : "t",
36767         "south" : "b"
36768     },
36769
36770     canchors : {
36771         "west" : "tl-tr",
36772         "east" : "tr-tl",
36773         "north" : "tl-bl",
36774         "south" : "bl-tl"
36775     },
36776
36777     getAnchor : function(){
36778         return this.anchors[this.position];
36779     },
36780
36781     getCollapseAnchor : function(){
36782         return this.canchors[this.position];
36783     },
36784
36785     getSlideAnchor : function(){
36786         return this.sanchors[this.position];
36787     },
36788
36789     getAlignAdj : function(){
36790         var cm = this.cmargins;
36791         switch(this.position){
36792             case "west":
36793                 return [0, 0];
36794             break;
36795             case "east":
36796                 return [0, 0];
36797             break;
36798             case "north":
36799                 return [0, 0];
36800             break;
36801             case "south":
36802                 return [0, 0];
36803             break;
36804         }
36805     },
36806
36807     getExpandAdj : function(){
36808         var c = this.collapsedEl, cm = this.cmargins;
36809         switch(this.position){
36810             case "west":
36811                 return [-(cm.right+c.getWidth()+cm.left), 0];
36812             break;
36813             case "east":
36814                 return [cm.right+c.getWidth()+cm.left, 0];
36815             break;
36816             case "north":
36817                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36818             break;
36819             case "south":
36820                 return [0, cm.top+cm.bottom+c.getHeight()];
36821             break;
36822         }
36823     }
36824 });/*
36825  * Based on:
36826  * Ext JS Library 1.1.1
36827  * Copyright(c) 2006-2007, Ext JS, LLC.
36828  *
36829  * Originally Released Under LGPL - original licence link has changed is not relivant.
36830  *
36831  * Fork - LGPL
36832  * <script type="text/javascript">
36833  */
36834 /*
36835  * These classes are private internal classes
36836  */
36837 Roo.bootstrap.layout.Center = function(config){
36838     config.region = "center";
36839     Roo.bootstrap.layout.Region.call(this, config);
36840     this.visible = true;
36841     this.minWidth = config.minWidth || 20;
36842     this.minHeight = config.minHeight || 20;
36843 };
36844
36845 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36846     hide : function(){
36847         // center panel can't be hidden
36848     },
36849     
36850     show : function(){
36851         // center panel can't be hidden
36852     },
36853     
36854     getMinWidth: function(){
36855         return this.minWidth;
36856     },
36857     
36858     getMinHeight: function(){
36859         return this.minHeight;
36860     }
36861 });
36862
36863
36864
36865
36866  
36867
36868
36869
36870
36871
36872 Roo.bootstrap.layout.North = function(config)
36873 {
36874     config.region = 'north';
36875     config.cursor = 'n-resize';
36876     
36877     Roo.bootstrap.layout.Split.call(this, config);
36878     
36879     
36880     if(this.split){
36881         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36882         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36883         this.split.el.addClass("roo-layout-split-v");
36884     }
36885     var size = config.initialSize || config.height;
36886     if(typeof size != "undefined"){
36887         this.el.setHeight(size);
36888     }
36889 };
36890 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36891 {
36892     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36893     
36894     
36895     
36896     getBox : function(){
36897         if(this.collapsed){
36898             return this.collapsedEl.getBox();
36899         }
36900         var box = this.el.getBox();
36901         if(this.split){
36902             box.height += this.split.el.getHeight();
36903         }
36904         return box;
36905     },
36906     
36907     updateBox : function(box){
36908         if(this.split && !this.collapsed){
36909             box.height -= this.split.el.getHeight();
36910             this.split.el.setLeft(box.x);
36911             this.split.el.setTop(box.y+box.height);
36912             this.split.el.setWidth(box.width);
36913         }
36914         if(this.collapsed){
36915             this.updateBody(box.width, null);
36916         }
36917         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36918     }
36919 });
36920
36921
36922
36923
36924
36925 Roo.bootstrap.layout.South = function(config){
36926     config.region = 'south';
36927     config.cursor = 's-resize';
36928     Roo.bootstrap.layout.Split.call(this, config);
36929     if(this.split){
36930         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36931         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36932         this.split.el.addClass("roo-layout-split-v");
36933     }
36934     var size = config.initialSize || config.height;
36935     if(typeof size != "undefined"){
36936         this.el.setHeight(size);
36937     }
36938 };
36939
36940 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36941     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36942     getBox : function(){
36943         if(this.collapsed){
36944             return this.collapsedEl.getBox();
36945         }
36946         var box = this.el.getBox();
36947         if(this.split){
36948             var sh = this.split.el.getHeight();
36949             box.height += sh;
36950             box.y -= sh;
36951         }
36952         return box;
36953     },
36954     
36955     updateBox : function(box){
36956         if(this.split && !this.collapsed){
36957             var sh = this.split.el.getHeight();
36958             box.height -= sh;
36959             box.y += sh;
36960             this.split.el.setLeft(box.x);
36961             this.split.el.setTop(box.y-sh);
36962             this.split.el.setWidth(box.width);
36963         }
36964         if(this.collapsed){
36965             this.updateBody(box.width, null);
36966         }
36967         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36968     }
36969 });
36970
36971 Roo.bootstrap.layout.East = function(config){
36972     config.region = "east";
36973     config.cursor = "e-resize";
36974     Roo.bootstrap.layout.Split.call(this, config);
36975     if(this.split){
36976         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36977         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36978         this.split.el.addClass("roo-layout-split-h");
36979     }
36980     var size = config.initialSize || config.width;
36981     if(typeof size != "undefined"){
36982         this.el.setWidth(size);
36983     }
36984 };
36985 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36986     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36987     getBox : function(){
36988         if(this.collapsed){
36989             return this.collapsedEl.getBox();
36990         }
36991         var box = this.el.getBox();
36992         if(this.split){
36993             var sw = this.split.el.getWidth();
36994             box.width += sw;
36995             box.x -= sw;
36996         }
36997         return box;
36998     },
36999
37000     updateBox : function(box){
37001         if(this.split && !this.collapsed){
37002             var sw = this.split.el.getWidth();
37003             box.width -= sw;
37004             this.split.el.setLeft(box.x);
37005             this.split.el.setTop(box.y);
37006             this.split.el.setHeight(box.height);
37007             box.x += sw;
37008         }
37009         if(this.collapsed){
37010             this.updateBody(null, box.height);
37011         }
37012         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37013     }
37014 });
37015
37016 Roo.bootstrap.layout.West = function(config){
37017     config.region = "west";
37018     config.cursor = "w-resize";
37019     
37020     Roo.bootstrap.layout.Split.call(this, config);
37021     if(this.split){
37022         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37023         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37024         this.split.el.addClass("roo-layout-split-h");
37025     }
37026     
37027 };
37028 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37029     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37030     
37031     onRender: function(ctr, pos)
37032     {
37033         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37034         var size = this.config.initialSize || this.config.width;
37035         if(typeof size != "undefined"){
37036             this.el.setWidth(size);
37037         }
37038     },
37039     
37040     getBox : function(){
37041         if(this.collapsed){
37042             return this.collapsedEl.getBox();
37043         }
37044         var box = this.el.getBox();
37045         if(this.split){
37046             box.width += this.split.el.getWidth();
37047         }
37048         return box;
37049     },
37050     
37051     updateBox : function(box){
37052         if(this.split && !this.collapsed){
37053             var sw = this.split.el.getWidth();
37054             box.width -= sw;
37055             this.split.el.setLeft(box.x+box.width);
37056             this.split.el.setTop(box.y);
37057             this.split.el.setHeight(box.height);
37058         }
37059         if(this.collapsed){
37060             this.updateBody(null, box.height);
37061         }
37062         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37063     }
37064 });
37065 Roo.namespace("Roo.bootstrap.panel");/*
37066  * Based on:
37067  * Ext JS Library 1.1.1
37068  * Copyright(c) 2006-2007, Ext JS, LLC.
37069  *
37070  * Originally Released Under LGPL - original licence link has changed is not relivant.
37071  *
37072  * Fork - LGPL
37073  * <script type="text/javascript">
37074  */
37075 /**
37076  * @class Roo.ContentPanel
37077  * @extends Roo.util.Observable
37078  * A basic ContentPanel element.
37079  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37080  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37081  * @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
37082  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37083  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37084  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37085  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37086  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37087  * @cfg {String} title          The title for this panel
37088  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37089  * @cfg {String} url            Calls {@link #setUrl} with this value
37090  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37091  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37092  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37093  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37094  * @cfg {Boolean} badges render the badges
37095
37096  * @constructor
37097  * Create a new ContentPanel.
37098  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37099  * @param {String/Object} config A string to set only the title or a config object
37100  * @param {String} content (optional) Set the HTML content for this panel
37101  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37102  */
37103 Roo.bootstrap.panel.Content = function( config){
37104     
37105     this.tpl = config.tpl || false;
37106     
37107     var el = config.el;
37108     var content = config.content;
37109
37110     if(config.autoCreate){ // xtype is available if this is called from factory
37111         el = Roo.id();
37112     }
37113     this.el = Roo.get(el);
37114     if(!this.el && config && config.autoCreate){
37115         if(typeof config.autoCreate == "object"){
37116             if(!config.autoCreate.id){
37117                 config.autoCreate.id = config.id||el;
37118             }
37119             this.el = Roo.DomHelper.append(document.body,
37120                         config.autoCreate, true);
37121         }else{
37122             var elcfg =  {   tag: "div",
37123                             cls: "roo-layout-inactive-content",
37124                             id: config.id||el
37125                             };
37126             if (config.html) {
37127                 elcfg.html = config.html;
37128                 
37129             }
37130                         
37131             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37132         }
37133     } 
37134     this.closable = false;
37135     this.loaded = false;
37136     this.active = false;
37137    
37138       
37139     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37140         
37141         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37142         
37143         this.wrapEl = this.el; //this.el.wrap();
37144         var ti = [];
37145         if (config.toolbar.items) {
37146             ti = config.toolbar.items ;
37147             delete config.toolbar.items ;
37148         }
37149         
37150         var nitems = [];
37151         this.toolbar.render(this.wrapEl, 'before');
37152         for(var i =0;i < ti.length;i++) {
37153           //  Roo.log(['add child', items[i]]);
37154             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37155         }
37156         this.toolbar.items = nitems;
37157         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37158         delete config.toolbar;
37159         
37160     }
37161     /*
37162     // xtype created footer. - not sure if will work as we normally have to render first..
37163     if (this.footer && !this.footer.el && this.footer.xtype) {
37164         if (!this.wrapEl) {
37165             this.wrapEl = this.el.wrap();
37166         }
37167     
37168         this.footer.container = this.wrapEl.createChild();
37169          
37170         this.footer = Roo.factory(this.footer, Roo);
37171         
37172     }
37173     */
37174     
37175      if(typeof config == "string"){
37176         this.title = config;
37177     }else{
37178         Roo.apply(this, config);
37179     }
37180     
37181     if(this.resizeEl){
37182         this.resizeEl = Roo.get(this.resizeEl, true);
37183     }else{
37184         this.resizeEl = this.el;
37185     }
37186     // handle view.xtype
37187     
37188  
37189     
37190     
37191     this.addEvents({
37192         /**
37193          * @event activate
37194          * Fires when this panel is activated. 
37195          * @param {Roo.ContentPanel} this
37196          */
37197         "activate" : true,
37198         /**
37199          * @event deactivate
37200          * Fires when this panel is activated. 
37201          * @param {Roo.ContentPanel} this
37202          */
37203         "deactivate" : true,
37204
37205         /**
37206          * @event resize
37207          * Fires when this panel is resized if fitToFrame is true.
37208          * @param {Roo.ContentPanel} this
37209          * @param {Number} width The width after any component adjustments
37210          * @param {Number} height The height after any component adjustments
37211          */
37212         "resize" : true,
37213         
37214          /**
37215          * @event render
37216          * Fires when this tab is created
37217          * @param {Roo.ContentPanel} this
37218          */
37219         "render" : true
37220         
37221         
37222         
37223     });
37224     
37225
37226     
37227     
37228     if(this.autoScroll){
37229         this.resizeEl.setStyle("overflow", "auto");
37230     } else {
37231         // fix randome scrolling
37232         //this.el.on('scroll', function() {
37233         //    Roo.log('fix random scolling');
37234         //    this.scrollTo('top',0); 
37235         //});
37236     }
37237     content = content || this.content;
37238     if(content){
37239         this.setContent(content);
37240     }
37241     if(config && config.url){
37242         this.setUrl(this.url, this.params, this.loadOnce);
37243     }
37244     
37245     
37246     
37247     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37248     
37249     if (this.view && typeof(this.view.xtype) != 'undefined') {
37250         this.view.el = this.el.appendChild(document.createElement("div"));
37251         this.view = Roo.factory(this.view); 
37252         this.view.render  &&  this.view.render(false, '');  
37253     }
37254     
37255     
37256     this.fireEvent('render', this);
37257 };
37258
37259 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37260     
37261     tabTip : '',
37262     
37263     setRegion : function(region){
37264         this.region = region;
37265         this.setActiveClass(region && !this.background);
37266     },
37267     
37268     
37269     setActiveClass: function(state)
37270     {
37271         if(state){
37272            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37273            this.el.setStyle('position','relative');
37274         }else{
37275            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37276            this.el.setStyle('position', 'absolute');
37277         } 
37278     },
37279     
37280     /**
37281      * Returns the toolbar for this Panel if one was configured. 
37282      * @return {Roo.Toolbar} 
37283      */
37284     getToolbar : function(){
37285         return this.toolbar;
37286     },
37287     
37288     setActiveState : function(active)
37289     {
37290         this.active = active;
37291         this.setActiveClass(active);
37292         if(!active){
37293             if(this.fireEvent("deactivate", this) === false){
37294                 return false;
37295             }
37296             return true;
37297         }
37298         this.fireEvent("activate", this);
37299         return true;
37300     },
37301     /**
37302      * Updates this panel's element
37303      * @param {String} content The new content
37304      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37305     */
37306     setContent : function(content, loadScripts){
37307         this.el.update(content, loadScripts);
37308     },
37309
37310     ignoreResize : function(w, h){
37311         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37312             return true;
37313         }else{
37314             this.lastSize = {width: w, height: h};
37315             return false;
37316         }
37317     },
37318     /**
37319      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37320      * @return {Roo.UpdateManager} The UpdateManager
37321      */
37322     getUpdateManager : function(){
37323         return this.el.getUpdateManager();
37324     },
37325      /**
37326      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37327      * @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:
37328 <pre><code>
37329 panel.load({
37330     url: "your-url.php",
37331     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37332     callback: yourFunction,
37333     scope: yourObject, //(optional scope)
37334     discardUrl: false,
37335     nocache: false,
37336     text: "Loading...",
37337     timeout: 30,
37338     scripts: false
37339 });
37340 </code></pre>
37341      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37342      * 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.
37343      * @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}
37344      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37345      * @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.
37346      * @return {Roo.ContentPanel} this
37347      */
37348     load : function(){
37349         var um = this.el.getUpdateManager();
37350         um.update.apply(um, arguments);
37351         return this;
37352     },
37353
37354
37355     /**
37356      * 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.
37357      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37358      * @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)
37359      * @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)
37360      * @return {Roo.UpdateManager} The UpdateManager
37361      */
37362     setUrl : function(url, params, loadOnce){
37363         if(this.refreshDelegate){
37364             this.removeListener("activate", this.refreshDelegate);
37365         }
37366         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37367         this.on("activate", this.refreshDelegate);
37368         return this.el.getUpdateManager();
37369     },
37370     
37371     _handleRefresh : function(url, params, loadOnce){
37372         if(!loadOnce || !this.loaded){
37373             var updater = this.el.getUpdateManager();
37374             updater.update(url, params, this._setLoaded.createDelegate(this));
37375         }
37376     },
37377     
37378     _setLoaded : function(){
37379         this.loaded = true;
37380     }, 
37381     
37382     /**
37383      * Returns this panel's id
37384      * @return {String} 
37385      */
37386     getId : function(){
37387         return this.el.id;
37388     },
37389     
37390     /** 
37391      * Returns this panel's element - used by regiosn to add.
37392      * @return {Roo.Element} 
37393      */
37394     getEl : function(){
37395         return this.wrapEl || this.el;
37396     },
37397     
37398    
37399     
37400     adjustForComponents : function(width, height)
37401     {
37402         //Roo.log('adjustForComponents ');
37403         if(this.resizeEl != this.el){
37404             width -= this.el.getFrameWidth('lr');
37405             height -= this.el.getFrameWidth('tb');
37406         }
37407         if(this.toolbar){
37408             var te = this.toolbar.getEl();
37409             te.setWidth(width);
37410             height -= te.getHeight();
37411         }
37412         if(this.footer){
37413             var te = this.footer.getEl();
37414             te.setWidth(width);
37415             height -= te.getHeight();
37416         }
37417         
37418         
37419         if(this.adjustments){
37420             width += this.adjustments[0];
37421             height += this.adjustments[1];
37422         }
37423         return {"width": width, "height": height};
37424     },
37425     
37426     setSize : function(width, height){
37427         if(this.fitToFrame && !this.ignoreResize(width, height)){
37428             if(this.fitContainer && this.resizeEl != this.el){
37429                 this.el.setSize(width, height);
37430             }
37431             var size = this.adjustForComponents(width, height);
37432             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37433             this.fireEvent('resize', this, size.width, size.height);
37434         }
37435     },
37436     
37437     /**
37438      * Returns this panel's title
37439      * @return {String} 
37440      */
37441     getTitle : function(){
37442         
37443         if (typeof(this.title) != 'object') {
37444             return this.title;
37445         }
37446         
37447         var t = '';
37448         for (var k in this.title) {
37449             if (!this.title.hasOwnProperty(k)) {
37450                 continue;
37451             }
37452             
37453             if (k.indexOf('-') >= 0) {
37454                 var s = k.split('-');
37455                 for (var i = 0; i<s.length; i++) {
37456                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37457                 }
37458             } else {
37459                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37460             }
37461         }
37462         return t;
37463     },
37464     
37465     /**
37466      * Set this panel's title
37467      * @param {String} title
37468      */
37469     setTitle : function(title){
37470         this.title = title;
37471         if(this.region){
37472             this.region.updatePanelTitle(this, title);
37473         }
37474     },
37475     
37476     /**
37477      * Returns true is this panel was configured to be closable
37478      * @return {Boolean} 
37479      */
37480     isClosable : function(){
37481         return this.closable;
37482     },
37483     
37484     beforeSlide : function(){
37485         this.el.clip();
37486         this.resizeEl.clip();
37487     },
37488     
37489     afterSlide : function(){
37490         this.el.unclip();
37491         this.resizeEl.unclip();
37492     },
37493     
37494     /**
37495      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37496      *   Will fail silently if the {@link #setUrl} method has not been called.
37497      *   This does not activate the panel, just updates its content.
37498      */
37499     refresh : function(){
37500         if(this.refreshDelegate){
37501            this.loaded = false;
37502            this.refreshDelegate();
37503         }
37504     },
37505     
37506     /**
37507      * Destroys this panel
37508      */
37509     destroy : function(){
37510         this.el.removeAllListeners();
37511         var tempEl = document.createElement("span");
37512         tempEl.appendChild(this.el.dom);
37513         tempEl.innerHTML = "";
37514         this.el.remove();
37515         this.el = null;
37516     },
37517     
37518     /**
37519      * form - if the content panel contains a form - this is a reference to it.
37520      * @type {Roo.form.Form}
37521      */
37522     form : false,
37523     /**
37524      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37525      *    This contains a reference to it.
37526      * @type {Roo.View}
37527      */
37528     view : false,
37529     
37530       /**
37531      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37532      * <pre><code>
37533
37534 layout.addxtype({
37535        xtype : 'Form',
37536        items: [ .... ]
37537    }
37538 );
37539
37540 </code></pre>
37541      * @param {Object} cfg Xtype definition of item to add.
37542      */
37543     
37544     
37545     getChildContainer: function () {
37546         return this.getEl();
37547     }
37548     
37549     
37550     /*
37551         var  ret = new Roo.factory(cfg);
37552         return ret;
37553         
37554         
37555         // add form..
37556         if (cfg.xtype.match(/^Form$/)) {
37557             
37558             var el;
37559             //if (this.footer) {
37560             //    el = this.footer.container.insertSibling(false, 'before');
37561             //} else {
37562                 el = this.el.createChild();
37563             //}
37564
37565             this.form = new  Roo.form.Form(cfg);
37566             
37567             
37568             if ( this.form.allItems.length) {
37569                 this.form.render(el.dom);
37570             }
37571             return this.form;
37572         }
37573         // should only have one of theses..
37574         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37575             // views.. should not be just added - used named prop 'view''
37576             
37577             cfg.el = this.el.appendChild(document.createElement("div"));
37578             // factory?
37579             
37580             var ret = new Roo.factory(cfg);
37581              
37582              ret.render && ret.render(false, ''); // render blank..
37583             this.view = ret;
37584             return ret;
37585         }
37586         return false;
37587     }
37588     \*/
37589 });
37590  
37591 /**
37592  * @class Roo.bootstrap.panel.Grid
37593  * @extends Roo.bootstrap.panel.Content
37594  * @constructor
37595  * Create a new GridPanel.
37596  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37597  * @param {Object} config A the config object
37598   
37599  */
37600
37601
37602
37603 Roo.bootstrap.panel.Grid = function(config)
37604 {
37605     
37606       
37607     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37608         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37609
37610     config.el = this.wrapper;
37611     //this.el = this.wrapper;
37612     
37613       if (config.container) {
37614         // ctor'ed from a Border/panel.grid
37615         
37616         
37617         this.wrapper.setStyle("overflow", "hidden");
37618         this.wrapper.addClass('roo-grid-container');
37619
37620     }
37621     
37622     
37623     if(config.toolbar){
37624         var tool_el = this.wrapper.createChild();    
37625         this.toolbar = Roo.factory(config.toolbar);
37626         var ti = [];
37627         if (config.toolbar.items) {
37628             ti = config.toolbar.items ;
37629             delete config.toolbar.items ;
37630         }
37631         
37632         var nitems = [];
37633         this.toolbar.render(tool_el);
37634         for(var i =0;i < ti.length;i++) {
37635           //  Roo.log(['add child', items[i]]);
37636             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37637         }
37638         this.toolbar.items = nitems;
37639         
37640         delete config.toolbar;
37641     }
37642     
37643     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37644     config.grid.scrollBody = true;;
37645     config.grid.monitorWindowResize = false; // turn off autosizing
37646     config.grid.autoHeight = false;
37647     config.grid.autoWidth = false;
37648     
37649     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37650     
37651     if (config.background) {
37652         // render grid on panel activation (if panel background)
37653         this.on('activate', function(gp) {
37654             if (!gp.grid.rendered) {
37655                 gp.grid.render(this.wrapper);
37656                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37657             }
37658         });
37659             
37660     } else {
37661         this.grid.render(this.wrapper);
37662         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37663
37664     }
37665     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37666     // ??? needed ??? config.el = this.wrapper;
37667     
37668     
37669     
37670   
37671     // xtype created footer. - not sure if will work as we normally have to render first..
37672     if (this.footer && !this.footer.el && this.footer.xtype) {
37673         
37674         var ctr = this.grid.getView().getFooterPanel(true);
37675         this.footer.dataSource = this.grid.dataSource;
37676         this.footer = Roo.factory(this.footer, Roo);
37677         this.footer.render(ctr);
37678         
37679     }
37680     
37681     
37682     
37683     
37684      
37685 };
37686
37687 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37688     getId : function(){
37689         return this.grid.id;
37690     },
37691     
37692     /**
37693      * Returns the grid for this panel
37694      * @return {Roo.bootstrap.Table} 
37695      */
37696     getGrid : function(){
37697         return this.grid;    
37698     },
37699     
37700     setSize : function(width, height){
37701         if(!this.ignoreResize(width, height)){
37702             var grid = this.grid;
37703             var size = this.adjustForComponents(width, height);
37704             var gridel = grid.getGridEl();
37705             gridel.setSize(size.width, size.height);
37706             /*
37707             var thd = grid.getGridEl().select('thead',true).first();
37708             var tbd = grid.getGridEl().select('tbody', true).first();
37709             if (tbd) {
37710                 tbd.setSize(width, height - thd.getHeight());
37711             }
37712             */
37713             grid.autoSize();
37714         }
37715     },
37716      
37717     
37718     
37719     beforeSlide : function(){
37720         this.grid.getView().scroller.clip();
37721     },
37722     
37723     afterSlide : function(){
37724         this.grid.getView().scroller.unclip();
37725     },
37726     
37727     destroy : function(){
37728         this.grid.destroy();
37729         delete this.grid;
37730         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37731     }
37732 });
37733
37734 /**
37735  * @class Roo.bootstrap.panel.Nest
37736  * @extends Roo.bootstrap.panel.Content
37737  * @constructor
37738  * Create a new Panel, that can contain a layout.Border.
37739  * 
37740  * 
37741  * @param {Roo.BorderLayout} layout The layout for this panel
37742  * @param {String/Object} config A string to set only the title or a config object
37743  */
37744 Roo.bootstrap.panel.Nest = function(config)
37745 {
37746     // construct with only one argument..
37747     /* FIXME - implement nicer consturctors
37748     if (layout.layout) {
37749         config = layout;
37750         layout = config.layout;
37751         delete config.layout;
37752     }
37753     if (layout.xtype && !layout.getEl) {
37754         // then layout needs constructing..
37755         layout = Roo.factory(layout, Roo);
37756     }
37757     */
37758     
37759     config.el =  config.layout.getEl();
37760     
37761     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37762     
37763     config.layout.monitorWindowResize = false; // turn off autosizing
37764     this.layout = config.layout;
37765     this.layout.getEl().addClass("roo-layout-nested-layout");
37766     
37767     
37768     
37769     
37770 };
37771
37772 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37773
37774     setSize : function(width, height){
37775         if(!this.ignoreResize(width, height)){
37776             var size = this.adjustForComponents(width, height);
37777             var el = this.layout.getEl();
37778             if (size.height < 1) {
37779                 el.setWidth(size.width);   
37780             } else {
37781                 el.setSize(size.width, size.height);
37782             }
37783             var touch = el.dom.offsetWidth;
37784             this.layout.layout();
37785             // ie requires a double layout on the first pass
37786             if(Roo.isIE && !this.initialized){
37787                 this.initialized = true;
37788                 this.layout.layout();
37789             }
37790         }
37791     },
37792     
37793     // activate all subpanels if not currently active..
37794     
37795     setActiveState : function(active){
37796         this.active = active;
37797         this.setActiveClass(active);
37798         
37799         if(!active){
37800             this.fireEvent("deactivate", this);
37801             return;
37802         }
37803         
37804         this.fireEvent("activate", this);
37805         // not sure if this should happen before or after..
37806         if (!this.layout) {
37807             return; // should not happen..
37808         }
37809         var reg = false;
37810         for (var r in this.layout.regions) {
37811             reg = this.layout.getRegion(r);
37812             if (reg.getActivePanel()) {
37813                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37814                 reg.setActivePanel(reg.getActivePanel());
37815                 continue;
37816             }
37817             if (!reg.panels.length) {
37818                 continue;
37819             }
37820             reg.showPanel(reg.getPanel(0));
37821         }
37822         
37823         
37824         
37825         
37826     },
37827     
37828     /**
37829      * Returns the nested BorderLayout for this panel
37830      * @return {Roo.BorderLayout} 
37831      */
37832     getLayout : function(){
37833         return this.layout;
37834     },
37835     
37836      /**
37837      * Adds a xtype elements to the layout of the nested panel
37838      * <pre><code>
37839
37840 panel.addxtype({
37841        xtype : 'ContentPanel',
37842        region: 'west',
37843        items: [ .... ]
37844    }
37845 );
37846
37847 panel.addxtype({
37848         xtype : 'NestedLayoutPanel',
37849         region: 'west',
37850         layout: {
37851            center: { },
37852            west: { }   
37853         },
37854         items : [ ... list of content panels or nested layout panels.. ]
37855    }
37856 );
37857 </code></pre>
37858      * @param {Object} cfg Xtype definition of item to add.
37859      */
37860     addxtype : function(cfg) {
37861         return this.layout.addxtype(cfg);
37862     
37863     }
37864 });        /*
37865  * Based on:
37866  * Ext JS Library 1.1.1
37867  * Copyright(c) 2006-2007, Ext JS, LLC.
37868  *
37869  * Originally Released Under LGPL - original licence link has changed is not relivant.
37870  *
37871  * Fork - LGPL
37872  * <script type="text/javascript">
37873  */
37874 /**
37875  * @class Roo.TabPanel
37876  * @extends Roo.util.Observable
37877  * A lightweight tab container.
37878  * <br><br>
37879  * Usage:
37880  * <pre><code>
37881 // basic tabs 1, built from existing content
37882 var tabs = new Roo.TabPanel("tabs1");
37883 tabs.addTab("script", "View Script");
37884 tabs.addTab("markup", "View Markup");
37885 tabs.activate("script");
37886
37887 // more advanced tabs, built from javascript
37888 var jtabs = new Roo.TabPanel("jtabs");
37889 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37890
37891 // set up the UpdateManager
37892 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37893 var updater = tab2.getUpdateManager();
37894 updater.setDefaultUrl("ajax1.htm");
37895 tab2.on('activate', updater.refresh, updater, true);
37896
37897 // Use setUrl for Ajax loading
37898 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37899 tab3.setUrl("ajax2.htm", null, true);
37900
37901 // Disabled tab
37902 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37903 tab4.disable();
37904
37905 jtabs.activate("jtabs-1");
37906  * </code></pre>
37907  * @constructor
37908  * Create a new TabPanel.
37909  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37910  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37911  */
37912 Roo.bootstrap.panel.Tabs = function(config){
37913     /**
37914     * The container element for this TabPanel.
37915     * @type Roo.Element
37916     */
37917     this.el = Roo.get(config.el);
37918     delete config.el;
37919     if(config){
37920         if(typeof config == "boolean"){
37921             this.tabPosition = config ? "bottom" : "top";
37922         }else{
37923             Roo.apply(this, config);
37924         }
37925     }
37926     
37927     if(this.tabPosition == "bottom"){
37928         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37929         this.el.addClass("roo-tabs-bottom");
37930     }
37931     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37932     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37933     this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
37934     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37935     if(Roo.isIE){
37936         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37937     }
37938     if(this.tabPosition != "bottom"){
37939         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37940          * @type Roo.Element
37941          */
37942         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37943         this.el.addClass("roo-tabs-top");
37944     }
37945     this.items = [];
37946
37947     this.bodyEl.setStyle("position", "relative");
37948
37949     this.active = null;
37950     this.activateDelegate = this.activate.createDelegate(this);
37951
37952     this.addEvents({
37953         /**
37954          * @event tabchange
37955          * Fires when the active tab changes
37956          * @param {Roo.TabPanel} this
37957          * @param {Roo.TabPanelItem} activePanel The new active tab
37958          */
37959         "tabchange": true,
37960         /**
37961          * @event beforetabchange
37962          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37963          * @param {Roo.TabPanel} this
37964          * @param {Object} e Set cancel to true on this object to cancel the tab change
37965          * @param {Roo.TabPanelItem} tab The tab being changed to
37966          */
37967         "beforetabchange" : true
37968     });
37969
37970     Roo.EventManager.onWindowResize(this.onResize, this);
37971     this.cpad = this.el.getPadding("lr");
37972     this.hiddenCount = 0;
37973
37974
37975     // toolbar on the tabbar support...
37976     if (this.toolbar) {
37977         alert("no toolbar support yet");
37978         this.toolbar  = false;
37979         /*
37980         var tcfg = this.toolbar;
37981         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37982         this.toolbar = new Roo.Toolbar(tcfg);
37983         if (Roo.isSafari) {
37984             var tbl = tcfg.container.child('table', true);
37985             tbl.setAttribute('width', '100%');
37986         }
37987         */
37988         
37989     }
37990    
37991
37992
37993     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37994 };
37995
37996 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37997     /*
37998      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37999      */
38000     tabPosition : "top",
38001     /*
38002      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38003      */
38004     currentTabWidth : 0,
38005     /*
38006      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38007      */
38008     minTabWidth : 40,
38009     /*
38010      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38011      */
38012     maxTabWidth : 250,
38013     /*
38014      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38015      */
38016     preferredTabWidth : 175,
38017     /*
38018      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38019      */
38020     resizeTabs : false,
38021     /*
38022      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38023      */
38024     monitorResize : true,
38025     /*
38026      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38027      */
38028     toolbar : false,
38029
38030     /**
38031      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38032      * @param {String} id The id of the div to use <b>or create</b>
38033      * @param {String} text The text for the tab
38034      * @param {String} content (optional) Content to put in the TabPanelItem body
38035      * @param {Boolean} closable (optional) True to create a close icon on the tab
38036      * @return {Roo.TabPanelItem} The created TabPanelItem
38037      */
38038     addTab : function(id, text, content, closable, tpl)
38039     {
38040         var item = new Roo.bootstrap.panel.TabItem({
38041             panel: this,
38042             id : id,
38043             text : text,
38044             closable : closable,
38045             tpl : tpl
38046         });
38047         this.addTabItem(item);
38048         if(content){
38049             item.setContent(content);
38050         }
38051         return item;
38052     },
38053
38054     /**
38055      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38056      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38057      * @return {Roo.TabPanelItem}
38058      */
38059     getTab : function(id){
38060         return this.items[id];
38061     },
38062
38063     /**
38064      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38065      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38066      */
38067     hideTab : function(id){
38068         var t = this.items[id];
38069         if(!t.isHidden()){
38070            t.setHidden(true);
38071            this.hiddenCount++;
38072            this.autoSizeTabs();
38073         }
38074     },
38075
38076     /**
38077      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38078      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38079      */
38080     unhideTab : function(id){
38081         var t = this.items[id];
38082         if(t.isHidden()){
38083            t.setHidden(false);
38084            this.hiddenCount--;
38085            this.autoSizeTabs();
38086         }
38087     },
38088
38089     /**
38090      * Adds an existing {@link Roo.TabPanelItem}.
38091      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38092      */
38093     addTabItem : function(item)
38094     {
38095         this.items[item.id] = item;
38096         this.items.push(item);
38097         this.autoSizeTabs();
38098       //  if(this.resizeTabs){
38099     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38100   //         this.autoSizeTabs();
38101 //        }else{
38102 //            item.autoSize();
38103        // }
38104     },
38105
38106     /**
38107      * Removes a {@link Roo.TabPanelItem}.
38108      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38109      */
38110     removeTab : function(id){
38111         var items = this.items;
38112         var tab = items[id];
38113         if(!tab) { return; }
38114         var index = items.indexOf(tab);
38115         if(this.active == tab && items.length > 1){
38116             var newTab = this.getNextAvailable(index);
38117             if(newTab) {
38118                 newTab.activate();
38119             }
38120         }
38121         this.stripEl.dom.removeChild(tab.pnode.dom);
38122         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38123             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38124         }
38125         items.splice(index, 1);
38126         delete this.items[tab.id];
38127         tab.fireEvent("close", tab);
38128         tab.purgeListeners();
38129         this.autoSizeTabs();
38130     },
38131
38132     getNextAvailable : function(start){
38133         var items = this.items;
38134         var index = start;
38135         // look for a next tab that will slide over to
38136         // replace the one being removed
38137         while(index < items.length){
38138             var item = items[++index];
38139             if(item && !item.isHidden()){
38140                 return item;
38141             }
38142         }
38143         // if one isn't found select the previous tab (on the left)
38144         index = start;
38145         while(index >= 0){
38146             var item = items[--index];
38147             if(item && !item.isHidden()){
38148                 return item;
38149             }
38150         }
38151         return null;
38152     },
38153
38154     /**
38155      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38156      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38157      */
38158     disableTab : function(id){
38159         var tab = this.items[id];
38160         if(tab && this.active != tab){
38161             tab.disable();
38162         }
38163     },
38164
38165     /**
38166      * Enables a {@link Roo.TabPanelItem} that is disabled.
38167      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38168      */
38169     enableTab : function(id){
38170         var tab = this.items[id];
38171         tab.enable();
38172     },
38173
38174     /**
38175      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38176      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38177      * @return {Roo.TabPanelItem} The TabPanelItem.
38178      */
38179     activate : function(id)
38180     {
38181         var tab = this.items[id];
38182         if(!tab){
38183             return null;
38184         }
38185         if(tab == this.active || tab.disabled){
38186             return tab;
38187         }
38188         var e = {};
38189         this.fireEvent("beforetabchange", this, e, tab);
38190         if(e.cancel !== true && !tab.disabled){
38191             if(this.active){
38192                 this.active.hide();
38193             }
38194             this.active = this.items[id];
38195             this.active.show();
38196             this.fireEvent("tabchange", this, this.active);
38197         }
38198         return tab;
38199     },
38200
38201     /**
38202      * Gets the active {@link Roo.TabPanelItem}.
38203      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38204      */
38205     getActiveTab : function(){
38206         return this.active;
38207     },
38208
38209     /**
38210      * Updates the tab body element to fit the height of the container element
38211      * for overflow scrolling
38212      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38213      */
38214     syncHeight : function(targetHeight){
38215         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38216         var bm = this.bodyEl.getMargins();
38217         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38218         this.bodyEl.setHeight(newHeight);
38219         return newHeight;
38220     },
38221
38222     onResize : function(){
38223         if(this.monitorResize){
38224             this.autoSizeTabs();
38225         }
38226     },
38227
38228     /**
38229      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38230      */
38231     beginUpdate : function(){
38232         this.updating = true;
38233     },
38234
38235     /**
38236      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38237      */
38238     endUpdate : function(){
38239         this.updating = false;
38240         this.autoSizeTabs();
38241     },
38242
38243     /**
38244      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38245      */
38246     autoSizeTabs : function()
38247     {
38248         var count = this.items.length;
38249         var vcount = count - this.hiddenCount;
38250         
38251         if (vcount < 2) {
38252             this.stripEl.hide();
38253         } else {
38254             this.stripEl.show();
38255         }
38256         
38257         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38258             return;
38259         }
38260         
38261         
38262         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38263         var availWidth = Math.floor(w / vcount);
38264         var b = this.stripBody;
38265         if(b.getWidth() > w){
38266             var tabs = this.items;
38267             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38268             if(availWidth < this.minTabWidth){
38269                 /*if(!this.sleft){    // incomplete scrolling code
38270                     this.createScrollButtons();
38271                 }
38272                 this.showScroll();
38273                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38274             }
38275         }else{
38276             if(this.currentTabWidth < this.preferredTabWidth){
38277                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38278             }
38279         }
38280     },
38281
38282     /**
38283      * Returns the number of tabs in this TabPanel.
38284      * @return {Number}
38285      */
38286      getCount : function(){
38287          return this.items.length;
38288      },
38289
38290     /**
38291      * Resizes all the tabs to the passed width
38292      * @param {Number} The new width
38293      */
38294     setTabWidth : function(width){
38295         this.currentTabWidth = width;
38296         for(var i = 0, len = this.items.length; i < len; i++) {
38297                 if(!this.items[i].isHidden()) {
38298                 this.items[i].setWidth(width);
38299             }
38300         }
38301     },
38302
38303     /**
38304      * Destroys this TabPanel
38305      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38306      */
38307     destroy : function(removeEl){
38308         Roo.EventManager.removeResizeListener(this.onResize, this);
38309         for(var i = 0, len = this.items.length; i < len; i++){
38310             this.items[i].purgeListeners();
38311         }
38312         if(removeEl === true){
38313             this.el.update("");
38314             this.el.remove();
38315         }
38316     },
38317     
38318     createStrip : function(container)
38319     {
38320         var strip = document.createElement("nav");
38321         strip.className = Roo.bootstrap.version == 4 ?
38322             "navbar-light bg-light" : 
38323             "navbar navbar-default"; //"x-tabs-wrap";
38324         container.appendChild(strip);
38325         return strip;
38326     },
38327     
38328     createStripList : function(strip)
38329     {
38330         // div wrapper for retard IE
38331         // returns the "tr" element.
38332         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38333         //'<div class="x-tabs-strip-wrap">'+
38334           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38335           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38336         return strip.firstChild; //.firstChild.firstChild.firstChild;
38337     },
38338     createBody : function(container)
38339     {
38340         var body = document.createElement("div");
38341         Roo.id(body, "tab-body");
38342         //Roo.fly(body).addClass("x-tabs-body");
38343         Roo.fly(body).addClass("tab-content");
38344         container.appendChild(body);
38345         return body;
38346     },
38347     createItemBody :function(bodyEl, id){
38348         var body = Roo.getDom(id);
38349         if(!body){
38350             body = document.createElement("div");
38351             body.id = id;
38352         }
38353         //Roo.fly(body).addClass("x-tabs-item-body");
38354         Roo.fly(body).addClass("tab-pane");
38355          bodyEl.insertBefore(body, bodyEl.firstChild);
38356         return body;
38357     },
38358     /** @private */
38359     createStripElements :  function(stripEl, text, closable, tpl)
38360     {
38361         var td = document.createElement("li"); // was td..
38362         td.className = 'nav-item';
38363         
38364         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38365         
38366         
38367         stripEl.appendChild(td);
38368         /*if(closable){
38369             td.className = "x-tabs-closable";
38370             if(!this.closeTpl){
38371                 this.closeTpl = new Roo.Template(
38372                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38373                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38374                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38375                 );
38376             }
38377             var el = this.closeTpl.overwrite(td, {"text": text});
38378             var close = el.getElementsByTagName("div")[0];
38379             var inner = el.getElementsByTagName("em")[0];
38380             return {"el": el, "close": close, "inner": inner};
38381         } else {
38382         */
38383         // not sure what this is..
38384 //            if(!this.tabTpl){
38385                 //this.tabTpl = new Roo.Template(
38386                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38387                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38388                 //);
38389 //                this.tabTpl = new Roo.Template(
38390 //                   '<a href="#">' +
38391 //                   '<span unselectable="on"' +
38392 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38393 //                            ' >{text}</span></a>'
38394 //                );
38395 //                
38396 //            }
38397
38398
38399             var template = tpl || this.tabTpl || false;
38400             
38401             if(!template){
38402                 template =  new Roo.Template(
38403                         Roo.bootstrap.version == 4 ? 
38404                             (
38405                                 '<a class="nav-link" href="#" unselectable="on"' +
38406                                      (this.disableTooltips ? '' : ' title="{text}"') +
38407                                      ' >{text}</a>'
38408                             ) : (
38409                                 '<a class="nav-link" href="#">' +
38410                                 '<span unselectable="on"' +
38411                                          (this.disableTooltips ? '' : ' title="{text}"') +
38412                                     ' >{text}</span></a>'
38413                             )
38414                 );
38415             }
38416             
38417             switch (typeof(template)) {
38418                 case 'object' :
38419                     break;
38420                 case 'string' :
38421                     template = new Roo.Template(template);
38422                     break;
38423                 default :
38424                     break;
38425             }
38426             
38427             var el = template.overwrite(td, {"text": text});
38428             
38429             var inner = el.getElementsByTagName("span")[0];
38430             
38431             return {"el": el, "inner": inner};
38432             
38433     }
38434         
38435     
38436 });
38437
38438 /**
38439  * @class Roo.TabPanelItem
38440  * @extends Roo.util.Observable
38441  * Represents an individual item (tab plus body) in a TabPanel.
38442  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38443  * @param {String} id The id of this TabPanelItem
38444  * @param {String} text The text for the tab of this TabPanelItem
38445  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38446  */
38447 Roo.bootstrap.panel.TabItem = function(config){
38448     /**
38449      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38450      * @type Roo.TabPanel
38451      */
38452     this.tabPanel = config.panel;
38453     /**
38454      * The id for this TabPanelItem
38455      * @type String
38456      */
38457     this.id = config.id;
38458     /** @private */
38459     this.disabled = false;
38460     /** @private */
38461     this.text = config.text;
38462     /** @private */
38463     this.loaded = false;
38464     this.closable = config.closable;
38465
38466     /**
38467      * The body element for this TabPanelItem.
38468      * @type Roo.Element
38469      */
38470     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38471     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38472     this.bodyEl.setStyle("display", "block");
38473     this.bodyEl.setStyle("zoom", "1");
38474     //this.hideAction();
38475
38476     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38477     /** @private */
38478     this.el = Roo.get(els.el);
38479     this.inner = Roo.get(els.inner, true);
38480      this.textEl = Roo.bootstrap.version == 4 ?
38481         this.el : Roo.get(this.el.dom.firstChild, true);
38482
38483     this.linode = Roo.get(els.el.parentNode, true);
38484     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38485
38486     
38487 //    this.el.on("mousedown", this.onTabMouseDown, this);
38488     this.el.on("click", this.onTabClick, this);
38489     /** @private */
38490     if(config.closable){
38491         var c = Roo.get(els.close, true);
38492         c.dom.title = this.closeText;
38493         c.addClassOnOver("close-over");
38494         c.on("click", this.closeClick, this);
38495      }
38496
38497     this.addEvents({
38498          /**
38499          * @event activate
38500          * Fires when this tab becomes the active tab.
38501          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38502          * @param {Roo.TabPanelItem} this
38503          */
38504         "activate": true,
38505         /**
38506          * @event beforeclose
38507          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38508          * @param {Roo.TabPanelItem} this
38509          * @param {Object} e Set cancel to true on this object to cancel the close.
38510          */
38511         "beforeclose": true,
38512         /**
38513          * @event close
38514          * Fires when this tab is closed.
38515          * @param {Roo.TabPanelItem} this
38516          */
38517          "close": true,
38518         /**
38519          * @event deactivate
38520          * Fires when this tab is no longer the active tab.
38521          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38522          * @param {Roo.TabPanelItem} this
38523          */
38524          "deactivate" : true
38525     });
38526     this.hidden = false;
38527
38528     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38529 };
38530
38531 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38532            {
38533     purgeListeners : function(){
38534        Roo.util.Observable.prototype.purgeListeners.call(this);
38535        this.el.removeAllListeners();
38536     },
38537     /**
38538      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38539      */
38540     show : function(){
38541         this.status_node.addClass("active");
38542         this.showAction();
38543         if(Roo.isOpera){
38544             this.tabPanel.stripWrap.repaint();
38545         }
38546         this.fireEvent("activate", this.tabPanel, this);
38547     },
38548
38549     /**
38550      * Returns true if this tab is the active tab.
38551      * @return {Boolean}
38552      */
38553     isActive : function(){
38554         return this.tabPanel.getActiveTab() == this;
38555     },
38556
38557     /**
38558      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38559      */
38560     hide : function(){
38561         this.status_node.removeClass("active");
38562         this.hideAction();
38563         this.fireEvent("deactivate", this.tabPanel, this);
38564     },
38565
38566     hideAction : function(){
38567         this.bodyEl.hide();
38568         this.bodyEl.setStyle("position", "absolute");
38569         this.bodyEl.setLeft("-20000px");
38570         this.bodyEl.setTop("-20000px");
38571     },
38572
38573     showAction : function(){
38574         this.bodyEl.setStyle("position", "relative");
38575         this.bodyEl.setTop("");
38576         this.bodyEl.setLeft("");
38577         this.bodyEl.show();
38578     },
38579
38580     /**
38581      * Set the tooltip for the tab.
38582      * @param {String} tooltip The tab's tooltip
38583      */
38584     setTooltip : function(text){
38585         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38586             this.textEl.dom.qtip = text;
38587             this.textEl.dom.removeAttribute('title');
38588         }else{
38589             this.textEl.dom.title = text;
38590         }
38591     },
38592
38593     onTabClick : function(e){
38594         e.preventDefault();
38595         this.tabPanel.activate(this.id);
38596     },
38597
38598     onTabMouseDown : function(e){
38599         e.preventDefault();
38600         this.tabPanel.activate(this.id);
38601     },
38602 /*
38603     getWidth : function(){
38604         return this.inner.getWidth();
38605     },
38606
38607     setWidth : function(width){
38608         var iwidth = width - this.linode.getPadding("lr");
38609         this.inner.setWidth(iwidth);
38610         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38611         this.linode.setWidth(width);
38612     },
38613 */
38614     /**
38615      * Show or hide the tab
38616      * @param {Boolean} hidden True to hide or false to show.
38617      */
38618     setHidden : function(hidden){
38619         this.hidden = hidden;
38620         this.linode.setStyle("display", hidden ? "none" : "");
38621     },
38622
38623     /**
38624      * Returns true if this tab is "hidden"
38625      * @return {Boolean}
38626      */
38627     isHidden : function(){
38628         return this.hidden;
38629     },
38630
38631     /**
38632      * Returns the text for this tab
38633      * @return {String}
38634      */
38635     getText : function(){
38636         return this.text;
38637     },
38638     /*
38639     autoSize : function(){
38640         //this.el.beginMeasure();
38641         this.textEl.setWidth(1);
38642         /*
38643          *  #2804 [new] Tabs in Roojs
38644          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38645          */
38646         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38647         //this.el.endMeasure();
38648     //},
38649
38650     /**
38651      * Sets the text for the tab (Note: this also sets the tooltip text)
38652      * @param {String} text The tab's text and tooltip
38653      */
38654     setText : function(text){
38655         this.text = text;
38656         this.textEl.update(text);
38657         this.setTooltip(text);
38658         //if(!this.tabPanel.resizeTabs){
38659         //    this.autoSize();
38660         //}
38661     },
38662     /**
38663      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38664      */
38665     activate : function(){
38666         this.tabPanel.activate(this.id);
38667     },
38668
38669     /**
38670      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38671      */
38672     disable : function(){
38673         if(this.tabPanel.active != this){
38674             this.disabled = true;
38675             this.status_node.addClass("disabled");
38676         }
38677     },
38678
38679     /**
38680      * Enables this TabPanelItem if it was previously disabled.
38681      */
38682     enable : function(){
38683         this.disabled = false;
38684         this.status_node.removeClass("disabled");
38685     },
38686
38687     /**
38688      * Sets the content for this TabPanelItem.
38689      * @param {String} content The content
38690      * @param {Boolean} loadScripts true to look for and load scripts
38691      */
38692     setContent : function(content, loadScripts){
38693         this.bodyEl.update(content, loadScripts);
38694     },
38695
38696     /**
38697      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38698      * @return {Roo.UpdateManager} The UpdateManager
38699      */
38700     getUpdateManager : function(){
38701         return this.bodyEl.getUpdateManager();
38702     },
38703
38704     /**
38705      * Set a URL to be used to load the content for this TabPanelItem.
38706      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38707      * @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)
38708      * @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)
38709      * @return {Roo.UpdateManager} The UpdateManager
38710      */
38711     setUrl : function(url, params, loadOnce){
38712         if(this.refreshDelegate){
38713             this.un('activate', this.refreshDelegate);
38714         }
38715         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38716         this.on("activate", this.refreshDelegate);
38717         return this.bodyEl.getUpdateManager();
38718     },
38719
38720     /** @private */
38721     _handleRefresh : function(url, params, loadOnce){
38722         if(!loadOnce || !this.loaded){
38723             var updater = this.bodyEl.getUpdateManager();
38724             updater.update(url, params, this._setLoaded.createDelegate(this));
38725         }
38726     },
38727
38728     /**
38729      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38730      *   Will fail silently if the setUrl method has not been called.
38731      *   This does not activate the panel, just updates its content.
38732      */
38733     refresh : function(){
38734         if(this.refreshDelegate){
38735            this.loaded = false;
38736            this.refreshDelegate();
38737         }
38738     },
38739
38740     /** @private */
38741     _setLoaded : function(){
38742         this.loaded = true;
38743     },
38744
38745     /** @private */
38746     closeClick : function(e){
38747         var o = {};
38748         e.stopEvent();
38749         this.fireEvent("beforeclose", this, o);
38750         if(o.cancel !== true){
38751             this.tabPanel.removeTab(this.id);
38752         }
38753     },
38754     /**
38755      * The text displayed in the tooltip for the close icon.
38756      * @type String
38757      */
38758     closeText : "Close this tab"
38759 });
38760 /**
38761 *    This script refer to:
38762 *    Title: International Telephone Input
38763 *    Author: Jack O'Connor
38764 *    Code version:  v12.1.12
38765 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38766 **/
38767
38768 Roo.bootstrap.PhoneInputData = function() {
38769     var d = [
38770       [
38771         "Afghanistan (‫افغانستان‬‎)",
38772         "af",
38773         "93"
38774       ],
38775       [
38776         "Albania (Shqipëri)",
38777         "al",
38778         "355"
38779       ],
38780       [
38781         "Algeria (‫الجزائر‬‎)",
38782         "dz",
38783         "213"
38784       ],
38785       [
38786         "American Samoa",
38787         "as",
38788         "1684"
38789       ],
38790       [
38791         "Andorra",
38792         "ad",
38793         "376"
38794       ],
38795       [
38796         "Angola",
38797         "ao",
38798         "244"
38799       ],
38800       [
38801         "Anguilla",
38802         "ai",
38803         "1264"
38804       ],
38805       [
38806         "Antigua and Barbuda",
38807         "ag",
38808         "1268"
38809       ],
38810       [
38811         "Argentina",
38812         "ar",
38813         "54"
38814       ],
38815       [
38816         "Armenia (Հայաստան)",
38817         "am",
38818         "374"
38819       ],
38820       [
38821         "Aruba",
38822         "aw",
38823         "297"
38824       ],
38825       [
38826         "Australia",
38827         "au",
38828         "61",
38829         0
38830       ],
38831       [
38832         "Austria (Österreich)",
38833         "at",
38834         "43"
38835       ],
38836       [
38837         "Azerbaijan (Azərbaycan)",
38838         "az",
38839         "994"
38840       ],
38841       [
38842         "Bahamas",
38843         "bs",
38844         "1242"
38845       ],
38846       [
38847         "Bahrain (‫البحرين‬‎)",
38848         "bh",
38849         "973"
38850       ],
38851       [
38852         "Bangladesh (বাংলাদেশ)",
38853         "bd",
38854         "880"
38855       ],
38856       [
38857         "Barbados",
38858         "bb",
38859         "1246"
38860       ],
38861       [
38862         "Belarus (Беларусь)",
38863         "by",
38864         "375"
38865       ],
38866       [
38867         "Belgium (België)",
38868         "be",
38869         "32"
38870       ],
38871       [
38872         "Belize",
38873         "bz",
38874         "501"
38875       ],
38876       [
38877         "Benin (Bénin)",
38878         "bj",
38879         "229"
38880       ],
38881       [
38882         "Bermuda",
38883         "bm",
38884         "1441"
38885       ],
38886       [
38887         "Bhutan (འབྲུག)",
38888         "bt",
38889         "975"
38890       ],
38891       [
38892         "Bolivia",
38893         "bo",
38894         "591"
38895       ],
38896       [
38897         "Bosnia and Herzegovina (Босна и Херцеговина)",
38898         "ba",
38899         "387"
38900       ],
38901       [
38902         "Botswana",
38903         "bw",
38904         "267"
38905       ],
38906       [
38907         "Brazil (Brasil)",
38908         "br",
38909         "55"
38910       ],
38911       [
38912         "British Indian Ocean Territory",
38913         "io",
38914         "246"
38915       ],
38916       [
38917         "British Virgin Islands",
38918         "vg",
38919         "1284"
38920       ],
38921       [
38922         "Brunei",
38923         "bn",
38924         "673"
38925       ],
38926       [
38927         "Bulgaria (България)",
38928         "bg",
38929         "359"
38930       ],
38931       [
38932         "Burkina Faso",
38933         "bf",
38934         "226"
38935       ],
38936       [
38937         "Burundi (Uburundi)",
38938         "bi",
38939         "257"
38940       ],
38941       [
38942         "Cambodia (កម្ពុជា)",
38943         "kh",
38944         "855"
38945       ],
38946       [
38947         "Cameroon (Cameroun)",
38948         "cm",
38949         "237"
38950       ],
38951       [
38952         "Canada",
38953         "ca",
38954         "1",
38955         1,
38956         ["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"]
38957       ],
38958       [
38959         "Cape Verde (Kabu Verdi)",
38960         "cv",
38961         "238"
38962       ],
38963       [
38964         "Caribbean Netherlands",
38965         "bq",
38966         "599",
38967         1
38968       ],
38969       [
38970         "Cayman Islands",
38971         "ky",
38972         "1345"
38973       ],
38974       [
38975         "Central African Republic (République centrafricaine)",
38976         "cf",
38977         "236"
38978       ],
38979       [
38980         "Chad (Tchad)",
38981         "td",
38982         "235"
38983       ],
38984       [
38985         "Chile",
38986         "cl",
38987         "56"
38988       ],
38989       [
38990         "China (中国)",
38991         "cn",
38992         "86"
38993       ],
38994       [
38995         "Christmas Island",
38996         "cx",
38997         "61",
38998         2
38999       ],
39000       [
39001         "Cocos (Keeling) Islands",
39002         "cc",
39003         "61",
39004         1
39005       ],
39006       [
39007         "Colombia",
39008         "co",
39009         "57"
39010       ],
39011       [
39012         "Comoros (‫جزر القمر‬‎)",
39013         "km",
39014         "269"
39015       ],
39016       [
39017         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39018         "cd",
39019         "243"
39020       ],
39021       [
39022         "Congo (Republic) (Congo-Brazzaville)",
39023         "cg",
39024         "242"
39025       ],
39026       [
39027         "Cook Islands",
39028         "ck",
39029         "682"
39030       ],
39031       [
39032         "Costa Rica",
39033         "cr",
39034         "506"
39035       ],
39036       [
39037         "Côte d’Ivoire",
39038         "ci",
39039         "225"
39040       ],
39041       [
39042         "Croatia (Hrvatska)",
39043         "hr",
39044         "385"
39045       ],
39046       [
39047         "Cuba",
39048         "cu",
39049         "53"
39050       ],
39051       [
39052         "Curaçao",
39053         "cw",
39054         "599",
39055         0
39056       ],
39057       [
39058         "Cyprus (Κύπρος)",
39059         "cy",
39060         "357"
39061       ],
39062       [
39063         "Czech Republic (Česká republika)",
39064         "cz",
39065         "420"
39066       ],
39067       [
39068         "Denmark (Danmark)",
39069         "dk",
39070         "45"
39071       ],
39072       [
39073         "Djibouti",
39074         "dj",
39075         "253"
39076       ],
39077       [
39078         "Dominica",
39079         "dm",
39080         "1767"
39081       ],
39082       [
39083         "Dominican Republic (República Dominicana)",
39084         "do",
39085         "1",
39086         2,
39087         ["809", "829", "849"]
39088       ],
39089       [
39090         "Ecuador",
39091         "ec",
39092         "593"
39093       ],
39094       [
39095         "Egypt (‫مصر‬‎)",
39096         "eg",
39097         "20"
39098       ],
39099       [
39100         "El Salvador",
39101         "sv",
39102         "503"
39103       ],
39104       [
39105         "Equatorial Guinea (Guinea Ecuatorial)",
39106         "gq",
39107         "240"
39108       ],
39109       [
39110         "Eritrea",
39111         "er",
39112         "291"
39113       ],
39114       [
39115         "Estonia (Eesti)",
39116         "ee",
39117         "372"
39118       ],
39119       [
39120         "Ethiopia",
39121         "et",
39122         "251"
39123       ],
39124       [
39125         "Falkland Islands (Islas Malvinas)",
39126         "fk",
39127         "500"
39128       ],
39129       [
39130         "Faroe Islands (Føroyar)",
39131         "fo",
39132         "298"
39133       ],
39134       [
39135         "Fiji",
39136         "fj",
39137         "679"
39138       ],
39139       [
39140         "Finland (Suomi)",
39141         "fi",
39142         "358",
39143         0
39144       ],
39145       [
39146         "France",
39147         "fr",
39148         "33"
39149       ],
39150       [
39151         "French Guiana (Guyane française)",
39152         "gf",
39153         "594"
39154       ],
39155       [
39156         "French Polynesia (Polynésie française)",
39157         "pf",
39158         "689"
39159       ],
39160       [
39161         "Gabon",
39162         "ga",
39163         "241"
39164       ],
39165       [
39166         "Gambia",
39167         "gm",
39168         "220"
39169       ],
39170       [
39171         "Georgia (საქართველო)",
39172         "ge",
39173         "995"
39174       ],
39175       [
39176         "Germany (Deutschland)",
39177         "de",
39178         "49"
39179       ],
39180       [
39181         "Ghana (Gaana)",
39182         "gh",
39183         "233"
39184       ],
39185       [
39186         "Gibraltar",
39187         "gi",
39188         "350"
39189       ],
39190       [
39191         "Greece (Ελλάδα)",
39192         "gr",
39193         "30"
39194       ],
39195       [
39196         "Greenland (Kalaallit Nunaat)",
39197         "gl",
39198         "299"
39199       ],
39200       [
39201         "Grenada",
39202         "gd",
39203         "1473"
39204       ],
39205       [
39206         "Guadeloupe",
39207         "gp",
39208         "590",
39209         0
39210       ],
39211       [
39212         "Guam",
39213         "gu",
39214         "1671"
39215       ],
39216       [
39217         "Guatemala",
39218         "gt",
39219         "502"
39220       ],
39221       [
39222         "Guernsey",
39223         "gg",
39224         "44",
39225         1
39226       ],
39227       [
39228         "Guinea (Guinée)",
39229         "gn",
39230         "224"
39231       ],
39232       [
39233         "Guinea-Bissau (Guiné Bissau)",
39234         "gw",
39235         "245"
39236       ],
39237       [
39238         "Guyana",
39239         "gy",
39240         "592"
39241       ],
39242       [
39243         "Haiti",
39244         "ht",
39245         "509"
39246       ],
39247       [
39248         "Honduras",
39249         "hn",
39250         "504"
39251       ],
39252       [
39253         "Hong Kong (香港)",
39254         "hk",
39255         "852"
39256       ],
39257       [
39258         "Hungary (Magyarország)",
39259         "hu",
39260         "36"
39261       ],
39262       [
39263         "Iceland (Ísland)",
39264         "is",
39265         "354"
39266       ],
39267       [
39268         "India (भारत)",
39269         "in",
39270         "91"
39271       ],
39272       [
39273         "Indonesia",
39274         "id",
39275         "62"
39276       ],
39277       [
39278         "Iran (‫ایران‬‎)",
39279         "ir",
39280         "98"
39281       ],
39282       [
39283         "Iraq (‫العراق‬‎)",
39284         "iq",
39285         "964"
39286       ],
39287       [
39288         "Ireland",
39289         "ie",
39290         "353"
39291       ],
39292       [
39293         "Isle of Man",
39294         "im",
39295         "44",
39296         2
39297       ],
39298       [
39299         "Israel (‫ישראל‬‎)",
39300         "il",
39301         "972"
39302       ],
39303       [
39304         "Italy (Italia)",
39305         "it",
39306         "39",
39307         0
39308       ],
39309       [
39310         "Jamaica",
39311         "jm",
39312         "1876"
39313       ],
39314       [
39315         "Japan (日本)",
39316         "jp",
39317         "81"
39318       ],
39319       [
39320         "Jersey",
39321         "je",
39322         "44",
39323         3
39324       ],
39325       [
39326         "Jordan (‫الأردن‬‎)",
39327         "jo",
39328         "962"
39329       ],
39330       [
39331         "Kazakhstan (Казахстан)",
39332         "kz",
39333         "7",
39334         1
39335       ],
39336       [
39337         "Kenya",
39338         "ke",
39339         "254"
39340       ],
39341       [
39342         "Kiribati",
39343         "ki",
39344         "686"
39345       ],
39346       [
39347         "Kosovo",
39348         "xk",
39349         "383"
39350       ],
39351       [
39352         "Kuwait (‫الكويت‬‎)",
39353         "kw",
39354         "965"
39355       ],
39356       [
39357         "Kyrgyzstan (Кыргызстан)",
39358         "kg",
39359         "996"
39360       ],
39361       [
39362         "Laos (ລາວ)",
39363         "la",
39364         "856"
39365       ],
39366       [
39367         "Latvia (Latvija)",
39368         "lv",
39369         "371"
39370       ],
39371       [
39372         "Lebanon (‫لبنان‬‎)",
39373         "lb",
39374         "961"
39375       ],
39376       [
39377         "Lesotho",
39378         "ls",
39379         "266"
39380       ],
39381       [
39382         "Liberia",
39383         "lr",
39384         "231"
39385       ],
39386       [
39387         "Libya (‫ليبيا‬‎)",
39388         "ly",
39389         "218"
39390       ],
39391       [
39392         "Liechtenstein",
39393         "li",
39394         "423"
39395       ],
39396       [
39397         "Lithuania (Lietuva)",
39398         "lt",
39399         "370"
39400       ],
39401       [
39402         "Luxembourg",
39403         "lu",
39404         "352"
39405       ],
39406       [
39407         "Macau (澳門)",
39408         "mo",
39409         "853"
39410       ],
39411       [
39412         "Macedonia (FYROM) (Македонија)",
39413         "mk",
39414         "389"
39415       ],
39416       [
39417         "Madagascar (Madagasikara)",
39418         "mg",
39419         "261"
39420       ],
39421       [
39422         "Malawi",
39423         "mw",
39424         "265"
39425       ],
39426       [
39427         "Malaysia",
39428         "my",
39429         "60"
39430       ],
39431       [
39432         "Maldives",
39433         "mv",
39434         "960"
39435       ],
39436       [
39437         "Mali",
39438         "ml",
39439         "223"
39440       ],
39441       [
39442         "Malta",
39443         "mt",
39444         "356"
39445       ],
39446       [
39447         "Marshall Islands",
39448         "mh",
39449         "692"
39450       ],
39451       [
39452         "Martinique",
39453         "mq",
39454         "596"
39455       ],
39456       [
39457         "Mauritania (‫موريتانيا‬‎)",
39458         "mr",
39459         "222"
39460       ],
39461       [
39462         "Mauritius (Moris)",
39463         "mu",
39464         "230"
39465       ],
39466       [
39467         "Mayotte",
39468         "yt",
39469         "262",
39470         1
39471       ],
39472       [
39473         "Mexico (México)",
39474         "mx",
39475         "52"
39476       ],
39477       [
39478         "Micronesia",
39479         "fm",
39480         "691"
39481       ],
39482       [
39483         "Moldova (Republica Moldova)",
39484         "md",
39485         "373"
39486       ],
39487       [
39488         "Monaco",
39489         "mc",
39490         "377"
39491       ],
39492       [
39493         "Mongolia (Монгол)",
39494         "mn",
39495         "976"
39496       ],
39497       [
39498         "Montenegro (Crna Gora)",
39499         "me",
39500         "382"
39501       ],
39502       [
39503         "Montserrat",
39504         "ms",
39505         "1664"
39506       ],
39507       [
39508         "Morocco (‫المغرب‬‎)",
39509         "ma",
39510         "212",
39511         0
39512       ],
39513       [
39514         "Mozambique (Moçambique)",
39515         "mz",
39516         "258"
39517       ],
39518       [
39519         "Myanmar (Burma) (မြန်မာ)",
39520         "mm",
39521         "95"
39522       ],
39523       [
39524         "Namibia (Namibië)",
39525         "na",
39526         "264"
39527       ],
39528       [
39529         "Nauru",
39530         "nr",
39531         "674"
39532       ],
39533       [
39534         "Nepal (नेपाल)",
39535         "np",
39536         "977"
39537       ],
39538       [
39539         "Netherlands (Nederland)",
39540         "nl",
39541         "31"
39542       ],
39543       [
39544         "New Caledonia (Nouvelle-Calédonie)",
39545         "nc",
39546         "687"
39547       ],
39548       [
39549         "New Zealand",
39550         "nz",
39551         "64"
39552       ],
39553       [
39554         "Nicaragua",
39555         "ni",
39556         "505"
39557       ],
39558       [
39559         "Niger (Nijar)",
39560         "ne",
39561         "227"
39562       ],
39563       [
39564         "Nigeria",
39565         "ng",
39566         "234"
39567       ],
39568       [
39569         "Niue",
39570         "nu",
39571         "683"
39572       ],
39573       [
39574         "Norfolk Island",
39575         "nf",
39576         "672"
39577       ],
39578       [
39579         "North Korea (조선 민주주의 인민 공화국)",
39580         "kp",
39581         "850"
39582       ],
39583       [
39584         "Northern Mariana Islands",
39585         "mp",
39586         "1670"
39587       ],
39588       [
39589         "Norway (Norge)",
39590         "no",
39591         "47",
39592         0
39593       ],
39594       [
39595         "Oman (‫عُمان‬‎)",
39596         "om",
39597         "968"
39598       ],
39599       [
39600         "Pakistan (‫پاکستان‬‎)",
39601         "pk",
39602         "92"
39603       ],
39604       [
39605         "Palau",
39606         "pw",
39607         "680"
39608       ],
39609       [
39610         "Palestine (‫فلسطين‬‎)",
39611         "ps",
39612         "970"
39613       ],
39614       [
39615         "Panama (Panamá)",
39616         "pa",
39617         "507"
39618       ],
39619       [
39620         "Papua New Guinea",
39621         "pg",
39622         "675"
39623       ],
39624       [
39625         "Paraguay",
39626         "py",
39627         "595"
39628       ],
39629       [
39630         "Peru (Perú)",
39631         "pe",
39632         "51"
39633       ],
39634       [
39635         "Philippines",
39636         "ph",
39637         "63"
39638       ],
39639       [
39640         "Poland (Polska)",
39641         "pl",
39642         "48"
39643       ],
39644       [
39645         "Portugal",
39646         "pt",
39647         "351"
39648       ],
39649       [
39650         "Puerto Rico",
39651         "pr",
39652         "1",
39653         3,
39654         ["787", "939"]
39655       ],
39656       [
39657         "Qatar (‫قطر‬‎)",
39658         "qa",
39659         "974"
39660       ],
39661       [
39662         "Réunion (La Réunion)",
39663         "re",
39664         "262",
39665         0
39666       ],
39667       [
39668         "Romania (România)",
39669         "ro",
39670         "40"
39671       ],
39672       [
39673         "Russia (Россия)",
39674         "ru",
39675         "7",
39676         0
39677       ],
39678       [
39679         "Rwanda",
39680         "rw",
39681         "250"
39682       ],
39683       [
39684         "Saint Barthélemy",
39685         "bl",
39686         "590",
39687         1
39688       ],
39689       [
39690         "Saint Helena",
39691         "sh",
39692         "290"
39693       ],
39694       [
39695         "Saint Kitts and Nevis",
39696         "kn",
39697         "1869"
39698       ],
39699       [
39700         "Saint Lucia",
39701         "lc",
39702         "1758"
39703       ],
39704       [
39705         "Saint Martin (Saint-Martin (partie française))",
39706         "mf",
39707         "590",
39708         2
39709       ],
39710       [
39711         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39712         "pm",
39713         "508"
39714       ],
39715       [
39716         "Saint Vincent and the Grenadines",
39717         "vc",
39718         "1784"
39719       ],
39720       [
39721         "Samoa",
39722         "ws",
39723         "685"
39724       ],
39725       [
39726         "San Marino",
39727         "sm",
39728         "378"
39729       ],
39730       [
39731         "São Tomé and Príncipe (São Tomé e Príncipe)",
39732         "st",
39733         "239"
39734       ],
39735       [
39736         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39737         "sa",
39738         "966"
39739       ],
39740       [
39741         "Senegal (Sénégal)",
39742         "sn",
39743         "221"
39744       ],
39745       [
39746         "Serbia (Србија)",
39747         "rs",
39748         "381"
39749       ],
39750       [
39751         "Seychelles",
39752         "sc",
39753         "248"
39754       ],
39755       [
39756         "Sierra Leone",
39757         "sl",
39758         "232"
39759       ],
39760       [
39761         "Singapore",
39762         "sg",
39763         "65"
39764       ],
39765       [
39766         "Sint Maarten",
39767         "sx",
39768         "1721"
39769       ],
39770       [
39771         "Slovakia (Slovensko)",
39772         "sk",
39773         "421"
39774       ],
39775       [
39776         "Slovenia (Slovenija)",
39777         "si",
39778         "386"
39779       ],
39780       [
39781         "Solomon Islands",
39782         "sb",
39783         "677"
39784       ],
39785       [
39786         "Somalia (Soomaaliya)",
39787         "so",
39788         "252"
39789       ],
39790       [
39791         "South Africa",
39792         "za",
39793         "27"
39794       ],
39795       [
39796         "South Korea (대한민국)",
39797         "kr",
39798         "82"
39799       ],
39800       [
39801         "South Sudan (‫جنوب السودان‬‎)",
39802         "ss",
39803         "211"
39804       ],
39805       [
39806         "Spain (España)",
39807         "es",
39808         "34"
39809       ],
39810       [
39811         "Sri Lanka (ශ්‍රී ලංකාව)",
39812         "lk",
39813         "94"
39814       ],
39815       [
39816         "Sudan (‫السودان‬‎)",
39817         "sd",
39818         "249"
39819       ],
39820       [
39821         "Suriname",
39822         "sr",
39823         "597"
39824       ],
39825       [
39826         "Svalbard and Jan Mayen",
39827         "sj",
39828         "47",
39829         1
39830       ],
39831       [
39832         "Swaziland",
39833         "sz",
39834         "268"
39835       ],
39836       [
39837         "Sweden (Sverige)",
39838         "se",
39839         "46"
39840       ],
39841       [
39842         "Switzerland (Schweiz)",
39843         "ch",
39844         "41"
39845       ],
39846       [
39847         "Syria (‫سوريا‬‎)",
39848         "sy",
39849         "963"
39850       ],
39851       [
39852         "Taiwan (台灣)",
39853         "tw",
39854         "886"
39855       ],
39856       [
39857         "Tajikistan",
39858         "tj",
39859         "992"
39860       ],
39861       [
39862         "Tanzania",
39863         "tz",
39864         "255"
39865       ],
39866       [
39867         "Thailand (ไทย)",
39868         "th",
39869         "66"
39870       ],
39871       [
39872         "Timor-Leste",
39873         "tl",
39874         "670"
39875       ],
39876       [
39877         "Togo",
39878         "tg",
39879         "228"
39880       ],
39881       [
39882         "Tokelau",
39883         "tk",
39884         "690"
39885       ],
39886       [
39887         "Tonga",
39888         "to",
39889         "676"
39890       ],
39891       [
39892         "Trinidad and Tobago",
39893         "tt",
39894         "1868"
39895       ],
39896       [
39897         "Tunisia (‫تونس‬‎)",
39898         "tn",
39899         "216"
39900       ],
39901       [
39902         "Turkey (Türkiye)",
39903         "tr",
39904         "90"
39905       ],
39906       [
39907         "Turkmenistan",
39908         "tm",
39909         "993"
39910       ],
39911       [
39912         "Turks and Caicos Islands",
39913         "tc",
39914         "1649"
39915       ],
39916       [
39917         "Tuvalu",
39918         "tv",
39919         "688"
39920       ],
39921       [
39922         "U.S. Virgin Islands",
39923         "vi",
39924         "1340"
39925       ],
39926       [
39927         "Uganda",
39928         "ug",
39929         "256"
39930       ],
39931       [
39932         "Ukraine (Україна)",
39933         "ua",
39934         "380"
39935       ],
39936       [
39937         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39938         "ae",
39939         "971"
39940       ],
39941       [
39942         "United Kingdom",
39943         "gb",
39944         "44",
39945         0
39946       ],
39947       [
39948         "United States",
39949         "us",
39950         "1",
39951         0
39952       ],
39953       [
39954         "Uruguay",
39955         "uy",
39956         "598"
39957       ],
39958       [
39959         "Uzbekistan (Oʻzbekiston)",
39960         "uz",
39961         "998"
39962       ],
39963       [
39964         "Vanuatu",
39965         "vu",
39966         "678"
39967       ],
39968       [
39969         "Vatican City (Città del Vaticano)",
39970         "va",
39971         "39",
39972         1
39973       ],
39974       [
39975         "Venezuela",
39976         "ve",
39977         "58"
39978       ],
39979       [
39980         "Vietnam (Việt Nam)",
39981         "vn",
39982         "84"
39983       ],
39984       [
39985         "Wallis and Futuna (Wallis-et-Futuna)",
39986         "wf",
39987         "681"
39988       ],
39989       [
39990         "Western Sahara (‫الصحراء الغربية‬‎)",
39991         "eh",
39992         "212",
39993         1
39994       ],
39995       [
39996         "Yemen (‫اليمن‬‎)",
39997         "ye",
39998         "967"
39999       ],
40000       [
40001         "Zambia",
40002         "zm",
40003         "260"
40004       ],
40005       [
40006         "Zimbabwe",
40007         "zw",
40008         "263"
40009       ],
40010       [
40011         "Åland Islands",
40012         "ax",
40013         "358",
40014         1
40015       ]
40016   ];
40017   
40018   return d;
40019 }/**
40020 *    This script refer to:
40021 *    Title: International Telephone Input
40022 *    Author: Jack O'Connor
40023 *    Code version:  v12.1.12
40024 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40025 **/
40026
40027 /**
40028  * @class Roo.bootstrap.PhoneInput
40029  * @extends Roo.bootstrap.TriggerField
40030  * An input with International dial-code selection
40031  
40032  * @cfg {String} defaultDialCode default '+852'
40033  * @cfg {Array} preferedCountries default []
40034   
40035  * @constructor
40036  * Create a new PhoneInput.
40037  * @param {Object} config Configuration options
40038  */
40039
40040 Roo.bootstrap.PhoneInput = function(config) {
40041     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40042 };
40043
40044 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40045         
40046         listWidth: undefined,
40047         
40048         selectedClass: 'active',
40049         
40050         invalidClass : "has-warning",
40051         
40052         validClass: 'has-success',
40053         
40054         allowed: '0123456789',
40055         
40056         max_length: 15,
40057         
40058         /**
40059          * @cfg {String} defaultDialCode The default dial code when initializing the input
40060          */
40061         defaultDialCode: '+852',
40062         
40063         /**
40064          * @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
40065          */
40066         preferedCountries: false,
40067         
40068         getAutoCreate : function()
40069         {
40070             var data = Roo.bootstrap.PhoneInputData();
40071             var align = this.labelAlign || this.parentLabelAlign();
40072             var id = Roo.id();
40073             
40074             this.allCountries = [];
40075             this.dialCodeMapping = [];
40076             
40077             for (var i = 0; i < data.length; i++) {
40078               var c = data[i];
40079               this.allCountries[i] = {
40080                 name: c[0],
40081                 iso2: c[1],
40082                 dialCode: c[2],
40083                 priority: c[3] || 0,
40084                 areaCodes: c[4] || null
40085               };
40086               this.dialCodeMapping[c[2]] = {
40087                   name: c[0],
40088                   iso2: c[1],
40089                   priority: c[3] || 0,
40090                   areaCodes: c[4] || null
40091               };
40092             }
40093             
40094             var cfg = {
40095                 cls: 'form-group',
40096                 cn: []
40097             };
40098             
40099             var input =  {
40100                 tag: 'input',
40101                 id : id,
40102                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40103                 maxlength: this.max_length,
40104                 cls : 'form-control tel-input',
40105                 autocomplete: 'new-password'
40106             };
40107             
40108             var hiddenInput = {
40109                 tag: 'input',
40110                 type: 'hidden',
40111                 cls: 'hidden-tel-input'
40112             };
40113             
40114             if (this.name) {
40115                 hiddenInput.name = this.name;
40116             }
40117             
40118             if (this.disabled) {
40119                 input.disabled = true;
40120             }
40121             
40122             var flag_container = {
40123                 tag: 'div',
40124                 cls: 'flag-box',
40125                 cn: [
40126                     {
40127                         tag: 'div',
40128                         cls: 'flag'
40129                     },
40130                     {
40131                         tag: 'div',
40132                         cls: 'caret'
40133                     }
40134                 ]
40135             };
40136             
40137             var box = {
40138                 tag: 'div',
40139                 cls: this.hasFeedback ? 'has-feedback' : '',
40140                 cn: [
40141                     hiddenInput,
40142                     input,
40143                     {
40144                         tag: 'input',
40145                         cls: 'dial-code-holder',
40146                         disabled: true
40147                     }
40148                 ]
40149             };
40150             
40151             var container = {
40152                 cls: 'roo-select2-container input-group',
40153                 cn: [
40154                     flag_container,
40155                     box
40156                 ]
40157             };
40158             
40159             if (this.fieldLabel.length) {
40160                 var indicator = {
40161                     tag: 'i',
40162                     tooltip: 'This field is required'
40163                 };
40164                 
40165                 var label = {
40166                     tag: 'label',
40167                     'for':  id,
40168                     cls: 'control-label',
40169                     cn: []
40170                 };
40171                 
40172                 var label_text = {
40173                     tag: 'span',
40174                     html: this.fieldLabel
40175                 };
40176                 
40177                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40178                 label.cn = [
40179                     indicator,
40180                     label_text
40181                 ];
40182                 
40183                 if(this.indicatorpos == 'right') {
40184                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40185                     label.cn = [
40186                         label_text,
40187                         indicator
40188                     ];
40189                 }
40190                 
40191                 if(align == 'left') {
40192                     container = {
40193                         tag: 'div',
40194                         cn: [
40195                             container
40196                         ]
40197                     };
40198                     
40199                     if(this.labelWidth > 12){
40200                         label.style = "width: " + this.labelWidth + 'px';
40201                     }
40202                     if(this.labelWidth < 13 && this.labelmd == 0){
40203                         this.labelmd = this.labelWidth;
40204                     }
40205                     if(this.labellg > 0){
40206                         label.cls += ' col-lg-' + this.labellg;
40207                         input.cls += ' col-lg-' + (12 - this.labellg);
40208                     }
40209                     if(this.labelmd > 0){
40210                         label.cls += ' col-md-' + this.labelmd;
40211                         container.cls += ' col-md-' + (12 - this.labelmd);
40212                     }
40213                     if(this.labelsm > 0){
40214                         label.cls += ' col-sm-' + this.labelsm;
40215                         container.cls += ' col-sm-' + (12 - this.labelsm);
40216                     }
40217                     if(this.labelxs > 0){
40218                         label.cls += ' col-xs-' + this.labelxs;
40219                         container.cls += ' col-xs-' + (12 - this.labelxs);
40220                     }
40221                 }
40222             }
40223             
40224             cfg.cn = [
40225                 label,
40226                 container
40227             ];
40228             
40229             var settings = this;
40230             
40231             ['xs','sm','md','lg'].map(function(size){
40232                 if (settings[size]) {
40233                     cfg.cls += ' col-' + size + '-' + settings[size];
40234                 }
40235             });
40236             
40237             this.store = new Roo.data.Store({
40238                 proxy : new Roo.data.MemoryProxy({}),
40239                 reader : new Roo.data.JsonReader({
40240                     fields : [
40241                         {
40242                             'name' : 'name',
40243                             'type' : 'string'
40244                         },
40245                         {
40246                             'name' : 'iso2',
40247                             'type' : 'string'
40248                         },
40249                         {
40250                             'name' : 'dialCode',
40251                             'type' : 'string'
40252                         },
40253                         {
40254                             'name' : 'priority',
40255                             'type' : 'string'
40256                         },
40257                         {
40258                             'name' : 'areaCodes',
40259                             'type' : 'string'
40260                         }
40261                     ]
40262                 })
40263             });
40264             
40265             if(!this.preferedCountries) {
40266                 this.preferedCountries = [
40267                     'hk',
40268                     'gb',
40269                     'us'
40270                 ];
40271             }
40272             
40273             var p = this.preferedCountries.reverse();
40274             
40275             if(p) {
40276                 for (var i = 0; i < p.length; i++) {
40277                     for (var j = 0; j < this.allCountries.length; j++) {
40278                         if(this.allCountries[j].iso2 == p[i]) {
40279                             var t = this.allCountries[j];
40280                             this.allCountries.splice(j,1);
40281                             this.allCountries.unshift(t);
40282                         }
40283                     } 
40284                 }
40285             }
40286             
40287             this.store.proxy.data = {
40288                 success: true,
40289                 data: this.allCountries
40290             };
40291             
40292             return cfg;
40293         },
40294         
40295         initEvents : function()
40296         {
40297             this.createList();
40298             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40299             
40300             this.indicator = this.indicatorEl();
40301             this.flag = this.flagEl();
40302             this.dialCodeHolder = this.dialCodeHolderEl();
40303             
40304             this.trigger = this.el.select('div.flag-box',true).first();
40305             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40306             
40307             var _this = this;
40308             
40309             (function(){
40310                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40311                 _this.list.setWidth(lw);
40312             }).defer(100);
40313             
40314             this.list.on('mouseover', this.onViewOver, this);
40315             this.list.on('mousemove', this.onViewMove, this);
40316             this.inputEl().on("keyup", this.onKeyUp, this);
40317             this.inputEl().on("keypress", this.onKeyPress, this);
40318             
40319             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40320
40321             this.view = new Roo.View(this.list, this.tpl, {
40322                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40323             });
40324             
40325             this.view.on('click', this.onViewClick, this);
40326             this.setValue(this.defaultDialCode);
40327         },
40328         
40329         onTriggerClick : function(e)
40330         {
40331             Roo.log('trigger click');
40332             if(this.disabled){
40333                 return;
40334             }
40335             
40336             if(this.isExpanded()){
40337                 this.collapse();
40338                 this.hasFocus = false;
40339             }else {
40340                 this.store.load({});
40341                 this.hasFocus = true;
40342                 this.expand();
40343             }
40344         },
40345         
40346         isExpanded : function()
40347         {
40348             return this.list.isVisible();
40349         },
40350         
40351         collapse : function()
40352         {
40353             if(!this.isExpanded()){
40354                 return;
40355             }
40356             this.list.hide();
40357             Roo.get(document).un('mousedown', this.collapseIf, this);
40358             Roo.get(document).un('mousewheel', this.collapseIf, this);
40359             this.fireEvent('collapse', this);
40360             this.validate();
40361         },
40362         
40363         expand : function()
40364         {
40365             Roo.log('expand');
40366
40367             if(this.isExpanded() || !this.hasFocus){
40368                 return;
40369             }
40370             
40371             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40372             this.list.setWidth(lw);
40373             
40374             this.list.show();
40375             this.restrictHeight();
40376             
40377             Roo.get(document).on('mousedown', this.collapseIf, this);
40378             Roo.get(document).on('mousewheel', this.collapseIf, this);
40379             
40380             this.fireEvent('expand', this);
40381         },
40382         
40383         restrictHeight : function()
40384         {
40385             this.list.alignTo(this.inputEl(), this.listAlign);
40386             this.list.alignTo(this.inputEl(), this.listAlign);
40387         },
40388         
40389         onViewOver : function(e, t)
40390         {
40391             if(this.inKeyMode){
40392                 return;
40393             }
40394             var item = this.view.findItemFromChild(t);
40395             
40396             if(item){
40397                 var index = this.view.indexOf(item);
40398                 this.select(index, false);
40399             }
40400         },
40401
40402         // private
40403         onViewClick : function(view, doFocus, el, e)
40404         {
40405             var index = this.view.getSelectedIndexes()[0];
40406             
40407             var r = this.store.getAt(index);
40408             
40409             if(r){
40410                 this.onSelect(r, index);
40411             }
40412             if(doFocus !== false && !this.blockFocus){
40413                 this.inputEl().focus();
40414             }
40415         },
40416         
40417         onViewMove : function(e, t)
40418         {
40419             this.inKeyMode = false;
40420         },
40421         
40422         select : function(index, scrollIntoView)
40423         {
40424             this.selectedIndex = index;
40425             this.view.select(index);
40426             if(scrollIntoView !== false){
40427                 var el = this.view.getNode(index);
40428                 if(el){
40429                     this.list.scrollChildIntoView(el, false);
40430                 }
40431             }
40432         },
40433         
40434         createList : function()
40435         {
40436             this.list = Roo.get(document.body).createChild({
40437                 tag: 'ul',
40438                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40439                 style: 'display:none'
40440             });
40441             
40442             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40443         },
40444         
40445         collapseIf : function(e)
40446         {
40447             var in_combo  = e.within(this.el);
40448             var in_list =  e.within(this.list);
40449             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40450             
40451             if (in_combo || in_list || is_list) {
40452                 return;
40453             }
40454             this.collapse();
40455         },
40456         
40457         onSelect : function(record, index)
40458         {
40459             if(this.fireEvent('beforeselect', this, record, index) !== false){
40460                 
40461                 this.setFlagClass(record.data.iso2);
40462                 this.setDialCode(record.data.dialCode);
40463                 this.hasFocus = false;
40464                 this.collapse();
40465                 this.fireEvent('select', this, record, index);
40466             }
40467         },
40468         
40469         flagEl : function()
40470         {
40471             var flag = this.el.select('div.flag',true).first();
40472             if(!flag){
40473                 return false;
40474             }
40475             return flag;
40476         },
40477         
40478         dialCodeHolderEl : function()
40479         {
40480             var d = this.el.select('input.dial-code-holder',true).first();
40481             if(!d){
40482                 return false;
40483             }
40484             return d;
40485         },
40486         
40487         setDialCode : function(v)
40488         {
40489             this.dialCodeHolder.dom.value = '+'+v;
40490         },
40491         
40492         setFlagClass : function(n)
40493         {
40494             this.flag.dom.className = 'flag '+n;
40495         },
40496         
40497         getValue : function()
40498         {
40499             var v = this.inputEl().getValue();
40500             if(this.dialCodeHolder) {
40501                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40502             }
40503             return v;
40504         },
40505         
40506         setValue : function(v)
40507         {
40508             var d = this.getDialCode(v);
40509             
40510             //invalid dial code
40511             if(v.length == 0 || !d || d.length == 0) {
40512                 if(this.rendered){
40513                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40514                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40515                 }
40516                 return;
40517             }
40518             
40519             //valid dial code
40520             this.setFlagClass(this.dialCodeMapping[d].iso2);
40521             this.setDialCode(d);
40522             this.inputEl().dom.value = v.replace('+'+d,'');
40523             this.hiddenEl().dom.value = this.getValue();
40524             
40525             this.validate();
40526         },
40527         
40528         getDialCode : function(v)
40529         {
40530             v = v ||  '';
40531             
40532             if (v.length == 0) {
40533                 return this.dialCodeHolder.dom.value;
40534             }
40535             
40536             var dialCode = "";
40537             if (v.charAt(0) != "+") {
40538                 return false;
40539             }
40540             var numericChars = "";
40541             for (var i = 1; i < v.length; i++) {
40542               var c = v.charAt(i);
40543               if (!isNaN(c)) {
40544                 numericChars += c;
40545                 if (this.dialCodeMapping[numericChars]) {
40546                   dialCode = v.substr(1, i);
40547                 }
40548                 if (numericChars.length == 4) {
40549                   break;
40550                 }
40551               }
40552             }
40553             return dialCode;
40554         },
40555         
40556         reset : function()
40557         {
40558             this.setValue(this.defaultDialCode);
40559             this.validate();
40560         },
40561         
40562         hiddenEl : function()
40563         {
40564             return this.el.select('input.hidden-tel-input',true).first();
40565         },
40566         
40567         // after setting val
40568         onKeyUp : function(e){
40569             this.setValue(this.getValue());
40570         },
40571         
40572         onKeyPress : function(e){
40573             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40574                 e.stopEvent();
40575             }
40576         }
40577         
40578 });
40579 /**
40580  * @class Roo.bootstrap.MoneyField
40581  * @extends Roo.bootstrap.ComboBox
40582  * Bootstrap MoneyField class
40583  * 
40584  * @constructor
40585  * Create a new MoneyField.
40586  * @param {Object} config Configuration options
40587  */
40588
40589 Roo.bootstrap.MoneyField = function(config) {
40590     
40591     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40592     
40593 };
40594
40595 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40596     
40597     /**
40598      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40599      */
40600     allowDecimals : true,
40601     /**
40602      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40603      */
40604     decimalSeparator : ".",
40605     /**
40606      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40607      */
40608     decimalPrecision : 0,
40609     /**
40610      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40611      */
40612     allowNegative : true,
40613     /**
40614      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40615      */
40616     allowZero: true,
40617     /**
40618      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40619      */
40620     minValue : Number.NEGATIVE_INFINITY,
40621     /**
40622      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40623      */
40624     maxValue : Number.MAX_VALUE,
40625     /**
40626      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40627      */
40628     minText : "The minimum value for this field is {0}",
40629     /**
40630      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40631      */
40632     maxText : "The maximum value for this field is {0}",
40633     /**
40634      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40635      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40636      */
40637     nanText : "{0} is not a valid number",
40638     /**
40639      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40640      */
40641     castInt : true,
40642     /**
40643      * @cfg {String} defaults currency of the MoneyField
40644      * value should be in lkey
40645      */
40646     defaultCurrency : false,
40647     /**
40648      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40649      */
40650     thousandsDelimiter : false,
40651     /**
40652      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40653      */
40654     max_length: false,
40655     
40656     inputlg : 9,
40657     inputmd : 9,
40658     inputsm : 9,
40659     inputxs : 6,
40660     
40661     store : false,
40662     
40663     getAutoCreate : function()
40664     {
40665         var align = this.labelAlign || this.parentLabelAlign();
40666         
40667         var id = Roo.id();
40668
40669         var cfg = {
40670             cls: 'form-group',
40671             cn: []
40672         };
40673
40674         var input =  {
40675             tag: 'input',
40676             id : id,
40677             cls : 'form-control roo-money-amount-input',
40678             autocomplete: 'new-password'
40679         };
40680         
40681         var hiddenInput = {
40682             tag: 'input',
40683             type: 'hidden',
40684             id: Roo.id(),
40685             cls: 'hidden-number-input'
40686         };
40687         
40688         if(this.max_length) {
40689             input.maxlength = this.max_length; 
40690         }
40691         
40692         if (this.name) {
40693             hiddenInput.name = this.name;
40694         }
40695
40696         if (this.disabled) {
40697             input.disabled = true;
40698         }
40699
40700         var clg = 12 - this.inputlg;
40701         var cmd = 12 - this.inputmd;
40702         var csm = 12 - this.inputsm;
40703         var cxs = 12 - this.inputxs;
40704         
40705         var container = {
40706             tag : 'div',
40707             cls : 'row roo-money-field',
40708             cn : [
40709                 {
40710                     tag : 'div',
40711                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40712                     cn : [
40713                         {
40714                             tag : 'div',
40715                             cls: 'roo-select2-container input-group',
40716                             cn: [
40717                                 {
40718                                     tag : 'input',
40719                                     cls : 'form-control roo-money-currency-input',
40720                                     autocomplete: 'new-password',
40721                                     readOnly : 1,
40722                                     name : this.currencyName
40723                                 },
40724                                 {
40725                                     tag :'span',
40726                                     cls : 'input-group-addon',
40727                                     cn : [
40728                                         {
40729                                             tag: 'span',
40730                                             cls: 'caret'
40731                                         }
40732                                     ]
40733                                 }
40734                             ]
40735                         }
40736                     ]
40737                 },
40738                 {
40739                     tag : 'div',
40740                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40741                     cn : [
40742                         {
40743                             tag: 'div',
40744                             cls: this.hasFeedback ? 'has-feedback' : '',
40745                             cn: [
40746                                 input
40747                             ]
40748                         }
40749                     ]
40750                 }
40751             ]
40752             
40753         };
40754         
40755         if (this.fieldLabel.length) {
40756             var indicator = {
40757                 tag: 'i',
40758                 tooltip: 'This field is required'
40759             };
40760
40761             var label = {
40762                 tag: 'label',
40763                 'for':  id,
40764                 cls: 'control-label',
40765                 cn: []
40766             };
40767
40768             var label_text = {
40769                 tag: 'span',
40770                 html: this.fieldLabel
40771             };
40772
40773             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40774             label.cn = [
40775                 indicator,
40776                 label_text
40777             ];
40778
40779             if(this.indicatorpos == 'right') {
40780                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40781                 label.cn = [
40782                     label_text,
40783                     indicator
40784                 ];
40785             }
40786
40787             if(align == 'left') {
40788                 container = {
40789                     tag: 'div',
40790                     cn: [
40791                         container
40792                     ]
40793                 };
40794
40795                 if(this.labelWidth > 12){
40796                     label.style = "width: " + this.labelWidth + 'px';
40797                 }
40798                 if(this.labelWidth < 13 && this.labelmd == 0){
40799                     this.labelmd = this.labelWidth;
40800                 }
40801                 if(this.labellg > 0){
40802                     label.cls += ' col-lg-' + this.labellg;
40803                     input.cls += ' col-lg-' + (12 - this.labellg);
40804                 }
40805                 if(this.labelmd > 0){
40806                     label.cls += ' col-md-' + this.labelmd;
40807                     container.cls += ' col-md-' + (12 - this.labelmd);
40808                 }
40809                 if(this.labelsm > 0){
40810                     label.cls += ' col-sm-' + this.labelsm;
40811                     container.cls += ' col-sm-' + (12 - this.labelsm);
40812                 }
40813                 if(this.labelxs > 0){
40814                     label.cls += ' col-xs-' + this.labelxs;
40815                     container.cls += ' col-xs-' + (12 - this.labelxs);
40816                 }
40817             }
40818         }
40819
40820         cfg.cn = [
40821             label,
40822             container,
40823             hiddenInput
40824         ];
40825         
40826         var settings = this;
40827
40828         ['xs','sm','md','lg'].map(function(size){
40829             if (settings[size]) {
40830                 cfg.cls += ' col-' + size + '-' + settings[size];
40831             }
40832         });
40833         
40834         return cfg;
40835     },
40836     
40837     initEvents : function()
40838     {
40839         this.indicator = this.indicatorEl();
40840         
40841         this.initCurrencyEvent();
40842         
40843         this.initNumberEvent();
40844     },
40845     
40846     initCurrencyEvent : function()
40847     {
40848         if (!this.store) {
40849             throw "can not find store for combo";
40850         }
40851         
40852         this.store = Roo.factory(this.store, Roo.data);
40853         this.store.parent = this;
40854         
40855         this.createList();
40856         
40857         this.triggerEl = this.el.select('.input-group-addon', true).first();
40858         
40859         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40860         
40861         var _this = this;
40862         
40863         (function(){
40864             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40865             _this.list.setWidth(lw);
40866         }).defer(100);
40867         
40868         this.list.on('mouseover', this.onViewOver, this);
40869         this.list.on('mousemove', this.onViewMove, this);
40870         this.list.on('scroll', this.onViewScroll, this);
40871         
40872         if(!this.tpl){
40873             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40874         }
40875         
40876         this.view = new Roo.View(this.list, this.tpl, {
40877             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40878         });
40879         
40880         this.view.on('click', this.onViewClick, this);
40881         
40882         this.store.on('beforeload', this.onBeforeLoad, this);
40883         this.store.on('load', this.onLoad, this);
40884         this.store.on('loadexception', this.onLoadException, this);
40885         
40886         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40887             "up" : function(e){
40888                 this.inKeyMode = true;
40889                 this.selectPrev();
40890             },
40891
40892             "down" : function(e){
40893                 if(!this.isExpanded()){
40894                     this.onTriggerClick();
40895                 }else{
40896                     this.inKeyMode = true;
40897                     this.selectNext();
40898                 }
40899             },
40900
40901             "enter" : function(e){
40902                 this.collapse();
40903                 
40904                 if(this.fireEvent("specialkey", this, e)){
40905                     this.onViewClick(false);
40906                 }
40907                 
40908                 return true;
40909             },
40910
40911             "esc" : function(e){
40912                 this.collapse();
40913             },
40914
40915             "tab" : function(e){
40916                 this.collapse();
40917                 
40918                 if(this.fireEvent("specialkey", this, e)){
40919                     this.onViewClick(false);
40920                 }
40921                 
40922                 return true;
40923             },
40924
40925             scope : this,
40926
40927             doRelay : function(foo, bar, hname){
40928                 if(hname == 'down' || this.scope.isExpanded()){
40929                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40930                 }
40931                 return true;
40932             },
40933
40934             forceKeyDown: true
40935         });
40936         
40937         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40938         
40939     },
40940     
40941     initNumberEvent : function(e)
40942     {
40943         this.inputEl().on("keydown" , this.fireKey,  this);
40944         this.inputEl().on("focus", this.onFocus,  this);
40945         this.inputEl().on("blur", this.onBlur,  this);
40946         
40947         this.inputEl().relayEvent('keyup', this);
40948         
40949         if(this.indicator){
40950             this.indicator.addClass('invisible');
40951         }
40952  
40953         this.originalValue = this.getValue();
40954         
40955         if(this.validationEvent == 'keyup'){
40956             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40957             this.inputEl().on('keyup', this.filterValidation, this);
40958         }
40959         else if(this.validationEvent !== false){
40960             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40961         }
40962         
40963         if(this.selectOnFocus){
40964             this.on("focus", this.preFocus, this);
40965             
40966         }
40967         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40968             this.inputEl().on("keypress", this.filterKeys, this);
40969         } else {
40970             this.inputEl().relayEvent('keypress', this);
40971         }
40972         
40973         var allowed = "0123456789";
40974         
40975         if(this.allowDecimals){
40976             allowed += this.decimalSeparator;
40977         }
40978         
40979         if(this.allowNegative){
40980             allowed += "-";
40981         }
40982         
40983         if(this.thousandsDelimiter) {
40984             allowed += ",";
40985         }
40986         
40987         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40988         
40989         var keyPress = function(e){
40990             
40991             var k = e.getKey();
40992             
40993             var c = e.getCharCode();
40994             
40995             if(
40996                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40997                     allowed.indexOf(String.fromCharCode(c)) === -1
40998             ){
40999                 e.stopEvent();
41000                 return;
41001             }
41002             
41003             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41004                 return;
41005             }
41006             
41007             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41008                 e.stopEvent();
41009             }
41010         };
41011         
41012         this.inputEl().on("keypress", keyPress, this);
41013         
41014     },
41015     
41016     onTriggerClick : function(e)
41017     {   
41018         if(this.disabled){
41019             return;
41020         }
41021         
41022         this.page = 0;
41023         this.loadNext = false;
41024         
41025         if(this.isExpanded()){
41026             this.collapse();
41027             return;
41028         }
41029         
41030         this.hasFocus = true;
41031         
41032         if(this.triggerAction == 'all') {
41033             this.doQuery(this.allQuery, true);
41034             return;
41035         }
41036         
41037         this.doQuery(this.getRawValue());
41038     },
41039     
41040     getCurrency : function()
41041     {   
41042         var v = this.currencyEl().getValue();
41043         
41044         return v;
41045     },
41046     
41047     restrictHeight : function()
41048     {
41049         this.list.alignTo(this.currencyEl(), this.listAlign);
41050         this.list.alignTo(this.currencyEl(), this.listAlign);
41051     },
41052     
41053     onViewClick : function(view, doFocus, el, e)
41054     {
41055         var index = this.view.getSelectedIndexes()[0];
41056         
41057         var r = this.store.getAt(index);
41058         
41059         if(r){
41060             this.onSelect(r, index);
41061         }
41062     },
41063     
41064     onSelect : function(record, index){
41065         
41066         if(this.fireEvent('beforeselect', this, record, index) !== false){
41067         
41068             this.setFromCurrencyData(index > -1 ? record.data : false);
41069             
41070             this.collapse();
41071             
41072             this.fireEvent('select', this, record, index);
41073         }
41074     },
41075     
41076     setFromCurrencyData : function(o)
41077     {
41078         var currency = '';
41079         
41080         this.lastCurrency = o;
41081         
41082         if (this.currencyField) {
41083             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41084         } else {
41085             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41086         }
41087         
41088         this.lastSelectionText = currency;
41089         
41090         //setting default currency
41091         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41092             this.setCurrency(this.defaultCurrency);
41093             return;
41094         }
41095         
41096         this.setCurrency(currency);
41097     },
41098     
41099     setFromData : function(o)
41100     {
41101         var c = {};
41102         
41103         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41104         
41105         this.setFromCurrencyData(c);
41106         
41107         var value = '';
41108         
41109         if (this.name) {
41110             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41111         } else {
41112             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41113         }
41114         
41115         this.setValue(value);
41116         
41117     },
41118     
41119     setCurrency : function(v)
41120     {   
41121         this.currencyValue = v;
41122         
41123         if(this.rendered){
41124             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41125             this.validate();
41126         }
41127     },
41128     
41129     setValue : function(v)
41130     {
41131         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41132         
41133         this.value = v;
41134         
41135         if(this.rendered){
41136             
41137             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41138             
41139             this.inputEl().dom.value = (v == '') ? '' :
41140                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41141             
41142             if(!this.allowZero && v === '0') {
41143                 this.hiddenEl().dom.value = '';
41144                 this.inputEl().dom.value = '';
41145             }
41146             
41147             this.validate();
41148         }
41149     },
41150     
41151     getRawValue : function()
41152     {
41153         var v = this.inputEl().getValue();
41154         
41155         return v;
41156     },
41157     
41158     getValue : function()
41159     {
41160         return this.fixPrecision(this.parseValue(this.getRawValue()));
41161     },
41162     
41163     parseValue : function(value)
41164     {
41165         if(this.thousandsDelimiter) {
41166             value += "";
41167             r = new RegExp(",", "g");
41168             value = value.replace(r, "");
41169         }
41170         
41171         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41172         return isNaN(value) ? '' : value;
41173         
41174     },
41175     
41176     fixPrecision : function(value)
41177     {
41178         if(this.thousandsDelimiter) {
41179             value += "";
41180             r = new RegExp(",", "g");
41181             value = value.replace(r, "");
41182         }
41183         
41184         var nan = isNaN(value);
41185         
41186         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41187             return nan ? '' : value;
41188         }
41189         return parseFloat(value).toFixed(this.decimalPrecision);
41190     },
41191     
41192     decimalPrecisionFcn : function(v)
41193     {
41194         return Math.floor(v);
41195     },
41196     
41197     validateValue : function(value)
41198     {
41199         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41200             return false;
41201         }
41202         
41203         var num = this.parseValue(value);
41204         
41205         if(isNaN(num)){
41206             this.markInvalid(String.format(this.nanText, value));
41207             return false;
41208         }
41209         
41210         if(num < this.minValue){
41211             this.markInvalid(String.format(this.minText, this.minValue));
41212             return false;
41213         }
41214         
41215         if(num > this.maxValue){
41216             this.markInvalid(String.format(this.maxText, this.maxValue));
41217             return false;
41218         }
41219         
41220         return true;
41221     },
41222     
41223     validate : function()
41224     {
41225         if(this.disabled || this.allowBlank){
41226             this.markValid();
41227             return true;
41228         }
41229         
41230         var currency = this.getCurrency();
41231         
41232         if(this.validateValue(this.getRawValue()) && currency.length){
41233             this.markValid();
41234             return true;
41235         }
41236         
41237         this.markInvalid();
41238         return false;
41239     },
41240     
41241     getName: function()
41242     {
41243         return this.name;
41244     },
41245     
41246     beforeBlur : function()
41247     {
41248         if(!this.castInt){
41249             return;
41250         }
41251         
41252         var v = this.parseValue(this.getRawValue());
41253         
41254         if(v || v == 0){
41255             this.setValue(v);
41256         }
41257     },
41258     
41259     onBlur : function()
41260     {
41261         this.beforeBlur();
41262         
41263         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41264             //this.el.removeClass(this.focusClass);
41265         }
41266         
41267         this.hasFocus = false;
41268         
41269         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41270             this.validate();
41271         }
41272         
41273         var v = this.getValue();
41274         
41275         if(String(v) !== String(this.startValue)){
41276             this.fireEvent('change', this, v, this.startValue);
41277         }
41278         
41279         this.fireEvent("blur", this);
41280     },
41281     
41282     inputEl : function()
41283     {
41284         return this.el.select('.roo-money-amount-input', true).first();
41285     },
41286     
41287     currencyEl : function()
41288     {
41289         return this.el.select('.roo-money-currency-input', true).first();
41290     },
41291     
41292     hiddenEl : function()
41293     {
41294         return this.el.select('input.hidden-number-input',true).first();
41295     }
41296     
41297 });