deff70db2147d881333d9da2c9a793c4d2dca5f2
[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, function(s) {
10                     if ( s.href  && s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })(); /*
16  * - LGPL
17  *
18  * base class for bootstrap elements.
19  * 
20  */
21
22 Roo.bootstrap = Roo.bootstrap || {};
23 /**
24  * @class Roo.bootstrap.Component
25  * @extends Roo.Component
26  * Bootstrap Component base class
27  * @cfg {String} cls css class
28  * @cfg {String} style any extra css
29  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
31  * @cfg {string} dataId cutomer id
32  * @cfg {string} name Specifies name attribute
33  * @cfg {string} tooltip  Text for the tooltip
34  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
35  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
36  
37  * @constructor
38  * Do not use directly - it does not do anything..
39  * @param {Object} config The config object
40  */
41
42
43
44 Roo.bootstrap.Component = function(config){
45     Roo.bootstrap.Component.superclass.constructor.call(this, config);
46        
47     this.addEvents({
48         /**
49          * @event childrenrendered
50          * Fires when the children have been rendered..
51          * @param {Roo.bootstrap.Component} this
52          */
53         "childrenrendered" : true
54         
55         
56         
57     });
58     
59     
60 };
61
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
63     
64     
65     allowDomMove : false, // to stop relocations in parent onRender...
66     
67     cls : false,
68     
69     style : false,
70     
71     autoCreate : false,
72     
73     tooltip : null,
74     /**
75      * Initialize Events for the element
76      */
77     initEvents : function() { },
78     
79     xattr : false,
80     
81     parentId : false,
82     
83     can_build_overlaid : true,
84     
85     container_method : false,
86     
87     dataId : false,
88     
89     name : false,
90     
91     parent: function() {
92         // returns the parent component..
93         return Roo.ComponentMgr.get(this.parentId)
94         
95         
96     },
97     
98     // private
99     onRender : function(ct, position)
100     {
101        // Roo.log("Call onRender: " + this.xtype);
102         
103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
104         
105         if(this.el){
106             if (this.el.attr('xtype')) {
107                 this.el.attr('xtypex', this.el.attr('xtype'));
108                 this.el.dom.removeAttribute('xtype');
109                 
110                 this.initEvents();
111             }
112             
113             return;
114         }
115         
116          
117         
118         var cfg = Roo.apply({},  this.getAutoCreate());
119         
120         cfg.id = this.id || Roo.id();
121         
122         // fill in the extra attributes 
123         if (this.xattr && typeof(this.xattr) =='object') {
124             for (var i in this.xattr) {
125                 cfg[i] = this.xattr[i];
126             }
127         }
128         
129         if(this.dataId){
130             cfg.dataId = this.dataId;
131         }
132         
133         if (this.cls) {
134             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
135         }
136         
137         if (this.style) { // fixme needs to support more complex style data.
138             cfg.style = this.style;
139         }
140         
141         if(this.name){
142             cfg.name = this.name;
143         }
144         
145         this.el = ct.createChild(cfg, position);
146         
147         if (this.tooltip) {
148             this.tooltipEl().attr('tooltip', this.tooltip);
149         }
150         
151         if(this.tabIndex !== undefined){
152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
153         }
154         
155         this.initEvents();
156         
157     },
158     /**
159      * Fetch the element to add children to
160      * @return {Roo.Element} defaults to this.el
161      */
162     getChildContainer : function()
163     {
164         return this.el;
165     },
166     /**
167      * Fetch the element to display the tooltip on.
168      * @return {Roo.Element} defaults to this.el
169      */
170     tooltipEl : function()
171     {
172         return this.el;
173     },
174         
175     addxtype  : function(tree,cntr)
176     {
177         var cn = this;
178         
179         cn = Roo.factory(tree);
180         //Roo.log(['addxtype', cn]);
181            
182         cn.parentType = this.xtype; //??
183         cn.parentId = this.id;
184         
185         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186         if (typeof(cn.container_method) == 'string') {
187             cntr = cn.container_method;
188         }
189         
190         
191         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
192         
193         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
194         
195         var build_from_html =  Roo.XComponent.build_from_html;
196           
197         var is_body  = (tree.xtype == 'Body') ;
198           
199         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
200           
201         var self_cntr_el = Roo.get(this[cntr](false));
202         
203         // do not try and build conditional elements 
204         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
205             return false;
206         }
207         
208         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210                 return this.addxtypeChild(tree,cntr, is_body);
211             }
212             
213             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
214                 
215             if(echild){
216                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
217             }
218             
219             Roo.log('skipping render');
220             return cn;
221             
222         }
223         
224         var ret = false;
225         if (!build_from_html) {
226             return false;
227         }
228         
229         // this i think handles overlaying multiple children of the same type
230         // with the sam eelement.. - which might be buggy..
231         while (true) {
232             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
233             
234             if (!echild) {
235                 break;
236             }
237             
238             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
239                 break;
240             }
241             
242             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
243         }
244        
245         return ret;
246     },
247     
248     
249     addxtypeChild : function (tree, cntr, is_body)
250     {
251         Roo.debug && Roo.log('addxtypeChild:' + cntr);
252         var cn = this;
253         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
254         
255         
256         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257                     (typeof(tree['flexy:foreach']) != 'undefined');
258           
259     
260         
261         skip_children = false;
262         // render the element if it's not BODY.
263         if (!is_body) {
264             
265             // if parent was disabled, then do not try and create the children..
266             if(!this[cntr](true)){
267                 tree.items = [];
268                 return tree;
269             }
270            
271             cn = Roo.factory(tree);
272            
273             cn.parentType = this.xtype; //??
274             cn.parentId = this.id;
275             
276             var build_from_html =  Roo.XComponent.build_from_html;
277             
278             
279             // does the container contain child eleemnts with 'xtype' attributes.
280             // that match this xtype..
281             // note - when we render we create these as well..
282             // so we should check to see if body has xtype set.
283             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
284                
285                 var self_cntr_el = Roo.get(this[cntr](false));
286                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
287                 if (echild) { 
288                     //Roo.log(Roo.XComponent.build_from_html);
289                     //Roo.log("got echild:");
290                     //Roo.log(echild);
291                 }
292                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293                 // and are not displayed -this causes this to use up the wrong element when matching.
294                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
295                 
296                 
297                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
299                   
300                   
301                   
302                     cn.el = echild;
303                   //  Roo.log("GOT");
304                     //echild.dom.removeAttribute('xtype');
305                 } else {
306                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307                     Roo.debug && Roo.log(self_cntr_el);
308                     Roo.debug && Roo.log(echild);
309                     Roo.debug && Roo.log(cn);
310                 }
311             }
312            
313             
314            
315             // if object has flexy:if - then it may or may not be rendered.
316             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
317                 // skip a flexy if element.
318                 Roo.debug && Roo.log('skipping render');
319                 Roo.debug && Roo.log(tree);
320                 if (!cn.el) {
321                     Roo.debug && Roo.log('skipping all children');
322                     skip_children = true;
323                 }
324                 
325              } else {
326                  
327                 // actually if flexy:foreach is found, we really want to create 
328                 // multiple copies here...
329                 //Roo.log('render');
330                 //Roo.log(this[cntr]());
331                 // some elements do not have render methods.. like the layouts...
332                 /*
333                 if(this[cntr](true) === false){
334                     cn.items = [];
335                     return cn;
336                 }
337                 */
338                 cn.render && cn.render(this[cntr](true));
339                 
340              }
341             // then add the element..
342         }
343          
344         // handle the kids..
345         
346         var nitems = [];
347         /*
348         if (typeof (tree.menu) != 'undefined') {
349             tree.menu.parentType = cn.xtype;
350             tree.menu.triggerEl = cn.el;
351             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
352             
353         }
354         */
355         if (!tree.items || !tree.items.length) {
356             cn.items = nitems;
357             //Roo.log(["no children", this]);
358             
359             return cn;
360         }
361          
362         var items = tree.items;
363         delete tree.items;
364         
365         //Roo.log(items.length);
366             // add the items..
367         if (!skip_children) {    
368             for(var i =0;i < items.length;i++) {
369               //  Roo.log(['add child', items[i]]);
370                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
371             }
372         }
373         
374         cn.items = nitems;
375         
376         //Roo.log("fire childrenrendered");
377         
378         cn.fireEvent('childrenrendered', this);
379         
380         return cn;
381     },
382     
383     /**
384      * Set the element that will be used to show or hide
385      */
386     setVisibilityEl : function(el)
387     {
388         this.visibilityEl = el;
389     },
390     
391      /**
392      * Get the element that will be used to show or hide
393      */
394     getVisibilityEl : function()
395     {
396         if (typeof(this.visibilityEl) == 'object') {
397             return this.visibilityEl;
398         }
399         
400         if (typeof(this.visibilityEl) == 'string') {
401             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
402         }
403         
404         return this.getEl();
405     },
406     
407     /**
408      * Show a component - removes 'hidden' class
409      */
410     show : function()
411     {
412         if(!this.getVisibilityEl()){
413             return;
414         }
415          
416         this.getVisibilityEl().removeClass(['hidden','d-none']);
417         
418         this.fireEvent('show', this);
419         
420         
421     },
422     /**
423      * Hide a component - adds 'hidden' class
424      */
425     hide: function()
426     {
427         if(!this.getVisibilityEl()){
428             return;
429         }
430         
431         this.getVisibilityEl().addClass(['hidden','d-none']);
432         
433         this.fireEvent('hide', this);
434         
435     }
436 });
437
438  /*
439  * - LGPL
440  *
441  * Body
442  *
443  */
444
445 /**
446  * @class Roo.bootstrap.Body
447  * @extends Roo.bootstrap.Component
448  * Bootstrap Body class
449  *
450  * @constructor
451  * Create a new body
452  * @param {Object} config The config object
453  */
454
455 Roo.bootstrap.Body = function(config){
456
457     config = config || {};
458
459     Roo.bootstrap.Body.superclass.constructor.call(this, config);
460     this.el = Roo.get(config.el ? config.el : document.body );
461     if (this.cls && this.cls.length) {
462         Roo.get(document.body).addClass(this.cls);
463     }
464 };
465
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
467
468     is_body : true,// just to make sure it's constructed?
469
470         autoCreate : {
471         cls: 'container'
472     },
473     onRender : function(ct, position)
474     {
475        /* Roo.log("Roo.bootstrap.Body - onRender");
476         if (this.cls && this.cls.length) {
477             Roo.get(document.body).addClass(this.cls);
478         }
479         // style??? xttr???
480         */
481     }
482
483
484
485
486 });
487 /*
488  * - LGPL
489  *
490  * button group
491  * 
492  */
493
494
495 /**
496  * @class Roo.bootstrap.ButtonGroup
497  * @extends Roo.bootstrap.Component
498  * Bootstrap ButtonGroup class
499  * @cfg {String} size lg | sm | xs (default empty normal)
500  * @cfg {String} align vertical | justified  (default none)
501  * @cfg {String} direction up | down (default down)
502  * @cfg {Boolean} toolbar false | true
503  * @cfg {Boolean} btn true | false
504  * 
505  * 
506  * @constructor
507  * Create a new Input
508  * @param {Object} config The config object
509  */
510
511 Roo.bootstrap.ButtonGroup = function(config){
512     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
513 };
514
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
516     
517     size: '',
518     align: '',
519     direction: '',
520     toolbar: false,
521     btn: true,
522
523     getAutoCreate : function(){
524         var cfg = {
525             cls: 'btn-group',
526             html : null
527         };
528         
529         cfg.html = this.html || cfg.html;
530         
531         if (this.toolbar) {
532             cfg = {
533                 cls: 'btn-toolbar',
534                 html: null
535             };
536             
537             return cfg;
538         }
539         
540         if (['vertical','justified'].indexOf(this.align)!==-1) {
541             cfg.cls = 'btn-group-' + this.align;
542             
543             if (this.align == 'justified') {
544                 console.log(this.items);
545             }
546         }
547         
548         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549             cfg.cls += ' btn-group-' + this.size;
550         }
551         
552         if (this.direction == 'up') {
553             cfg.cls += ' dropup' ;
554         }
555         
556         return cfg;
557     },
558     /**
559      * Add a button to the group (similar to NavItem API.)
560      */
561     addItem : function(cfg)
562     {
563         var cn = new Roo.bootstrap.Button(cfg);
564         //this.register(cn);
565         cn.parentId = this.id;
566         cn.onRender(this.el, null);
567         return cn;
568     }
569    
570 });
571
572  /*
573  * - LGPL
574  *
575  * button
576  * 
577  */
578
579 /**
580  * @class Roo.bootstrap.Button
581  * @extends Roo.bootstrap.Component
582  * Bootstrap Button class
583  * @cfg {String} html The button content
584  * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
585  * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
586  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
587  * @cfg {String} size ( lg | sm | xs)
588  * @cfg {String} tag ( a | input | submit)
589  * @cfg {String} href empty or href
590  * @cfg {Boolean} disabled default false;
591  * @cfg {Boolean} isClose default false;
592  * @cfg {String} glyphicon depricated - use fa
593  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
594  * @cfg {String} badge text for badge
595  * @cfg {String} theme (default|glow)  
596  * @cfg {Boolean} inverse dark themed version
597  * @cfg {Boolean} toggle is it a slidy toggle button
598  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
599  * @cfg {String} ontext text for on slidy toggle state
600  * @cfg {String} offtext text for off slidy toggle state
601  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
602  * @cfg {Boolean} removeClass remove the standard class..
603  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
604  * 
605  * @constructor
606  * Create a new button
607  * @param {Object} config The config object
608  */
609
610
611 Roo.bootstrap.Button = function(config){
612     Roo.bootstrap.Button.superclass.constructor.call(this, config);
613     this.weightClass = ["btn-default btn-outline-secondary", 
614                        "btn-primary", 
615                        "btn-success", 
616                        "btn-info", 
617                        "btn-warning",
618                        "btn-danger",
619                        "btn-link"
620                       ],  
621     this.addEvents({
622         // raw events
623         /**
624          * @event click
625          * When a butotn is pressed
626          * @param {Roo.bootstrap.Button} btn
627          * @param {Roo.EventObject} e
628          */
629         "click" : true,
630          /**
631          * @event toggle
632          * After the button has been toggles
633          * @param {Roo.bootstrap.Button} btn
634          * @param {Roo.EventObject} e
635          * @param {boolean} pressed (also available as button.pressed)
636          */
637         "toggle" : true
638     });
639 };
640
641 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
642     html: false,
643     active: false,
644     weight: '',
645     badge_weight: '',
646     outline : false,
647     size: '',
648     tag: 'button',
649     href: '',
650     disabled: false,
651     isClose: false,
652     glyphicon: '',
653     fa: '',
654     badge: '',
655     theme: 'default',
656     inverse: false,
657     
658     toggle: false,
659     ontext: 'ON',
660     offtext: 'OFF',
661     defaulton: true,
662     preventDefault: true,
663     removeClass: false,
664     name: false,
665     target: false,
666      
667     pressed : null,
668      
669     
670     getAutoCreate : function(){
671         
672         var cfg = {
673             tag : 'button',
674             cls : 'roo-button',
675             html: ''
676         };
677         
678         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
679             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
680             this.tag = 'button';
681         } else {
682             cfg.tag = this.tag;
683         }
684         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
685         
686         if (this.toggle == true) {
687             cfg={
688                 tag: 'div',
689                 cls: 'slider-frame roo-button',
690                 cn: [
691                     {
692                         tag: 'span',
693                         'data-on-text':'ON',
694                         'data-off-text':'OFF',
695                         cls: 'slider-button',
696                         html: this.offtext
697                     }
698                 ]
699             };
700             
701             if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
702                 cfg.cls += ' '+this.weight;
703             }
704             
705             return cfg;
706         }
707         
708         if (this.isClose) {
709             cfg.cls += ' close';
710             
711             cfg["aria-hidden"] = true;
712             
713             cfg.html = "&times;";
714             
715             return cfg;
716         }
717         
718          
719         if (this.theme==='default') {
720             cfg.cls = 'btn roo-button';
721             
722             //if (this.parentType != 'Navbar') {
723             this.weight = this.weight.length ?  this.weight : 'default';
724             //}
725             if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
726                 
727                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
728                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
729                 cfg.cls += ' btn-' + outline + weight;
730                 if (this.weight == 'default') {
731                     // BC
732                     cfg.cls += ' btn-' + this.weight;
733                 }
734             }
735         } else if (this.theme==='glow') {
736             
737             cfg.tag = 'a';
738             cfg.cls = 'btn-glow roo-button';
739             
740             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
741                 
742                 cfg.cls += ' ' + this.weight;
743             }
744         }
745    
746         
747         if (this.inverse) {
748             this.cls += ' inverse';
749         }
750         
751         
752         if (this.active || this.pressed === true) {
753             cfg.cls += ' active';
754         }
755         
756         if (this.disabled) {
757             cfg.disabled = 'disabled';
758         }
759         
760         if (this.items) {
761             Roo.log('changing to ul' );
762             cfg.tag = 'ul';
763             this.glyphicon = 'caret';
764             if (Roo.bootstrap.version == 4) {
765                 this.fa = 'caret-down';
766             }
767             
768         }
769         
770         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
771          
772         //gsRoo.log(this.parentType);
773         if (this.parentType === 'Navbar' && !this.parent().bar) {
774             Roo.log('changing to li?');
775             
776             cfg.tag = 'li';
777             
778             cfg.cls = '';
779             cfg.cn =  [{
780                 tag : 'a',
781                 cls : 'roo-button',
782                 html : this.html,
783                 href : this.href || '#'
784             }];
785             if (this.menu) {
786                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
787                 cfg.cls += ' dropdown';
788             }   
789             
790             delete cfg.html;
791             
792         }
793         
794        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
795         
796         if (this.glyphicon) {
797             cfg.html = ' ' + cfg.html;
798             
799             cfg.cn = [
800                 {
801                     tag: 'span',
802                     cls: 'glyphicon glyphicon-' + this.glyphicon
803                 }
804             ];
805         }
806         if (this.fa) {
807             cfg.html = ' ' + cfg.html;
808             
809             cfg.cn = [
810                 {
811                     tag: 'i',
812                     cls: 'fa fas fa-' + this.fa
813                 }
814             ];
815         }
816         
817         if (this.badge) {
818             cfg.html += ' ';
819             
820             cfg.tag = 'a';
821             
822 //            cfg.cls='btn roo-button';
823             
824             cfg.href=this.href;
825             
826             var value = cfg.html;
827             
828             if(this.glyphicon){
829                 value = {
830                     tag: 'span',
831                     cls: 'glyphicon glyphicon-' + this.glyphicon,
832                     html: this.html
833                 };
834             }
835             if(this.fa){
836                 value = {
837                     tag: 'i',
838                     cls: 'fa fas fa-' + this.fa,
839                     html: this.html
840                 };
841             }
842             
843             var bw = this.badge_weight.length ? this.badge_weight :
844                 (this.weight.length ? this.weight : 'secondary');
845             bw = bw == 'default' ? 'secondary' : bw;
846             
847             cfg.cn = [
848                 value,
849                 {
850                     tag: 'span',
851                     cls: 'badge badge-' + bw,
852                     html: this.badge
853                 }
854             ];
855             
856             cfg.html='';
857         }
858         
859         if (this.menu) {
860             cfg.cls += ' dropdown';
861             cfg.html = typeof(cfg.html) != 'undefined' ?
862                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
863         }
864         
865         if (cfg.tag !== 'a' && this.href !== '') {
866             throw "Tag must be a to set href.";
867         } else if (this.href.length > 0) {
868             cfg.href = this.href;
869         }
870         
871         if(this.removeClass){
872             cfg.cls = '';
873         }
874         
875         if(this.target){
876             cfg.target = this.target;
877         }
878         
879         return cfg;
880     },
881     initEvents: function() {
882        // Roo.log('init events?');
883 //        Roo.log(this.el.dom);
884         // add the menu...
885         
886         if (typeof (this.menu) != 'undefined') {
887             this.menu.parentType = this.xtype;
888             this.menu.triggerEl = this.el;
889             this.addxtype(Roo.apply({}, this.menu));
890         }
891
892
893        if (this.el.hasClass('roo-button')) {
894             this.el.on('click', this.onClick, this);
895        } else {
896             this.el.select('.roo-button').on('click', this.onClick, this);
897        }
898        
899        if(this.removeClass){
900            this.el.on('click', this.onClick, this);
901        }
902        
903        this.el.enableDisplayMode();
904         
905     },
906     onClick : function(e)
907     {
908         if (this.disabled) {
909             return;
910         }
911         
912         Roo.log('button on click ');
913         if(this.preventDefault){
914             e.preventDefault();
915         }
916         
917         if (this.pressed === true || this.pressed === false) {
918             this.toggleActive(e);
919         }
920         
921         
922         this.fireEvent('click', this, e);
923     },
924     
925     /**
926      * Enables this button
927      */
928     enable : function()
929     {
930         this.disabled = false;
931         this.el.removeClass('disabled');
932     },
933     
934     /**
935      * Disable this button
936      */
937     disable : function()
938     {
939         this.disabled = true;
940         this.el.addClass('disabled');
941     },
942      /**
943      * sets the active state on/off, 
944      * @param {Boolean} state (optional) Force a particular state
945      */
946     setActive : function(v) {
947         
948         this.el[v ? 'addClass' : 'removeClass']('active');
949         this.pressed = v;
950     },
951      /**
952      * toggles the current active state 
953      */
954     toggleActive : function(e)
955     {
956         this.setActive(!this.pressed);
957         this.fireEvent('toggle', this, e, !this.pressed);
958     },
959      /**
960      * get the current active state
961      * @return {boolean} true if it's active
962      */
963     isActive : function()
964     {
965         return this.el.hasClass('active');
966     },
967     /**
968      * set the text of the first selected button
969      */
970     setText : function(str)
971     {
972         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
973     },
974     /**
975      * get the text of the first selected button
976      */
977     getText : function()
978     {
979         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
980     },
981     
982     setWeight : function(str)
983     {
984         this.el.removeClass(this.weightClass);
985         this.weight = str;
986         var outline = this.outline ? 'outline-' : '';
987         if (str == 'default') {
988             this.el.addClass('btn-default btn-outline-secondary');        
989             return;
990         }
991         this.el.addClass('btn-' + outline + str);        
992     }
993     
994     
995 });
996
997  /*
998  * - LGPL
999  *
1000  * column
1001  * 
1002  */
1003
1004 /**
1005  * @class Roo.bootstrap.Column
1006  * @extends Roo.bootstrap.Component
1007  * Bootstrap Column class
1008  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1009  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1010  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1011  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1012  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1013  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1014  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1015  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1016  *
1017  * 
1018  * @cfg {Boolean} hidden (true|false) hide the element
1019  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1020  * @cfg {String} fa (ban|check|...) font awesome icon
1021  * @cfg {Number} fasize (1|2|....) font awsome size
1022
1023  * @cfg {String} icon (info-sign|check|...) glyphicon name
1024
1025  * @cfg {String} html content of column.
1026  * 
1027  * @constructor
1028  * Create a new Column
1029  * @param {Object} config The config object
1030  */
1031
1032 Roo.bootstrap.Column = function(config){
1033     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1034 };
1035
1036 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1037     
1038     xs: false,
1039     sm: false,
1040     md: false,
1041     lg: false,
1042     xsoff: false,
1043     smoff: false,
1044     mdoff: false,
1045     lgoff: false,
1046     html: '',
1047     offset: 0,
1048     alert: false,
1049     fa: false,
1050     icon : false,
1051     hidden : false,
1052     fasize : 1,
1053     
1054     getAutoCreate : function(){
1055         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1056         
1057         cfg = {
1058             tag: 'div',
1059             cls: 'column'
1060         };
1061         
1062         var settings=this;
1063         ['xs','sm','md','lg'].map(function(size){
1064             //Roo.log( size + ':' + settings[size]);
1065             
1066             if (settings[size+'off'] !== false) {
1067                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1068             }
1069             
1070             if (settings[size] === false) {
1071                 return;
1072             }
1073             
1074             if (!settings[size]) { // 0 = hidden
1075                 cfg.cls += ' hidden-' + size + ' hidden' + size + '-down';;
1076                 return;
1077             }
1078             cfg.cls += ' col-' + size + '-' + settings[size] + (
1079                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1080             );
1081             
1082         });
1083         
1084         if (this.hidden) {
1085             cfg.cls += ' hidden';
1086         }
1087         
1088         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1089             cfg.cls +=' alert alert-' + this.alert;
1090         }
1091         
1092         
1093         if (this.html.length) {
1094             cfg.html = this.html;
1095         }
1096         if (this.fa) {
1097             var fasize = '';
1098             if (this.fasize > 1) {
1099                 fasize = ' fa-' + this.fasize + 'x';
1100             }
1101             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1102             
1103             
1104         }
1105         if (this.icon) {
1106             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1107         }
1108         
1109         return cfg;
1110     }
1111    
1112 });
1113
1114  
1115
1116  /*
1117  * - LGPL
1118  *
1119  * page container.
1120  * 
1121  */
1122
1123
1124 /**
1125  * @class Roo.bootstrap.Container
1126  * @extends Roo.bootstrap.Component
1127  * Bootstrap Container class
1128  * @cfg {Boolean} jumbotron is it a jumbotron element
1129  * @cfg {String} html content of element
1130  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1131  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1132  * @cfg {String} header content of header (for panel)
1133  * @cfg {String} footer content of footer (for panel)
1134  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1135  * @cfg {String} tag (header|aside|section) type of HTML tag.
1136  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1137  * @cfg {String} fa font awesome icon
1138  * @cfg {String} icon (info-sign|check|...) glyphicon name
1139  * @cfg {Boolean} hidden (true|false) hide the element
1140  * @cfg {Boolean} expandable (true|false) default false
1141  * @cfg {Boolean} expanded (true|false) default true
1142  * @cfg {String} rheader contet on the right of header
1143  * @cfg {Boolean} clickable (true|false) default false
1144
1145  *     
1146  * @constructor
1147  * Create a new Container
1148  * @param {Object} config The config object
1149  */
1150
1151 Roo.bootstrap.Container = function(config){
1152     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1153     
1154     this.addEvents({
1155         // raw events
1156          /**
1157          * @event expand
1158          * After the panel has been expand
1159          * 
1160          * @param {Roo.bootstrap.Container} this
1161          */
1162         "expand" : true,
1163         /**
1164          * @event collapse
1165          * After the panel has been collapsed
1166          * 
1167          * @param {Roo.bootstrap.Container} this
1168          */
1169         "collapse" : true,
1170         /**
1171          * @event click
1172          * When a element is chick
1173          * @param {Roo.bootstrap.Container} this
1174          * @param {Roo.EventObject} e
1175          */
1176         "click" : true
1177     });
1178 };
1179
1180 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1181     
1182     jumbotron : false,
1183     well: '',
1184     panel : '',
1185     header: '',
1186     footer : '',
1187     sticky: '',
1188     tag : false,
1189     alert : false,
1190     fa: false,
1191     icon : false,
1192     expandable : false,
1193     rheader : '',
1194     expanded : true,
1195     clickable: false,
1196   
1197      
1198     getChildContainer : function() {
1199         
1200         if(!this.el){
1201             return false;
1202         }
1203         
1204         if (this.panel.length) {
1205             return this.el.select('.panel-body',true).first();
1206         }
1207         
1208         return this.el;
1209     },
1210     
1211     
1212     getAutoCreate : function(){
1213         
1214         var cfg = {
1215             tag : this.tag || 'div',
1216             html : '',
1217             cls : ''
1218         };
1219         if (this.jumbotron) {
1220             cfg.cls = 'jumbotron';
1221         }
1222         
1223         
1224         
1225         // - this is applied by the parent..
1226         //if (this.cls) {
1227         //    cfg.cls = this.cls + '';
1228         //}
1229         
1230         if (this.sticky.length) {
1231             
1232             var bd = Roo.get(document.body);
1233             if (!bd.hasClass('bootstrap-sticky')) {
1234                 bd.addClass('bootstrap-sticky');
1235                 Roo.select('html',true).setStyle('height', '100%');
1236             }
1237              
1238             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1239         }
1240         
1241         
1242         if (this.well.length) {
1243             switch (this.well) {
1244                 case 'lg':
1245                 case 'sm':
1246                     cfg.cls +=' well well-' +this.well;
1247                     break;
1248                 default:
1249                     cfg.cls +=' well';
1250                     break;
1251             }
1252         }
1253         
1254         if (this.hidden) {
1255             cfg.cls += ' hidden';
1256         }
1257         
1258         
1259         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1260             cfg.cls +=' alert alert-' + this.alert;
1261         }
1262         
1263         var body = cfg;
1264         
1265         if (this.panel.length) {
1266             cfg.cls += ' panel panel-' + this.panel;
1267             cfg.cn = [];
1268             if (this.header.length) {
1269                 
1270                 var h = [];
1271                 
1272                 if(this.expandable){
1273                     
1274                     cfg.cls = cfg.cls + ' expandable';
1275                     
1276                     h.push({
1277                         tag: 'i',
1278                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1279                     });
1280                     
1281                 }
1282                 
1283                 h.push(
1284                     {
1285                         tag: 'span',
1286                         cls : 'panel-title',
1287                         html : (this.expandable ? '&nbsp;' : '') + this.header
1288                     },
1289                     {
1290                         tag: 'span',
1291                         cls: 'panel-header-right',
1292                         html: this.rheader
1293                     }
1294                 );
1295                 
1296                 cfg.cn.push({
1297                     cls : 'panel-heading',
1298                     style : this.expandable ? 'cursor: pointer' : '',
1299                     cn : h
1300                 });
1301                 
1302             }
1303             
1304             body = false;
1305             cfg.cn.push({
1306                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1307                 html : this.html
1308             });
1309             
1310             
1311             if (this.footer.length) {
1312                 cfg.cn.push({
1313                     cls : 'panel-footer',
1314                     html : this.footer
1315                     
1316                 });
1317             }
1318             
1319         }
1320         
1321         if (body) {
1322             body.html = this.html || cfg.html;
1323             // prefix with the icons..
1324             if (this.fa) {
1325                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1326             }
1327             if (this.icon) {
1328                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1329             }
1330             
1331             
1332         }
1333         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1334             cfg.cls =  'container';
1335         }
1336         
1337         return cfg;
1338     },
1339     
1340     initEvents: function() 
1341     {
1342         if(this.expandable){
1343             var headerEl = this.headerEl();
1344         
1345             if(headerEl){
1346                 headerEl.on('click', this.onToggleClick, this);
1347             }
1348         }
1349         
1350         if(this.clickable){
1351             this.el.on('click', this.onClick, this);
1352         }
1353         
1354     },
1355     
1356     onToggleClick : function()
1357     {
1358         var headerEl = this.headerEl();
1359         
1360         if(!headerEl){
1361             return;
1362         }
1363         
1364         if(this.expanded){
1365             this.collapse();
1366             return;
1367         }
1368         
1369         this.expand();
1370     },
1371     
1372     expand : function()
1373     {
1374         if(this.fireEvent('expand', this)) {
1375             
1376             this.expanded = true;
1377             
1378             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1379             
1380             this.el.select('.panel-body',true).first().removeClass('hide');
1381             
1382             var toggleEl = this.toggleEl();
1383
1384             if(!toggleEl){
1385                 return;
1386             }
1387
1388             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1389         }
1390         
1391     },
1392     
1393     collapse : function()
1394     {
1395         if(this.fireEvent('collapse', this)) {
1396             
1397             this.expanded = false;
1398             
1399             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1400             this.el.select('.panel-body',true).first().addClass('hide');
1401         
1402             var toggleEl = this.toggleEl();
1403
1404             if(!toggleEl){
1405                 return;
1406             }
1407
1408             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1409         }
1410     },
1411     
1412     toggleEl : function()
1413     {
1414         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1415             return;
1416         }
1417         
1418         return this.el.select('.panel-heading .fa',true).first();
1419     },
1420     
1421     headerEl : function()
1422     {
1423         if(!this.el || !this.panel.length || !this.header.length){
1424             return;
1425         }
1426         
1427         return this.el.select('.panel-heading',true).first()
1428     },
1429     
1430     bodyEl : function()
1431     {
1432         if(!this.el || !this.panel.length){
1433             return;
1434         }
1435         
1436         return this.el.select('.panel-body',true).first()
1437     },
1438     
1439     titleEl : function()
1440     {
1441         if(!this.el || !this.panel.length || !this.header.length){
1442             return;
1443         }
1444         
1445         return this.el.select('.panel-title',true).first();
1446     },
1447     
1448     setTitle : function(v)
1449     {
1450         var titleEl = this.titleEl();
1451         
1452         if(!titleEl){
1453             return;
1454         }
1455         
1456         titleEl.dom.innerHTML = v;
1457     },
1458     
1459     getTitle : function()
1460     {
1461         
1462         var titleEl = this.titleEl();
1463         
1464         if(!titleEl){
1465             return '';
1466         }
1467         
1468         return titleEl.dom.innerHTML;
1469     },
1470     
1471     setRightTitle : function(v)
1472     {
1473         var t = this.el.select('.panel-header-right',true).first();
1474         
1475         if(!t){
1476             return;
1477         }
1478         
1479         t.dom.innerHTML = v;
1480     },
1481     
1482     onClick : function(e)
1483     {
1484         e.preventDefault();
1485         
1486         this.fireEvent('click', this, e);
1487     }
1488 });
1489
1490  /*
1491  *  - LGPL
1492  *
1493  *  This is BS4's Card element.. - similar to our containers probably..
1494  * 
1495  */
1496 /**
1497  * @class Roo.bootstrap.Card
1498  * @extends Roo.bootstrap.Component
1499  * Bootstrap Card class
1500  *
1501  *
1502  * possible... may not be implemented..
1503  * @cfg {String} header_image  src url of image.
1504  * @cfg {String} header
1505  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1506  * 
1507  * @cfg {String} title
1508  * @cfg {String} subtitle
1509  * @cfg {String} html -- html contents - or just use children..
1510  * @cfg {String} footer
1511  
1512  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1513  * 
1514  * @cfg {String} margin (0|1|2|3|4|5|auto)
1515  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1516  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1517  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1518  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1519  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1520  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1521  *
1522  * @cfg {String} padding (0|1|2|3|4|5)
1523  * @cfg {String} padding_top (0|1|2|3|4|5)
1524  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1525  * @cfg {String} padding_left (0|1|2|3|4|5)
1526  * @cfg {String} padding_right (0|1|2|3|4|5)
1527  * @cfg {String} padding_x (0|1|2|3|4|5)
1528  * @cfg {String} padding_y (0|1|2|3|4|5)
1529  *
1530  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1531  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1532  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1533  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1534  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1535  
1536  * @constructor
1537  * Create a new Container
1538  * @param {Object} config The config object
1539  */
1540
1541 Roo.bootstrap.Card = function(config){
1542     Roo.bootstrap.Card.superclass.constructor.call(this, config);
1543     
1544     this.addEvents({
1545         
1546     });
1547 };
1548
1549
1550 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
1551     
1552     
1553     weight : '',
1554     
1555     margin: '', /// may be better in component?
1556     margin_top: '', 
1557     margin_bottom: '', 
1558     margin_left: '',
1559     margin_right: '',
1560     margin_x: '',
1561     margin_y: '',
1562     
1563     padding : '',
1564     padding_top: '', 
1565     padding_bottom: '', 
1566     padding_left: '',
1567     padding_right: '',
1568     padding_x: '',
1569     padding_y: '',
1570     
1571     display: '', 
1572     display_xs: '', 
1573     display_sm: '', 
1574     display_lg: '',
1575     display_xl: '',
1576  
1577     header_image  : '',
1578     header : '',
1579     header_size : 0,
1580     title : '',
1581     subtitle : '',
1582     html : '',
1583     
1584     
1585     childContainer : false,
1586
1587     layoutCls : function()
1588     {
1589         var cls = '';
1590         var t = this;
1591         
1592         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
1593             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
1594             
1595             if (t['margin' + (v.length ? '_' : '') + v].length) {
1596                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
1597             }
1598             if (t['padding' + (v.length ? '_' : '') + v].length) {
1599                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
1600             }
1601         });
1602         
1603         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
1604             if (t['display' + (v.length ? '_' : '') + v].length) {
1605                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['margin' + (v.length ? '_' : '') + v].length
1606             }
1607         });
1608         
1609         // more generic support?
1610         if (this.hidden) {
1611             cls += ' d-none';
1612         }
1613         
1614         return cls;
1615     },
1616  
1617        // Roo.log("Call onRender: " + this.xtype);
1618         /*  We are looking at something like this.
1619 <div class="card">
1620     <img src="..." class="card-img-top" alt="...">
1621     <div class="card-body">
1622         <h5 class="card-title">Card title</h5>
1623          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
1624
1625         >> this bit is really the body...
1626         <div> << we will ad dthis in hopefully it will not break shit.
1627         
1628         ** card text does not actually have any styling...
1629         
1630             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
1631         
1632         </div> <<
1633           <a href="#" class="card-link">Card link</a>
1634           
1635     </div>
1636     <div class="card-footer">
1637         <small class="text-muted">Last updated 3 mins ago</small>
1638     </div>
1639 </div>
1640          */
1641     getAutoCreate : function(){
1642         
1643         var cfg = {
1644             tag : 'div',
1645             cls : 'card',
1646             cn : [ ]
1647         };
1648         
1649         if (this.weight.length && this.weight != 'light') {
1650             cfg.cls += ' text-white'
1651         }
1652         if (this.weight.length) {
1653             cfg.cls += ' bg-' + this.weight;
1654         }
1655         
1656         cfg.cls += this.layoutCls(); 
1657         
1658         if (this.header.length) {
1659             cfg.cn.push({
1660                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
1661                 cls : 'card-header',
1662                 html : this.header // escape?
1663             });
1664         }
1665         if (this.header_image.length) {
1666             cfg.cn.push({
1667                 tag : 'img',
1668                 cls : 'card-img-top',
1669                 src: this.header_image // escape?
1670             });
1671         }
1672         
1673         var body = {
1674             tag : 'div',
1675             cls : 'card-body',
1676             cn : []
1677         };
1678         cfg.push(body);
1679         
1680         if (this.title.length) {
1681             body.cn.push({
1682                 tag : 'div',
1683                 cls : 'card-title',
1684                 src: this.title // escape?
1685             });
1686         }
1687         
1688         if (this.subtitle.length) {
1689             body.cn.push({
1690                 tag : 'div',
1691                 cls : 'card-title',
1692                 src: this.subtitle // escape?
1693             });
1694         }
1695         
1696         body.cn.push({
1697             tag : 'div',
1698             cls : 'roo-card-body-ctr'
1699         });
1700         
1701         // fixme ? handle objects?
1702         if (this.footer.length) {
1703             body.cn.push({
1704                 tag : 'div',
1705                 cls : 'card-footer',
1706                 html: this.footer // escape?
1707             });
1708         }
1709         // footer...
1710         
1711         return cfg;
1712     },
1713     
1714     
1715     getChildContainer : function()
1716     {
1717         
1718         if(!this.el){
1719             return false;
1720         }
1721         return this.el.select('.roo-card-body-ctr',true).first();    
1722     }
1723     
1724 }/*
1725  * - LGPL
1726  *
1727  * image
1728  * 
1729  */
1730
1731
1732 /**
1733  * @class Roo.bootstrap.Img
1734  * @extends Roo.bootstrap.Component
1735  * Bootstrap Img class
1736  * @cfg {Boolean} imgResponsive false | true
1737  * @cfg {String} border rounded | circle | thumbnail
1738  * @cfg {String} src image source
1739  * @cfg {String} alt image alternative text
1740  * @cfg {String} href a tag href
1741  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1742  * @cfg {String} xsUrl xs image source
1743  * @cfg {String} smUrl sm image source
1744  * @cfg {String} mdUrl md image source
1745  * @cfg {String} lgUrl lg image source
1746  * 
1747  * @constructor
1748  * Create a new Input
1749  * @param {Object} config The config object
1750  */
1751
1752 Roo.bootstrap.Img = function(config){
1753     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1754     
1755     this.addEvents({
1756         // img events
1757         /**
1758          * @event click
1759          * The img click event for the img.
1760          * @param {Roo.EventObject} e
1761          */
1762         "click" : true
1763     });
1764 };
1765
1766 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1767     
1768     imgResponsive: true,
1769     border: '',
1770     src: 'about:blank',
1771     href: false,
1772     target: false,
1773     xsUrl: '',
1774     smUrl: '',
1775     mdUrl: '',
1776     lgUrl: '',
1777
1778     getAutoCreate : function()
1779     {   
1780         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1781             return this.createSingleImg();
1782         }
1783         
1784         var cfg = {
1785             tag: 'div',
1786             cls: 'roo-image-responsive-group',
1787             cn: []
1788         };
1789         var _this = this;
1790         
1791         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1792             
1793             if(!_this[size + 'Url']){
1794                 return;
1795             }
1796             
1797             var img = {
1798                 tag: 'img',
1799                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1800                 html: _this.html || cfg.html,
1801                 src: _this[size + 'Url']
1802             };
1803             
1804             img.cls += ' roo-image-responsive-' + size;
1805             
1806             var s = ['xs', 'sm', 'md', 'lg'];
1807             
1808             s.splice(s.indexOf(size), 1);
1809             
1810             Roo.each(s, function(ss){
1811                 img.cls += ' hidden-' + ss;
1812             });
1813             
1814             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1815                 cfg.cls += ' img-' + _this.border;
1816             }
1817             
1818             if(_this.alt){
1819                 cfg.alt = _this.alt;
1820             }
1821             
1822             if(_this.href){
1823                 var a = {
1824                     tag: 'a',
1825                     href: _this.href,
1826                     cn: [
1827                         img
1828                     ]
1829                 };
1830
1831                 if(this.target){
1832                     a.target = _this.target;
1833                 }
1834             }
1835             
1836             cfg.cn.push((_this.href) ? a : img);
1837             
1838         });
1839         
1840         return cfg;
1841     },
1842     
1843     createSingleImg : function()
1844     {
1845         var cfg = {
1846             tag: 'img',
1847             cls: (this.imgResponsive) ? 'img-responsive' : '',
1848             html : null,
1849             src : 'about:blank'  // just incase src get's set to undefined?!?
1850         };
1851         
1852         cfg.html = this.html || cfg.html;
1853         
1854         cfg.src = this.src || cfg.src;
1855         
1856         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1857             cfg.cls += ' img-' + this.border;
1858         }
1859         
1860         if(this.alt){
1861             cfg.alt = this.alt;
1862         }
1863         
1864         if(this.href){
1865             var a = {
1866                 tag: 'a',
1867                 href: this.href,
1868                 cn: [
1869                     cfg
1870                 ]
1871             };
1872             
1873             if(this.target){
1874                 a.target = this.target;
1875             }
1876             
1877         }
1878         
1879         return (this.href) ? a : cfg;
1880     },
1881     
1882     initEvents: function() 
1883     {
1884         if(!this.href){
1885             this.el.on('click', this.onClick, this);
1886         }
1887         
1888     },
1889     
1890     onClick : function(e)
1891     {
1892         Roo.log('img onclick');
1893         this.fireEvent('click', this, e);
1894     },
1895     /**
1896      * Sets the url of the image - used to update it
1897      * @param {String} url the url of the image
1898      */
1899     
1900     setSrc : function(url)
1901     {
1902         this.src =  url;
1903         
1904         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1905             this.el.dom.src =  url;
1906             return;
1907         }
1908         
1909         this.el.select('img', true).first().dom.src =  url;
1910     }
1911     
1912     
1913    
1914 });
1915
1916  /*
1917  * - LGPL
1918  *
1919  * image
1920  * 
1921  */
1922
1923
1924 /**
1925  * @class Roo.bootstrap.Link
1926  * @extends Roo.bootstrap.Component
1927  * Bootstrap Link Class
1928  * @cfg {String} alt image alternative text
1929  * @cfg {String} href a tag href
1930  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1931  * @cfg {String} html the content of the link.
1932  * @cfg {String} anchor name for the anchor link
1933  * @cfg {String} fa - favicon
1934
1935  * @cfg {Boolean} preventDefault (true | false) default false
1936
1937  * 
1938  * @constructor
1939  * Create a new Input
1940  * @param {Object} config The config object
1941  */
1942
1943 Roo.bootstrap.Link = function(config){
1944     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1945     
1946     this.addEvents({
1947         // img events
1948         /**
1949          * @event click
1950          * The img click event for the img.
1951          * @param {Roo.EventObject} e
1952          */
1953         "click" : true
1954     });
1955 };
1956
1957 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1958     
1959     href: false,
1960     target: false,
1961     preventDefault: false,
1962     anchor : false,
1963     alt : false,
1964     fa: false,
1965
1966
1967     getAutoCreate : function()
1968     {
1969         var html = this.html || '';
1970         
1971         if (this.fa !== false) {
1972             html = '<i class="fa fa-' + this.fa + '"></i>';
1973         }
1974         var cfg = {
1975             tag: 'a'
1976         };
1977         // anchor's do not require html/href...
1978         if (this.anchor === false) {
1979             cfg.html = html;
1980             cfg.href = this.href || '#';
1981         } else {
1982             cfg.name = this.anchor;
1983             if (this.html !== false || this.fa !== false) {
1984                 cfg.html = html;
1985             }
1986             if (this.href !== false) {
1987                 cfg.href = this.href;
1988             }
1989         }
1990         
1991         if(this.alt !== false){
1992             cfg.alt = this.alt;
1993         }
1994         
1995         
1996         if(this.target !== false) {
1997             cfg.target = this.target;
1998         }
1999         
2000         return cfg;
2001     },
2002     
2003     initEvents: function() {
2004         
2005         if(!this.href || this.preventDefault){
2006             this.el.on('click', this.onClick, this);
2007         }
2008     },
2009     
2010     onClick : function(e)
2011     {
2012         if(this.preventDefault){
2013             e.preventDefault();
2014         }
2015         //Roo.log('img onclick');
2016         this.fireEvent('click', this, e);
2017     }
2018    
2019 });
2020
2021  /*
2022  * - LGPL
2023  *
2024  * header
2025  * 
2026  */
2027
2028 /**
2029  * @class Roo.bootstrap.Header
2030  * @extends Roo.bootstrap.Component
2031  * Bootstrap Header class
2032  * @cfg {String} html content of header
2033  * @cfg {Number} level (1|2|3|4|5|6) default 1
2034  * 
2035  * @constructor
2036  * Create a new Header
2037  * @param {Object} config The config object
2038  */
2039
2040
2041 Roo.bootstrap.Header  = function(config){
2042     Roo.bootstrap.Header.superclass.constructor.call(this, config);
2043 };
2044
2045 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
2046     
2047     //href : false,
2048     html : false,
2049     level : 1,
2050     
2051     
2052     
2053     getAutoCreate : function(){
2054         
2055         
2056         
2057         var cfg = {
2058             tag: 'h' + (1 *this.level),
2059             html: this.html || ''
2060         } ;
2061         
2062         return cfg;
2063     }
2064    
2065 });
2066
2067  
2068
2069  /*
2070  * Based on:
2071  * Ext JS Library 1.1.1
2072  * Copyright(c) 2006-2007, Ext JS, LLC.
2073  *
2074  * Originally Released Under LGPL - original licence link has changed is not relivant.
2075  *
2076  * Fork - LGPL
2077  * <script type="text/javascript">
2078  */
2079  
2080 /**
2081  * @class Roo.bootstrap.MenuMgr
2082  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
2083  * @singleton
2084  */
2085 Roo.bootstrap.MenuMgr = function(){
2086    var menus, active, groups = {}, attached = false, lastShow = new Date();
2087
2088    // private - called when first menu is created
2089    function init(){
2090        menus = {};
2091        active = new Roo.util.MixedCollection();
2092        Roo.get(document).addKeyListener(27, function(){
2093            if(active.length > 0){
2094                hideAll();
2095            }
2096        });
2097    }
2098
2099    // private
2100    function hideAll(){
2101        if(active && active.length > 0){
2102            var c = active.clone();
2103            c.each(function(m){
2104                m.hide();
2105            });
2106        }
2107    }
2108
2109    // private
2110    function onHide(m){
2111        active.remove(m);
2112        if(active.length < 1){
2113            Roo.get(document).un("mouseup", onMouseDown);
2114             
2115            attached = false;
2116        }
2117    }
2118
2119    // private
2120    function onShow(m){
2121        var last = active.last();
2122        lastShow = new Date();
2123        active.add(m);
2124        if(!attached){
2125           Roo.get(document).on("mouseup", onMouseDown);
2126            
2127            attached = true;
2128        }
2129        if(m.parentMenu){
2130           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
2131           m.parentMenu.activeChild = m;
2132        }else if(last && last.isVisible()){
2133           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
2134        }
2135    }
2136
2137    // private
2138    function onBeforeHide(m){
2139        if(m.activeChild){
2140            m.activeChild.hide();
2141        }
2142        if(m.autoHideTimer){
2143            clearTimeout(m.autoHideTimer);
2144            delete m.autoHideTimer;
2145        }
2146    }
2147
2148    // private
2149    function onBeforeShow(m){
2150        var pm = m.parentMenu;
2151        if(!pm && !m.allowOtherMenus){
2152            hideAll();
2153        }else if(pm && pm.activeChild && active != m){
2154            pm.activeChild.hide();
2155        }
2156    }
2157
2158    // private this should really trigger on mouseup..
2159    function onMouseDown(e){
2160         Roo.log("on Mouse Up");
2161         
2162         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
2163             Roo.log("MenuManager hideAll");
2164             hideAll();
2165             e.stopEvent();
2166         }
2167         
2168         
2169    }
2170
2171    // private
2172    function onBeforeCheck(mi, state){
2173        if(state){
2174            var g = groups[mi.group];
2175            for(var i = 0, l = g.length; i < l; i++){
2176                if(g[i] != mi){
2177                    g[i].setChecked(false);
2178                }
2179            }
2180        }
2181    }
2182
2183    return {
2184
2185        /**
2186         * Hides all menus that are currently visible
2187         */
2188        hideAll : function(){
2189             hideAll();  
2190        },
2191
2192        // private
2193        register : function(menu){
2194            if(!menus){
2195                init();
2196            }
2197            menus[menu.id] = menu;
2198            menu.on("beforehide", onBeforeHide);
2199            menu.on("hide", onHide);
2200            menu.on("beforeshow", onBeforeShow);
2201            menu.on("show", onShow);
2202            var g = menu.group;
2203            if(g && menu.events["checkchange"]){
2204                if(!groups[g]){
2205                    groups[g] = [];
2206                }
2207                groups[g].push(menu);
2208                menu.on("checkchange", onCheck);
2209            }
2210        },
2211
2212         /**
2213          * Returns a {@link Roo.menu.Menu} object
2214          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
2215          * be used to generate and return a new Menu instance.
2216          */
2217        get : function(menu){
2218            if(typeof menu == "string"){ // menu id
2219                return menus[menu];
2220            }else if(menu.events){  // menu instance
2221                return menu;
2222            }
2223            /*else if(typeof menu.length == 'number'){ // array of menu items?
2224                return new Roo.bootstrap.Menu({items:menu});
2225            }else{ // otherwise, must be a config
2226                return new Roo.bootstrap.Menu(menu);
2227            }
2228            */
2229            return false;
2230        },
2231
2232        // private
2233        unregister : function(menu){
2234            delete menus[menu.id];
2235            menu.un("beforehide", onBeforeHide);
2236            menu.un("hide", onHide);
2237            menu.un("beforeshow", onBeforeShow);
2238            menu.un("show", onShow);
2239            var g = menu.group;
2240            if(g && menu.events["checkchange"]){
2241                groups[g].remove(menu);
2242                menu.un("checkchange", onCheck);
2243            }
2244        },
2245
2246        // private
2247        registerCheckable : function(menuItem){
2248            var g = menuItem.group;
2249            if(g){
2250                if(!groups[g]){
2251                    groups[g] = [];
2252                }
2253                groups[g].push(menuItem);
2254                menuItem.on("beforecheckchange", onBeforeCheck);
2255            }
2256        },
2257
2258        // private
2259        unregisterCheckable : function(menuItem){
2260            var g = menuItem.group;
2261            if(g){
2262                groups[g].remove(menuItem);
2263                menuItem.un("beforecheckchange", onBeforeCheck);
2264            }
2265        }
2266    };
2267 }();/*
2268  * - LGPL
2269  *
2270  * menu
2271  * 
2272  */
2273
2274 /**
2275  * @class Roo.bootstrap.Menu
2276  * @extends Roo.bootstrap.Component
2277  * Bootstrap Menu class - container for MenuItems
2278  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2279  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2280  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2281  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2282  * 
2283  * @constructor
2284  * Create a new Menu
2285  * @param {Object} config The config object
2286  */
2287
2288
2289 Roo.bootstrap.Menu = function(config){
2290     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2291     if (this.registerMenu && this.type != 'treeview')  {
2292         Roo.bootstrap.MenuMgr.register(this);
2293     }
2294     
2295     
2296     this.addEvents({
2297         /**
2298          * @event beforeshow
2299          * Fires before this menu is displayed (return false to block)
2300          * @param {Roo.menu.Menu} this
2301          */
2302         beforeshow : true,
2303         /**
2304          * @event beforehide
2305          * Fires before this menu is hidden (return false to block)
2306          * @param {Roo.menu.Menu} this
2307          */
2308         beforehide : true,
2309         /**
2310          * @event show
2311          * Fires after this menu is displayed
2312          * @param {Roo.menu.Menu} this
2313          */
2314         show : true,
2315         /**
2316          * @event hide
2317          * Fires after this menu is hidden
2318          * @param {Roo.menu.Menu} this
2319          */
2320         hide : true,
2321         /**
2322          * @event click
2323          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2324          * @param {Roo.menu.Menu} this
2325          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2326          * @param {Roo.EventObject} e
2327          */
2328         click : true,
2329         /**
2330          * @event mouseover
2331          * Fires when the mouse is hovering over this menu
2332          * @param {Roo.menu.Menu} this
2333          * @param {Roo.EventObject} e
2334          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2335          */
2336         mouseover : true,
2337         /**
2338          * @event mouseout
2339          * Fires when the mouse exits this menu
2340          * @param {Roo.menu.Menu} this
2341          * @param {Roo.EventObject} e
2342          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2343          */
2344         mouseout : true,
2345         /**
2346          * @event itemclick
2347          * Fires when a menu item contained in this menu is clicked
2348          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2349          * @param {Roo.EventObject} e
2350          */
2351         itemclick: true
2352     });
2353     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2354 };
2355
2356 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2357     
2358    /// html : false,
2359     //align : '',
2360     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2361     type: false,
2362     /**
2363      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2364      */
2365     registerMenu : true,
2366     
2367     menuItems :false, // stores the menu items..
2368     
2369     hidden:true,
2370         
2371     parentMenu : false,
2372     
2373     stopEvent : true,
2374     
2375     isLink : false,
2376     
2377     getChildContainer : function() {
2378         return this.el;  
2379     },
2380     
2381     getAutoCreate : function(){
2382          
2383         //if (['right'].indexOf(this.align)!==-1) {
2384         //    cfg.cn[1].cls += ' pull-right'
2385         //}
2386         
2387         
2388         var cfg = {
2389             tag : 'ul',
2390             cls : 'dropdown-menu' ,
2391             style : 'z-index:1000'
2392             
2393         };
2394         
2395         if (this.type === 'submenu') {
2396             cfg.cls = 'submenu active';
2397         }
2398         if (this.type === 'treeview') {
2399             cfg.cls = 'treeview-menu';
2400         }
2401         
2402         return cfg;
2403     },
2404     initEvents : function() {
2405         
2406        // Roo.log("ADD event");
2407        // Roo.log(this.triggerEl.dom);
2408         
2409         this.triggerEl.on('click', this.onTriggerClick, this);
2410         
2411         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2412         
2413         
2414         if (this.triggerEl.hasClass('nav-item')) {
2415             // dropdown toggle on the 'a' in BS4?
2416             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2417         } else {
2418             this.triggerEl.addClass('dropdown-toggle');
2419         }
2420         if (Roo.isTouch) {
2421             this.el.on('touchstart'  , this.onTouch, this);
2422         }
2423         this.el.on('click' , this.onClick, this);
2424
2425         this.el.on("mouseover", this.onMouseOver, this);
2426         this.el.on("mouseout", this.onMouseOut, this);
2427         
2428     },
2429     
2430     findTargetItem : function(e)
2431     {
2432         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2433         if(!t){
2434             return false;
2435         }
2436         //Roo.log(t);         Roo.log(t.id);
2437         if(t && t.id){
2438             //Roo.log(this.menuitems);
2439             return this.menuitems.get(t.id);
2440             
2441             //return this.items.get(t.menuItemId);
2442         }
2443         
2444         return false;
2445     },
2446     
2447     onTouch : function(e) 
2448     {
2449         Roo.log("menu.onTouch");
2450         //e.stopEvent(); this make the user popdown broken
2451         this.onClick(e);
2452     },
2453     
2454     onClick : function(e)
2455     {
2456         Roo.log("menu.onClick");
2457         
2458         var t = this.findTargetItem(e);
2459         if(!t || t.isContainer){
2460             return;
2461         }
2462         Roo.log(e);
2463         /*
2464         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2465             if(t == this.activeItem && t.shouldDeactivate(e)){
2466                 this.activeItem.deactivate();
2467                 delete this.activeItem;
2468                 return;
2469             }
2470             if(t.canActivate){
2471                 this.setActiveItem(t, true);
2472             }
2473             return;
2474             
2475             
2476         }
2477         */
2478        
2479         Roo.log('pass click event');
2480         
2481         t.onClick(e);
2482         
2483         this.fireEvent("click", this, t, e);
2484         
2485         var _this = this;
2486         
2487         if(!t.href.length || t.href == '#'){
2488             (function() { _this.hide(); }).defer(100);
2489         }
2490         
2491     },
2492     
2493     onMouseOver : function(e){
2494         var t  = this.findTargetItem(e);
2495         //Roo.log(t);
2496         //if(t){
2497         //    if(t.canActivate && !t.disabled){
2498         //        this.setActiveItem(t, true);
2499         //    }
2500         //}
2501         
2502         this.fireEvent("mouseover", this, e, t);
2503     },
2504     isVisible : function(){
2505         return !this.hidden;
2506     },
2507     onMouseOut : function(e){
2508         var t  = this.findTargetItem(e);
2509         
2510         //if(t ){
2511         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2512         //        this.activeItem.deactivate();
2513         //        delete this.activeItem;
2514         //    }
2515         //}
2516         this.fireEvent("mouseout", this, e, t);
2517     },
2518     
2519     
2520     /**
2521      * Displays this menu relative to another element
2522      * @param {String/HTMLElement/Roo.Element} element The element to align to
2523      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2524      * the element (defaults to this.defaultAlign)
2525      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2526      */
2527     show : function(el, pos, parentMenu)
2528     {
2529         if (false === this.fireEvent("beforeshow", this)) {
2530             Roo.log("show canceled");
2531             return;
2532         }
2533         this.parentMenu = parentMenu;
2534         if(!this.el){
2535             this.render();
2536         }
2537         
2538         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2539     },
2540      /**
2541      * Displays this menu at a specific xy position
2542      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2543      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2544      */
2545     showAt : function(xy, parentMenu, /* private: */_e){
2546         this.parentMenu = parentMenu;
2547         if(!this.el){
2548             this.render();
2549         }
2550         if(_e !== false){
2551             this.fireEvent("beforeshow", this);
2552             //xy = this.el.adjustForConstraints(xy);
2553         }
2554         
2555         //this.el.show();
2556         this.hideMenuItems();
2557         this.hidden = false;
2558         this.triggerEl.addClass('open');
2559         this.el.addClass('show');
2560         
2561         // reassign x when hitting right
2562         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2563             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2564         }
2565         
2566         // reassign y when hitting bottom
2567         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2568             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2569         }
2570         
2571         // but the list may align on trigger left or trigger top... should it be a properity?
2572         
2573         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2574             this.el.setXY(xy);
2575         }
2576         
2577         this.focus();
2578         this.fireEvent("show", this);
2579     },
2580     
2581     focus : function(){
2582         return;
2583         if(!this.hidden){
2584             this.doFocus.defer(50, this);
2585         }
2586     },
2587
2588     doFocus : function(){
2589         if(!this.hidden){
2590             this.focusEl.focus();
2591         }
2592     },
2593
2594     /**
2595      * Hides this menu and optionally all parent menus
2596      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2597      */
2598     hide : function(deep)
2599     {
2600         if (false === this.fireEvent("beforehide", this)) {
2601             Roo.log("hide canceled");
2602             return;
2603         }
2604         this.hideMenuItems();
2605         if(this.el && this.isVisible()){
2606            
2607             if(this.activeItem){
2608                 this.activeItem.deactivate();
2609                 this.activeItem = null;
2610             }
2611             this.triggerEl.removeClass('open');;
2612             this.el.removeClass('show');
2613             this.hidden = true;
2614             this.fireEvent("hide", this);
2615         }
2616         if(deep === true && this.parentMenu){
2617             this.parentMenu.hide(true);
2618         }
2619     },
2620     
2621     onTriggerClick : function(e)
2622     {
2623         Roo.log('trigger click');
2624         
2625         var target = e.getTarget();
2626         
2627         Roo.log(target.nodeName.toLowerCase());
2628         
2629         if(target.nodeName.toLowerCase() === 'i'){
2630             e.preventDefault();
2631         }
2632         
2633     },
2634     
2635     onTriggerPress  : function(e)
2636     {
2637         Roo.log('trigger press');
2638         //Roo.log(e.getTarget());
2639        // Roo.log(this.triggerEl.dom);
2640        
2641         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2642         var pel = Roo.get(e.getTarget());
2643         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2644             Roo.log('is treeview or dropdown?');
2645             return;
2646         }
2647         
2648         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2649             return;
2650         }
2651         
2652         if (this.isVisible()) {
2653             Roo.log('hide');
2654             this.hide();
2655         } else {
2656             Roo.log('show');
2657             this.show(this.triggerEl, '?', false);
2658         }
2659         
2660         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2661             e.stopEvent();
2662         }
2663         
2664     },
2665        
2666     
2667     hideMenuItems : function()
2668     {
2669         Roo.log("hide Menu Items");
2670         if (!this.el) { 
2671             return;
2672         }
2673         
2674         this.el.select('.open',true).each(function(aa) {
2675             
2676             aa.removeClass('open');
2677          
2678         });
2679     },
2680     addxtypeChild : function (tree, cntr) {
2681         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2682           
2683         this.menuitems.add(comp);
2684         return comp;
2685
2686     },
2687     getEl : function()
2688     {
2689         Roo.log(this.el);
2690         return this.el;
2691     },
2692     
2693     clear : function()
2694     {
2695         this.getEl().dom.innerHTML = '';
2696         this.menuitems.clear();
2697     }
2698 });
2699
2700  
2701  /*
2702  * - LGPL
2703  *
2704  * menu item
2705  * 
2706  */
2707
2708
2709 /**
2710  * @class Roo.bootstrap.MenuItem
2711  * @extends Roo.bootstrap.Component
2712  * Bootstrap MenuItem class
2713  * @cfg {String} html the menu label
2714  * @cfg {String} href the link
2715  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2716  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2717  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2718  * @cfg {String} fa favicon to show on left of menu item.
2719  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2720  * 
2721  * 
2722  * @constructor
2723  * Create a new MenuItem
2724  * @param {Object} config The config object
2725  */
2726
2727
2728 Roo.bootstrap.MenuItem = function(config){
2729     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2730     this.addEvents({
2731         // raw events
2732         /**
2733          * @event click
2734          * The raw click event for the entire grid.
2735          * @param {Roo.bootstrap.MenuItem} this
2736          * @param {Roo.EventObject} e
2737          */
2738         "click" : true
2739     });
2740 };
2741
2742 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2743     
2744     href : false,
2745     html : false,
2746     preventDefault: false,
2747     isContainer : false,
2748     active : false,
2749     fa: false,
2750     
2751     getAutoCreate : function(){
2752         
2753         if(this.isContainer){
2754             return {
2755                 tag: 'li',
2756                 cls: 'dropdown-menu-item '
2757             };
2758         }
2759         var ctag = {
2760             tag: 'span',
2761             html: 'Link'
2762         };
2763         
2764         var anc = {
2765             tag : 'a',
2766             cls : 'dropdown-item',
2767             href : '#',
2768             cn : [  ]
2769         };
2770         
2771         if (this.fa !== false) {
2772             anc.cn.push({
2773                 tag : 'i',
2774                 cls : 'fa fa-' + this.fa
2775             });
2776         }
2777         
2778         anc.cn.push(ctag);
2779         
2780         
2781         var cfg= {
2782             tag: 'li',
2783             cls: 'dropdown-menu-item',
2784             cn: [ anc ]
2785         };
2786         if (this.parent().type == 'treeview') {
2787             cfg.cls = 'treeview-menu';
2788         }
2789         if (this.active) {
2790             cfg.cls += ' active';
2791         }
2792         
2793         
2794         
2795         anc.href = this.href || cfg.cn[0].href ;
2796         ctag.html = this.html || cfg.cn[0].html ;
2797         return cfg;
2798     },
2799     
2800     initEvents: function()
2801     {
2802         if (this.parent().type == 'treeview') {
2803             this.el.select('a').on('click', this.onClick, this);
2804         }
2805         
2806         if (this.menu) {
2807             this.menu.parentType = this.xtype;
2808             this.menu.triggerEl = this.el;
2809             this.menu = this.addxtype(Roo.apply({}, this.menu));
2810         }
2811         
2812     },
2813     onClick : function(e)
2814     {
2815         Roo.log('item on click ');
2816         
2817         if(this.preventDefault){
2818             e.preventDefault();
2819         }
2820         //this.parent().hideMenuItems();
2821         
2822         this.fireEvent('click', this, e);
2823     },
2824     getEl : function()
2825     {
2826         return this.el;
2827     } 
2828 });
2829
2830  
2831
2832  /*
2833  * - LGPL
2834  *
2835  * menu separator
2836  * 
2837  */
2838
2839
2840 /**
2841  * @class Roo.bootstrap.MenuSeparator
2842  * @extends Roo.bootstrap.Component
2843  * Bootstrap MenuSeparator class
2844  * 
2845  * @constructor
2846  * Create a new MenuItem
2847  * @param {Object} config The config object
2848  */
2849
2850
2851 Roo.bootstrap.MenuSeparator = function(config){
2852     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2853 };
2854
2855 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2856     
2857     getAutoCreate : function(){
2858         var cfg = {
2859             cls: 'divider',
2860             tag : 'li'
2861         };
2862         
2863         return cfg;
2864     }
2865    
2866 });
2867
2868  
2869
2870  
2871 /*
2872 * Licence: LGPL
2873 */
2874
2875 /**
2876  * @class Roo.bootstrap.Modal
2877  * @extends Roo.bootstrap.Component
2878  * Bootstrap Modal class
2879  * @cfg {String} title Title of dialog
2880  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2881  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2882  * @cfg {Boolean} specificTitle default false
2883  * @cfg {Array} buttons Array of buttons or standard button set..
2884  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2885  * @cfg {Boolean} animate default true
2886  * @cfg {Boolean} allow_close default true
2887  * @cfg {Boolean} fitwindow default false
2888  * @cfg {Number} width fixed width - usefull for chrome extension only really.
2889  * @cfg {Number} height fixed height - usefull for chrome extension only really.
2890  * @cfg {String} size (sm|lg) default empty
2891  * @cfg {Number} max_width set the max width of modal
2892  *
2893  *
2894  * @constructor
2895  * Create a new Modal Dialog
2896  * @param {Object} config The config object
2897  */
2898
2899 Roo.bootstrap.Modal = function(config){
2900     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2901     this.addEvents({
2902         // raw events
2903         /**
2904          * @event btnclick
2905          * The raw btnclick event for the button
2906          * @param {Roo.EventObject} e
2907          */
2908         "btnclick" : true,
2909         /**
2910          * @event resize
2911          * Fire when dialog resize
2912          * @param {Roo.bootstrap.Modal} this
2913          * @param {Roo.EventObject} e
2914          */
2915         "resize" : true
2916     });
2917     this.buttons = this.buttons || [];
2918
2919     if (this.tmpl) {
2920         this.tmpl = Roo.factory(this.tmpl);
2921     }
2922
2923 };
2924
2925 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2926
2927     title : 'test dialog',
2928
2929     buttons : false,
2930
2931     // set on load...
2932
2933     html: false,
2934
2935     tmp: false,
2936
2937     specificTitle: false,
2938
2939     buttonPosition: 'right',
2940
2941     allow_close : true,
2942
2943     animate : true,
2944
2945     fitwindow: false,
2946     
2947      // private
2948     dialogEl: false,
2949     bodyEl:  false,
2950     footerEl:  false,
2951     titleEl:  false,
2952     closeEl:  false,
2953
2954     size: '',
2955     
2956     max_width: 0,
2957     
2958     max_height: 0,
2959     
2960     fit_content: false,
2961
2962     onRender : function(ct, position)
2963     {
2964         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2965
2966         if(!this.el){
2967             var cfg = Roo.apply({},  this.getAutoCreate());
2968             cfg.id = Roo.id();
2969             //if(!cfg.name){
2970             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2971             //}
2972             //if (!cfg.name.length) {
2973             //    delete cfg.name;
2974            // }
2975             if (this.cls) {
2976                 cfg.cls += ' ' + this.cls;
2977             }
2978             if (this.style) {
2979                 cfg.style = this.style;
2980             }
2981             this.el = Roo.get(document.body).createChild(cfg, position);
2982         }
2983         //var type = this.el.dom.type;
2984
2985
2986         if(this.tabIndex !== undefined){
2987             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2988         }
2989
2990         this.dialogEl = this.el.select('.modal-dialog',true).first();
2991         this.bodyEl = this.el.select('.modal-body',true).first();
2992         this.closeEl = this.el.select('.modal-header .close', true).first();
2993         this.headerEl = this.el.select('.modal-header',true).first();
2994         this.titleEl = this.el.select('.modal-title',true).first();
2995         this.footerEl = this.el.select('.modal-footer',true).first();
2996
2997         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2998         
2999         //this.el.addClass("x-dlg-modal");
3000
3001         if (this.buttons.length) {
3002             Roo.each(this.buttons, function(bb) {
3003                 var b = Roo.apply({}, bb);
3004                 b.xns = b.xns || Roo.bootstrap;
3005                 b.xtype = b.xtype || 'Button';
3006                 if (typeof(b.listeners) == 'undefined') {
3007                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
3008                 }
3009
3010                 var btn = Roo.factory(b);
3011
3012                 btn.render(this.getButtonContainer());
3013
3014             },this);
3015         }
3016         // render the children.
3017         var nitems = [];
3018
3019         if(typeof(this.items) != 'undefined'){
3020             var items = this.items;
3021             delete this.items;
3022
3023             for(var i =0;i < items.length;i++) {
3024                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
3025             }
3026         }
3027
3028         this.items = nitems;
3029
3030         // where are these used - they used to be body/close/footer
3031
3032
3033         this.initEvents();
3034         //this.el.addClass([this.fieldClass, this.cls]);
3035
3036     },
3037
3038     getAutoCreate : function()
3039     {
3040         // we will default to modal-body-overflow - might need to remove or make optional later.
3041         var bdy = {
3042                 cls : 'modal-body enable-modal-body-overflow ', 
3043                 html : this.html || ''
3044         };
3045
3046         var title = {
3047             tag: 'h4',
3048             cls : 'modal-title',
3049             html : this.title
3050         };
3051
3052         if(this.specificTitle){
3053             title = this.title;
3054
3055         }
3056
3057         var header = [];
3058         if (this.allow_close && Roo.bootstrap.version == 3) {
3059             header.push({
3060                 tag: 'button',
3061                 cls : 'close',
3062                 html : '&times'
3063             });
3064         }
3065
3066         header.push(title);
3067
3068         if (this.allow_close && Roo.bootstrap.version == 4) {
3069             header.push({
3070                 tag: 'button',
3071                 cls : 'close',
3072                 html : '&times'
3073             });
3074         }
3075         
3076         var size = '';
3077
3078         if(this.size.length){
3079             size = 'modal-' + this.size;
3080         }
3081         
3082         var footer = Roo.bootstrap.version == 3 ?
3083             {
3084                 cls : 'modal-footer',
3085                 cn : [
3086                     {
3087                         tag: 'div',
3088                         cls: 'btn-' + this.buttonPosition
3089                     }
3090                 ]
3091
3092             } :
3093             {  // BS4 uses mr-auto on left buttons....
3094                 cls : 'modal-footer'
3095             };
3096
3097             
3098
3099         
3100         
3101         var modal = {
3102             cls: "modal",
3103              cn : [
3104                 {
3105                     cls: "modal-dialog " + size,
3106                     cn : [
3107                         {
3108                             cls : "modal-content",
3109                             cn : [
3110                                 {
3111                                     cls : 'modal-header',
3112                                     cn : header
3113                                 },
3114                                 bdy,
3115                                 footer
3116                             ]
3117
3118                         }
3119                     ]
3120
3121                 }
3122             ]
3123         };
3124
3125         if(this.animate){
3126             modal.cls += ' fade';
3127         }
3128
3129         return modal;
3130
3131     },
3132     getChildContainer : function() {
3133
3134          return this.bodyEl;
3135
3136     },
3137     getButtonContainer : function() {
3138         
3139          return Roo.bootstrap.version == 4 ?
3140             this.el.select('.modal-footer',true).first()
3141             : this.el.select('.modal-footer div',true).first();
3142
3143     },
3144     initEvents : function()
3145     {
3146         if (this.allow_close) {
3147             this.closeEl.on('click', this.hide, this);
3148         }
3149         Roo.EventManager.onWindowResize(this.resize, this, true);
3150
3151
3152     },
3153   
3154
3155     resize : function()
3156     {
3157         this.maskEl.setSize(
3158             Roo.lib.Dom.getViewWidth(true),
3159             Roo.lib.Dom.getViewHeight(true)
3160         );
3161         
3162         if (this.fitwindow) {
3163             
3164            
3165             this.setSize(
3166                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
3167                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
3168             );
3169             return;
3170         }
3171         
3172         if(this.max_width !== 0) {
3173             
3174             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
3175             
3176             if(this.height) {
3177                 this.setSize(w, this.height);
3178                 return;
3179             }
3180             
3181             if(this.max_height) {
3182                 this.setSize(w,Math.min(
3183                     this.max_height,
3184                     Roo.lib.Dom.getViewportHeight(true) - 60
3185                 ));
3186                 
3187                 return;
3188             }
3189             
3190             if(!this.fit_content) {
3191                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
3192                 return;
3193             }
3194             
3195             this.setSize(w, Math.min(
3196                 60 +
3197                 this.headerEl.getHeight() + 
3198                 this.footerEl.getHeight() + 
3199                 this.getChildHeight(this.bodyEl.dom.childNodes),
3200                 Roo.lib.Dom.getViewportHeight(true) - 60)
3201             );
3202         }
3203         
3204     },
3205
3206     setSize : function(w,h)
3207     {
3208         if (!w && !h) {
3209             return;
3210         }
3211         
3212         this.resizeTo(w,h);
3213     },
3214
3215     show : function() {
3216
3217         if (!this.rendered) {
3218             this.render();
3219         }
3220
3221         //this.el.setStyle('display', 'block');
3222         this.el.removeClass('hideing');
3223         this.el.dom.style.display='block';
3224         
3225         Roo.get(document.body).addClass('modal-open');
3226  
3227         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
3228             
3229             (function(){
3230                 this.el.addClass('show');
3231                 this.el.addClass('in');
3232             }).defer(50, this);
3233         }else{
3234             this.el.addClass('show');
3235             this.el.addClass('in');
3236         }
3237
3238         // not sure how we can show data in here..
3239         //if (this.tmpl) {
3240         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3241         //}
3242
3243         Roo.get(document.body).addClass("x-body-masked");
3244         
3245         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
3246         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3247         this.maskEl.dom.style.display = 'block';
3248         this.maskEl.addClass('show');
3249         
3250         
3251         this.resize();
3252         
3253         this.fireEvent('show', this);
3254
3255         // set zindex here - otherwise it appears to be ignored...
3256         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3257
3258         (function () {
3259             this.items.forEach( function(e) {
3260                 e.layout ? e.layout() : false;
3261
3262             });
3263         }).defer(100,this);
3264
3265     },
3266     hide : function()
3267     {
3268         if(this.fireEvent("beforehide", this) !== false){
3269             
3270             this.maskEl.removeClass('show');
3271             
3272             this.maskEl.dom.style.display = '';
3273             Roo.get(document.body).removeClass("x-body-masked");
3274             this.el.removeClass('in');
3275             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3276
3277             if(this.animate){ // why
3278                 this.el.addClass('hideing');
3279                 this.el.removeClass('show');
3280                 (function(){
3281                     if (!this.el.hasClass('hideing')) {
3282                         return; // it's been shown again...
3283                     }
3284                     
3285                     this.el.dom.style.display='';
3286
3287                     Roo.get(document.body).removeClass('modal-open');
3288                     this.el.removeClass('hideing');
3289                 }).defer(150,this);
3290                 
3291             }else{
3292                 this.el.removeClass('show');
3293                 this.el.dom.style.display='';
3294                 Roo.get(document.body).removeClass('modal-open');
3295
3296             }
3297             this.fireEvent('hide', this);
3298         }
3299     },
3300     isVisible : function()
3301     {
3302         
3303         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3304         
3305     },
3306
3307     addButton : function(str, cb)
3308     {
3309
3310
3311         var b = Roo.apply({}, { html : str } );
3312         b.xns = b.xns || Roo.bootstrap;
3313         b.xtype = b.xtype || 'Button';
3314         if (typeof(b.listeners) == 'undefined') {
3315             b.listeners = { click : cb.createDelegate(this)  };
3316         }
3317
3318         var btn = Roo.factory(b);
3319
3320         btn.render(this.getButtonContainer());
3321
3322         return btn;
3323
3324     },
3325
3326     setDefaultButton : function(btn)
3327     {
3328         //this.el.select('.modal-footer').()
3329     },
3330
3331     resizeTo: function(w,h)
3332     {
3333         this.dialogEl.setWidth(w);
3334         
3335         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
3336
3337         this.bodyEl.setHeight(h - diff);
3338         
3339         this.fireEvent('resize', this);
3340     },
3341     
3342     setContentSize  : function(w, h)
3343     {
3344
3345     },
3346     onButtonClick: function(btn,e)
3347     {
3348         //Roo.log([a,b,c]);
3349         this.fireEvent('btnclick', btn.name, e);
3350     },
3351      /**
3352      * Set the title of the Dialog
3353      * @param {String} str new Title
3354      */
3355     setTitle: function(str) {
3356         this.titleEl.dom.innerHTML = str;
3357     },
3358     /**
3359      * Set the body of the Dialog
3360      * @param {String} str new Title
3361      */
3362     setBody: function(str) {
3363         this.bodyEl.dom.innerHTML = str;
3364     },
3365     /**
3366      * Set the body of the Dialog using the template
3367      * @param {Obj} data - apply this data to the template and replace the body contents.
3368      */
3369     applyBody: function(obj)
3370     {
3371         if (!this.tmpl) {
3372             Roo.log("Error - using apply Body without a template");
3373             //code
3374         }
3375         this.tmpl.overwrite(this.bodyEl, obj);
3376     },
3377     
3378     getChildHeight : function(child_nodes)
3379     {
3380         if(
3381             !child_nodes ||
3382             child_nodes.length == 0
3383         ) {
3384             return;
3385         }
3386         
3387         var child_height = 0;
3388         
3389         for(var i = 0; i < child_nodes.length; i++) {
3390             
3391             /*
3392             * for modal with tabs...
3393             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3394                 
3395                 var layout_childs = child_nodes[i].childNodes;
3396                 
3397                 for(var j = 0; j < layout_childs.length; j++) {
3398                     
3399                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3400                         
3401                         var layout_body_childs = layout_childs[j].childNodes;
3402                         
3403                         for(var k = 0; k < layout_body_childs.length; k++) {
3404                             
3405                             if(layout_body_childs[k].classList.contains('navbar')) {
3406                                 child_height += layout_body_childs[k].offsetHeight;
3407                                 continue;
3408                             }
3409                             
3410                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3411                                 
3412                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3413                                 
3414                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3415                                     
3416                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3417                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3418                                         continue;
3419                                     }
3420                                     
3421                                 }
3422                                 
3423                             }
3424                             
3425                         }
3426                     }
3427                 }
3428                 continue;
3429             }
3430             */
3431             
3432             child_height += child_nodes[i].offsetHeight;
3433             // Roo.log(child_nodes[i].offsetHeight);
3434         }
3435         
3436         return child_height;
3437     }
3438
3439 });
3440
3441
3442 Roo.apply(Roo.bootstrap.Modal,  {
3443     /**
3444          * Button config that displays a single OK button
3445          * @type Object
3446          */
3447         OK :  [{
3448             name : 'ok',
3449             weight : 'primary',
3450             html : 'OK'
3451         }],
3452         /**
3453          * Button config that displays Yes and No buttons
3454          * @type Object
3455          */
3456         YESNO : [
3457             {
3458                 name  : 'no',
3459                 html : 'No'
3460             },
3461             {
3462                 name  :'yes',
3463                 weight : 'primary',
3464                 html : 'Yes'
3465             }
3466         ],
3467
3468         /**
3469          * Button config that displays OK and Cancel buttons
3470          * @type Object
3471          */
3472         OKCANCEL : [
3473             {
3474                name : 'cancel',
3475                 html : 'Cancel'
3476             },
3477             {
3478                 name : 'ok',
3479                 weight : 'primary',
3480                 html : 'OK'
3481             }
3482         ],
3483         /**
3484          * Button config that displays Yes, No and Cancel buttons
3485          * @type Object
3486          */
3487         YESNOCANCEL : [
3488             {
3489                 name : 'yes',
3490                 weight : 'primary',
3491                 html : 'Yes'
3492             },
3493             {
3494                 name : 'no',
3495                 html : 'No'
3496             },
3497             {
3498                 name : 'cancel',
3499                 html : 'Cancel'
3500             }
3501         ],
3502         
3503         zIndex : 10001
3504 });
3505 /*
3506  * - LGPL
3507  *
3508  * messagebox - can be used as a replace
3509  * 
3510  */
3511 /**
3512  * @class Roo.MessageBox
3513  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3514  * Example usage:
3515  *<pre><code>
3516 // Basic alert:
3517 Roo.Msg.alert('Status', 'Changes saved successfully.');
3518
3519 // Prompt for user data:
3520 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3521     if (btn == 'ok'){
3522         // process text value...
3523     }
3524 });
3525
3526 // Show a dialog using config options:
3527 Roo.Msg.show({
3528    title:'Save Changes?',
3529    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3530    buttons: Roo.Msg.YESNOCANCEL,
3531    fn: processResult,
3532    animEl: 'elId'
3533 });
3534 </code></pre>
3535  * @singleton
3536  */
3537 Roo.bootstrap.MessageBox = function(){
3538     var dlg, opt, mask, waitTimer;
3539     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3540     var buttons, activeTextEl, bwidth;
3541
3542     
3543     // private
3544     var handleButton = function(button){
3545         dlg.hide();
3546         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3547     };
3548
3549     // private
3550     var handleHide = function(){
3551         if(opt && opt.cls){
3552             dlg.el.removeClass(opt.cls);
3553         }
3554         //if(waitTimer){
3555         //    Roo.TaskMgr.stop(waitTimer);
3556         //    waitTimer = null;
3557         //}
3558     };
3559
3560     // private
3561     var updateButtons = function(b){
3562         var width = 0;
3563         if(!b){
3564             buttons["ok"].hide();
3565             buttons["cancel"].hide();
3566             buttons["yes"].hide();
3567             buttons["no"].hide();
3568             dlg.footerEl.hide();
3569             
3570             return width;
3571         }
3572         dlg.footerEl.show();
3573         for(var k in buttons){
3574             if(typeof buttons[k] != "function"){
3575                 if(b[k]){
3576                     buttons[k].show();
3577                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3578                     width += buttons[k].el.getWidth()+15;
3579                 }else{
3580                     buttons[k].hide();
3581                 }
3582             }
3583         }
3584         return width;
3585     };
3586
3587     // private
3588     var handleEsc = function(d, k, e){
3589         if(opt && opt.closable !== false){
3590             dlg.hide();
3591         }
3592         if(e){
3593             e.stopEvent();
3594         }
3595     };
3596
3597     return {
3598         /**
3599          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3600          * @return {Roo.BasicDialog} The BasicDialog element
3601          */
3602         getDialog : function(){
3603            if(!dlg){
3604                 dlg = new Roo.bootstrap.Modal( {
3605                     //draggable: true,
3606                     //resizable:false,
3607                     //constraintoviewport:false,
3608                     //fixedcenter:true,
3609                     //collapsible : false,
3610                     //shim:true,
3611                     //modal: true,
3612                 //    width: 'auto',
3613                   //  height:100,
3614                     //buttonAlign:"center",
3615                     closeClick : function(){
3616                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3617                             handleButton("no");
3618                         }else{
3619                             handleButton("cancel");
3620                         }
3621                     }
3622                 });
3623                 dlg.render();
3624                 dlg.on("hide", handleHide);
3625                 mask = dlg.mask;
3626                 //dlg.addKeyListener(27, handleEsc);
3627                 buttons = {};
3628                 this.buttons = buttons;
3629                 var bt = this.buttonText;
3630                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3631                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3632                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3633                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3634                 //Roo.log(buttons);
3635                 bodyEl = dlg.bodyEl.createChild({
3636
3637                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3638                         '<textarea class="roo-mb-textarea"></textarea>' +
3639                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3640                 });
3641                 msgEl = bodyEl.dom.firstChild;
3642                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3643                 textboxEl.enableDisplayMode();
3644                 textboxEl.addKeyListener([10,13], function(){
3645                     if(dlg.isVisible() && opt && opt.buttons){
3646                         if(opt.buttons.ok){
3647                             handleButton("ok");
3648                         }else if(opt.buttons.yes){
3649                             handleButton("yes");
3650                         }
3651                     }
3652                 });
3653                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3654                 textareaEl.enableDisplayMode();
3655                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3656                 progressEl.enableDisplayMode();
3657                 
3658                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3659                 var pf = progressEl.dom.firstChild;
3660                 if (pf) {
3661                     pp = Roo.get(pf.firstChild);
3662                     pp.setHeight(pf.offsetHeight);
3663                 }
3664                 
3665             }
3666             return dlg;
3667         },
3668
3669         /**
3670          * Updates the message box body text
3671          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3672          * the XHTML-compliant non-breaking space character '&amp;#160;')
3673          * @return {Roo.MessageBox} This message box
3674          */
3675         updateText : function(text)
3676         {
3677             if(!dlg.isVisible() && !opt.width){
3678                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3679                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3680             }
3681             msgEl.innerHTML = text || '&#160;';
3682       
3683             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3684             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3685             var w = Math.max(
3686                     Math.min(opt.width || cw , this.maxWidth), 
3687                     Math.max(opt.minWidth || this.minWidth, bwidth)
3688             );
3689             if(opt.prompt){
3690                 activeTextEl.setWidth(w);
3691             }
3692             if(dlg.isVisible()){
3693                 dlg.fixedcenter = false;
3694             }
3695             // to big, make it scroll. = But as usual stupid IE does not support
3696             // !important..
3697             
3698             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3699                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3700                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3701             } else {
3702                 bodyEl.dom.style.height = '';
3703                 bodyEl.dom.style.overflowY = '';
3704             }
3705             if (cw > w) {
3706                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3707             } else {
3708                 bodyEl.dom.style.overflowX = '';
3709             }
3710             
3711             dlg.setContentSize(w, bodyEl.getHeight());
3712             if(dlg.isVisible()){
3713                 dlg.fixedcenter = true;
3714             }
3715             return this;
3716         },
3717
3718         /**
3719          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3720          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3721          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3722          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3723          * @return {Roo.MessageBox} This message box
3724          */
3725         updateProgress : function(value, text){
3726             if(text){
3727                 this.updateText(text);
3728             }
3729             
3730             if (pp) { // weird bug on my firefox - for some reason this is not defined
3731                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3732                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3733             }
3734             return this;
3735         },        
3736
3737         /**
3738          * Returns true if the message box is currently displayed
3739          * @return {Boolean} True if the message box is visible, else false
3740          */
3741         isVisible : function(){
3742             return dlg && dlg.isVisible();  
3743         },
3744
3745         /**
3746          * Hides the message box if it is displayed
3747          */
3748         hide : function(){
3749             if(this.isVisible()){
3750                 dlg.hide();
3751             }  
3752         },
3753
3754         /**
3755          * Displays a new message box, or reinitializes an existing message box, based on the config options
3756          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3757          * The following config object properties are supported:
3758          * <pre>
3759 Property    Type             Description
3760 ----------  ---------------  ------------------------------------------------------------------------------------
3761 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3762                                    closes (defaults to undefined)
3763 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3764                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3765 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3766                                    progress and wait dialogs will ignore this property and always hide the
3767                                    close button as they can only be closed programmatically.
3768 cls               String           A custom CSS class to apply to the message box element
3769 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3770                                    displayed (defaults to 75)
3771 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3772                                    function will be btn (the name of the button that was clicked, if applicable,
3773                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3774                                    Progress and wait dialogs will ignore this option since they do not respond to
3775                                    user actions and can only be closed programmatically, so any required function
3776                                    should be called by the same code after it closes the dialog.
3777 icon              String           A CSS class that provides a background image to be used as an icon for
3778                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3779 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3780 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3781 modal             Boolean          False to allow user interaction with the page while the message box is
3782                                    displayed (defaults to true)
3783 msg               String           A string that will replace the existing message box body text (defaults
3784                                    to the XHTML-compliant non-breaking space character '&#160;')
3785 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3786 progress          Boolean          True to display a progress bar (defaults to false)
3787 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3788 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3789 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3790 title             String           The title text
3791 value             String           The string value to set into the active textbox element if displayed
3792 wait              Boolean          True to display a progress bar (defaults to false)
3793 width             Number           The width of the dialog in pixels
3794 </pre>
3795          *
3796          * Example usage:
3797          * <pre><code>
3798 Roo.Msg.show({
3799    title: 'Address',
3800    msg: 'Please enter your address:',
3801    width: 300,
3802    buttons: Roo.MessageBox.OKCANCEL,
3803    multiline: true,
3804    fn: saveAddress,
3805    animEl: 'addAddressBtn'
3806 });
3807 </code></pre>
3808          * @param {Object} config Configuration options
3809          * @return {Roo.MessageBox} This message box
3810          */
3811         show : function(options)
3812         {
3813             
3814             // this causes nightmares if you show one dialog after another
3815             // especially on callbacks..
3816              
3817             if(this.isVisible()){
3818                 
3819                 this.hide();
3820                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3821                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3822                 Roo.log("New Dialog Message:" +  options.msg )
3823                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3824                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3825                 
3826             }
3827             var d = this.getDialog();
3828             opt = options;
3829             d.setTitle(opt.title || "&#160;");
3830             d.closeEl.setDisplayed(opt.closable !== false);
3831             activeTextEl = textboxEl;
3832             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3833             if(opt.prompt){
3834                 if(opt.multiline){
3835                     textboxEl.hide();
3836                     textareaEl.show();
3837                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3838                         opt.multiline : this.defaultTextHeight);
3839                     activeTextEl = textareaEl;
3840                 }else{
3841                     textboxEl.show();
3842                     textareaEl.hide();
3843                 }
3844             }else{
3845                 textboxEl.hide();
3846                 textareaEl.hide();
3847             }
3848             progressEl.setDisplayed(opt.progress === true);
3849             if (opt.progress) {
3850                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3851             }
3852             this.updateProgress(0);
3853             activeTextEl.dom.value = opt.value || "";
3854             if(opt.prompt){
3855                 dlg.setDefaultButton(activeTextEl);
3856             }else{
3857                 var bs = opt.buttons;
3858                 var db = null;
3859                 if(bs && bs.ok){
3860                     db = buttons["ok"];
3861                 }else if(bs && bs.yes){
3862                     db = buttons["yes"];
3863                 }
3864                 dlg.setDefaultButton(db);
3865             }
3866             bwidth = updateButtons(opt.buttons);
3867             this.updateText(opt.msg);
3868             if(opt.cls){
3869                 d.el.addClass(opt.cls);
3870             }
3871             d.proxyDrag = opt.proxyDrag === true;
3872             d.modal = opt.modal !== false;
3873             d.mask = opt.modal !== false ? mask : false;
3874             if(!d.isVisible()){
3875                 // force it to the end of the z-index stack so it gets a cursor in FF
3876                 document.body.appendChild(dlg.el.dom);
3877                 d.animateTarget = null;
3878                 d.show(options.animEl);
3879             }
3880             return this;
3881         },
3882
3883         /**
3884          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3885          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3886          * and closing the message box when the process is complete.
3887          * @param {String} title The title bar text
3888          * @param {String} msg The message box body text
3889          * @return {Roo.MessageBox} This message box
3890          */
3891         progress : function(title, msg){
3892             this.show({
3893                 title : title,
3894                 msg : msg,
3895                 buttons: false,
3896                 progress:true,
3897                 closable:false,
3898                 minWidth: this.minProgressWidth,
3899                 modal : true
3900             });
3901             return this;
3902         },
3903
3904         /**
3905          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3906          * If a callback function is passed it will be called after the user clicks the button, and the
3907          * id of the button that was clicked will be passed as the only parameter to the callback
3908          * (could also be the top-right close button).
3909          * @param {String} title The title bar text
3910          * @param {String} msg The message box body text
3911          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3912          * @param {Object} scope (optional) The scope of the callback function
3913          * @return {Roo.MessageBox} This message box
3914          */
3915         alert : function(title, msg, fn, scope)
3916         {
3917             this.show({
3918                 title : title,
3919                 msg : msg,
3920                 buttons: this.OK,
3921                 fn: fn,
3922                 closable : false,
3923                 scope : scope,
3924                 modal : true
3925             });
3926             return this;
3927         },
3928
3929         /**
3930          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3931          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3932          * You are responsible for closing the message box when the process is complete.
3933          * @param {String} msg The message box body text
3934          * @param {String} title (optional) The title bar text
3935          * @return {Roo.MessageBox} This message box
3936          */
3937         wait : function(msg, title){
3938             this.show({
3939                 title : title,
3940                 msg : msg,
3941                 buttons: false,
3942                 closable:false,
3943                 progress:true,
3944                 modal:true,
3945                 width:300,
3946                 wait:true
3947             });
3948             waitTimer = Roo.TaskMgr.start({
3949                 run: function(i){
3950                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3951                 },
3952                 interval: 1000
3953             });
3954             return this;
3955         },
3956
3957         /**
3958          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3959          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3960          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3961          * @param {String} title The title bar text
3962          * @param {String} msg The message box body text
3963          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3964          * @param {Object} scope (optional) The scope of the callback function
3965          * @return {Roo.MessageBox} This message box
3966          */
3967         confirm : function(title, msg, fn, scope){
3968             this.show({
3969                 title : title,
3970                 msg : msg,
3971                 buttons: this.YESNO,
3972                 fn: fn,
3973                 scope : scope,
3974                 modal : true
3975             });
3976             return this;
3977         },
3978
3979         /**
3980          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3981          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3982          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3983          * (could also be the top-right close button) and the text that was entered will be passed as the two
3984          * parameters to the callback.
3985          * @param {String} title The title bar text
3986          * @param {String} msg The message box body text
3987          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3988          * @param {Object} scope (optional) The scope of the callback function
3989          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3990          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3991          * @return {Roo.MessageBox} This message box
3992          */
3993         prompt : function(title, msg, fn, scope, multiline){
3994             this.show({
3995                 title : title,
3996                 msg : msg,
3997                 buttons: this.OKCANCEL,
3998                 fn: fn,
3999                 minWidth:250,
4000                 scope : scope,
4001                 prompt:true,
4002                 multiline: multiline,
4003                 modal : true
4004             });
4005             return this;
4006         },
4007
4008         /**
4009          * Button config that displays a single OK button
4010          * @type Object
4011          */
4012         OK : {ok:true},
4013         /**
4014          * Button config that displays Yes and No buttons
4015          * @type Object
4016          */
4017         YESNO : {yes:true, no:true},
4018         /**
4019          * Button config that displays OK and Cancel buttons
4020          * @type Object
4021          */
4022         OKCANCEL : {ok:true, cancel:true},
4023         /**
4024          * Button config that displays Yes, No and Cancel buttons
4025          * @type Object
4026          */
4027         YESNOCANCEL : {yes:true, no:true, cancel:true},
4028
4029         /**
4030          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
4031          * @type Number
4032          */
4033         defaultTextHeight : 75,
4034         /**
4035          * The maximum width in pixels of the message box (defaults to 600)
4036          * @type Number
4037          */
4038         maxWidth : 600,
4039         /**
4040          * The minimum width in pixels of the message box (defaults to 100)
4041          * @type Number
4042          */
4043         minWidth : 100,
4044         /**
4045          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
4046          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
4047          * @type Number
4048          */
4049         minProgressWidth : 250,
4050         /**
4051          * An object containing the default button text strings that can be overriden for localized language support.
4052          * Supported properties are: ok, cancel, yes and no.
4053          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
4054          * @type Object
4055          */
4056         buttonText : {
4057             ok : "OK",
4058             cancel : "Cancel",
4059             yes : "Yes",
4060             no : "No"
4061         }
4062     };
4063 }();
4064
4065 /**
4066  * Shorthand for {@link Roo.MessageBox}
4067  */
4068 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
4069 Roo.Msg = Roo.Msg || Roo.MessageBox;
4070 /*
4071  * - LGPL
4072  *
4073  * navbar
4074  * 
4075  */
4076
4077 /**
4078  * @class Roo.bootstrap.Navbar
4079  * @extends Roo.bootstrap.Component
4080  * Bootstrap Navbar class
4081
4082  * @constructor
4083  * Create a new Navbar
4084  * @param {Object} config The config object
4085  */
4086
4087
4088 Roo.bootstrap.Navbar = function(config){
4089     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
4090     this.addEvents({
4091         // raw events
4092         /**
4093          * @event beforetoggle
4094          * Fire before toggle the menu
4095          * @param {Roo.EventObject} e
4096          */
4097         "beforetoggle" : true
4098     });
4099 };
4100
4101 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
4102     
4103     
4104    
4105     // private
4106     navItems : false,
4107     loadMask : false,
4108     
4109     
4110     getAutoCreate : function(){
4111         
4112         
4113         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
4114         
4115     },
4116     
4117     initEvents :function ()
4118     {
4119         //Roo.log(this.el.select('.navbar-toggle',true));
4120         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
4121         
4122         var mark = {
4123             tag: "div",
4124             cls:"x-dlg-mask"
4125         };
4126         
4127         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
4128         
4129         var size = this.el.getSize();
4130         this.maskEl.setSize(size.width, size.height);
4131         this.maskEl.enableDisplayMode("block");
4132         this.maskEl.hide();
4133         
4134         if(this.loadMask){
4135             this.maskEl.show();
4136         }
4137     },
4138     
4139     
4140     getChildContainer : function()
4141     {
4142         if (this.el && this.el.select('.collapse').getCount()) {
4143             return this.el.select('.collapse',true).first();
4144         }
4145         
4146         return this.el;
4147     },
4148     
4149     mask : function()
4150     {
4151         this.maskEl.show();
4152     },
4153     
4154     unmask : function()
4155     {
4156         this.maskEl.hide();
4157     },
4158     onToggle : function()
4159     {
4160         
4161         if(this.fireEvent('beforetoggle', this) === false){
4162             return;
4163         }
4164         var ce = this.el.select('.navbar-collapse',true).first();
4165       
4166         if (!ce.hasClass('show')) {
4167            this.expand();
4168         } else {
4169             this.collapse();
4170         }
4171         
4172         
4173     
4174     },
4175     /**
4176      * Expand the navbar pulldown 
4177      */
4178     expand : function ()
4179     {
4180        
4181         var ce = this.el.select('.navbar-collapse',true).first();
4182         if (ce.hasClass('collapsing')) {
4183             return;
4184         }
4185         ce.dom.style.height = '';
4186                // show it...
4187         ce.addClass('in'); // old...
4188         ce.removeClass('collapse');
4189         ce.addClass('show');
4190         var h = ce.getHeight();
4191         Roo.log(h);
4192         ce.removeClass('show');
4193         // at this point we should be able to see it..
4194         ce.addClass('collapsing');
4195         
4196         ce.setHeight(0); // resize it ...
4197         ce.on('transitionend', function() {
4198             //Roo.log('done transition');
4199             ce.removeClass('collapsing');
4200             ce.addClass('show');
4201             ce.removeClass('collapse');
4202
4203             ce.dom.style.height = '';
4204         }, this, { single: true} );
4205         ce.setHeight(h);
4206         ce.dom.scrollTop = 0;
4207     },
4208     /**
4209      * Collapse the navbar pulldown 
4210      */
4211     collapse : function()
4212     {
4213          var ce = this.el.select('.navbar-collapse',true).first();
4214        
4215         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
4216             // it's collapsed or collapsing..
4217             return;
4218         }
4219         ce.removeClass('in'); // old...
4220         ce.setHeight(ce.getHeight());
4221         ce.removeClass('show');
4222         ce.addClass('collapsing');
4223         
4224         ce.on('transitionend', function() {
4225             ce.dom.style.height = '';
4226             ce.removeClass('collapsing');
4227             ce.addClass('collapse');
4228         }, this, { single: true} );
4229         ce.setHeight(0);
4230     }
4231     
4232     
4233     
4234 });
4235
4236
4237
4238  
4239
4240  /*
4241  * - LGPL
4242  *
4243  * navbar
4244  * 
4245  */
4246
4247 /**
4248  * @class Roo.bootstrap.NavSimplebar
4249  * @extends Roo.bootstrap.Navbar
4250  * Bootstrap Sidebar class
4251  *
4252  * @cfg {Boolean} inverse is inverted color
4253  * 
4254  * @cfg {String} type (nav | pills | tabs)
4255  * @cfg {Boolean} arrangement stacked | justified
4256  * @cfg {String} align (left | right) alignment
4257  * 
4258  * @cfg {Boolean} main (true|false) main nav bar? default false
4259  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
4260  * 
4261  * @cfg {String} tag (header|footer|nav|div) default is nav 
4262
4263  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4264  * 
4265  * 
4266  * @constructor
4267  * Create a new Sidebar
4268  * @param {Object} config The config object
4269  */
4270
4271
4272 Roo.bootstrap.NavSimplebar = function(config){
4273     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4274 };
4275
4276 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
4277     
4278     inverse: false,
4279     
4280     type: false,
4281     arrangement: '',
4282     align : false,
4283     
4284     weight : 'light',
4285     
4286     main : false,
4287     
4288     
4289     tag : false,
4290     
4291     
4292     getAutoCreate : function(){
4293         
4294         
4295         var cfg = {
4296             tag : this.tag || 'div',
4297             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
4298         };
4299         if (['light','white'].indexOf(this.weight) > -1) {
4300             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4301         }
4302         cfg.cls += ' bg-' + this.weight;
4303         
4304         if (this.inverse) {
4305             cfg.cls += ' navbar-inverse';
4306             
4307         }
4308         
4309         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4310         
4311         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
4312             return cfg;
4313         }
4314         
4315         
4316     
4317         
4318         cfg.cn = [
4319             {
4320                 cls: 'nav nav-' + this.xtype,
4321                 tag : 'ul'
4322             }
4323         ];
4324         
4325          
4326         this.type = this.type || 'nav';
4327         if (['tabs','pills'].indexOf(this.type) != -1) {
4328             cfg.cn[0].cls += ' nav-' + this.type
4329         
4330         
4331         } else {
4332             if (this.type!=='nav') {
4333                 Roo.log('nav type must be nav/tabs/pills')
4334             }
4335             cfg.cn[0].cls += ' navbar-nav'
4336         }
4337         
4338         
4339         
4340         
4341         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4342             cfg.cn[0].cls += ' nav-' + this.arrangement;
4343         }
4344         
4345         
4346         if (this.align === 'right') {
4347             cfg.cn[0].cls += ' navbar-right';
4348         }
4349         
4350         
4351         
4352         
4353         return cfg;
4354     
4355         
4356     }
4357     
4358     
4359     
4360 });
4361
4362
4363
4364  
4365
4366  
4367        /*
4368  * - LGPL
4369  *
4370  * navbar
4371  * navbar-fixed-top
4372  * navbar-expand-md  fixed-top 
4373  */
4374
4375 /**
4376  * @class Roo.bootstrap.NavHeaderbar
4377  * @extends Roo.bootstrap.NavSimplebar
4378  * Bootstrap Sidebar class
4379  *
4380  * @cfg {String} brand what is brand
4381  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4382  * @cfg {String} brand_href href of the brand
4383  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4384  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4385  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4386  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4387  * 
4388  * @constructor
4389  * Create a new Sidebar
4390  * @param {Object} config The config object
4391  */
4392
4393
4394 Roo.bootstrap.NavHeaderbar = function(config){
4395     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4396       
4397 };
4398
4399 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4400     
4401     position: '',
4402     brand: '',
4403     brand_href: false,
4404     srButton : true,
4405     autohide : false,
4406     desktopCenter : false,
4407    
4408     
4409     getAutoCreate : function(){
4410         
4411         var   cfg = {
4412             tag: this.nav || 'nav',
4413             cls: 'navbar navbar-expand-md',
4414             role: 'navigation',
4415             cn: []
4416         };
4417         
4418         var cn = cfg.cn;
4419         if (this.desktopCenter) {
4420             cn.push({cls : 'container', cn : []});
4421             cn = cn[0].cn;
4422         }
4423         
4424         if(this.srButton){
4425             var btn = {
4426                 tag: 'button',
4427                 type: 'button',
4428                 cls: 'navbar-toggle navbar-toggler',
4429                 'data-toggle': 'collapse',
4430                 cn: [
4431                     {
4432                         tag: 'span',
4433                         cls: 'sr-only',
4434                         html: 'Toggle navigation'
4435                     },
4436                     {
4437                         tag: 'span',
4438                         cls: 'icon-bar navbar-toggler-icon'
4439                     },
4440                     {
4441                         tag: 'span',
4442                         cls: 'icon-bar'
4443                     },
4444                     {
4445                         tag: 'span',
4446                         cls: 'icon-bar'
4447                     }
4448                 ]
4449             };
4450             
4451             cn.push( Roo.bootstrap.version == 4 ? btn : {
4452                 tag: 'div',
4453                 cls: 'navbar-header',
4454                 cn: [
4455                     btn
4456                 ]
4457             });
4458         }
4459         
4460         cn.push({
4461             tag: 'div',
4462             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
4463             cn : []
4464         });
4465         
4466         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4467         
4468         if (['light','white'].indexOf(this.weight) > -1) {
4469             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4470         }
4471         cfg.cls += ' bg-' + this.weight;
4472         
4473         
4474         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4475             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4476             
4477             // tag can override this..
4478             
4479             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4480         }
4481         
4482         if (this.brand !== '') {
4483             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4484             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4485                 tag: 'a',
4486                 href: this.brand_href ? this.brand_href : '#',
4487                 cls: 'navbar-brand',
4488                 cn: [
4489                 this.brand
4490                 ]
4491             });
4492         }
4493         
4494         if(this.main){
4495             cfg.cls += ' main-nav';
4496         }
4497         
4498         
4499         return cfg;
4500
4501         
4502     },
4503     getHeaderChildContainer : function()
4504     {
4505         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4506             return this.el.select('.navbar-header',true).first();
4507         }
4508         
4509         return this.getChildContainer();
4510     },
4511     
4512     
4513     initEvents : function()
4514     {
4515         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4516         
4517         if (this.autohide) {
4518             
4519             var prevScroll = 0;
4520             var ft = this.el;
4521             
4522             Roo.get(document).on('scroll',function(e) {
4523                 var ns = Roo.get(document).getScroll().top;
4524                 var os = prevScroll;
4525                 prevScroll = ns;
4526                 
4527                 if(ns > os){
4528                     ft.removeClass('slideDown');
4529                     ft.addClass('slideUp');
4530                     return;
4531                 }
4532                 ft.removeClass('slideUp');
4533                 ft.addClass('slideDown');
4534                  
4535               
4536           },this);
4537         }
4538     }    
4539     
4540 });
4541
4542
4543
4544  
4545
4546  /*
4547  * - LGPL
4548  *
4549  * navbar
4550  * 
4551  */
4552
4553 /**
4554  * @class Roo.bootstrap.NavSidebar
4555  * @extends Roo.bootstrap.Navbar
4556  * Bootstrap Sidebar class
4557  * 
4558  * @constructor
4559  * Create a new Sidebar
4560  * @param {Object} config The config object
4561  */
4562
4563
4564 Roo.bootstrap.NavSidebar = function(config){
4565     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4566 };
4567
4568 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4569     
4570     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4571     
4572     getAutoCreate : function(){
4573         
4574         
4575         return  {
4576             tag: 'div',
4577             cls: 'sidebar sidebar-nav'
4578         };
4579     
4580         
4581     }
4582     
4583     
4584     
4585 });
4586
4587
4588
4589  
4590
4591  /*
4592  * - LGPL
4593  *
4594  * nav group
4595  * 
4596  */
4597
4598 /**
4599  * @class Roo.bootstrap.NavGroup
4600  * @extends Roo.bootstrap.Component
4601  * Bootstrap NavGroup class
4602  * @cfg {String} align (left|right)
4603  * @cfg {Boolean} inverse
4604  * @cfg {String} type (nav|pills|tab) default nav
4605  * @cfg {String} navId - reference Id for navbar.
4606
4607  * 
4608  * @constructor
4609  * Create a new nav group
4610  * @param {Object} config The config object
4611  */
4612
4613 Roo.bootstrap.NavGroup = function(config){
4614     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4615     this.navItems = [];
4616    
4617     Roo.bootstrap.NavGroup.register(this);
4618      this.addEvents({
4619         /**
4620              * @event changed
4621              * Fires when the active item changes
4622              * @param {Roo.bootstrap.NavGroup} this
4623              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4624              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4625          */
4626         'changed': true
4627      });
4628     
4629 };
4630
4631 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4632     
4633     align: '',
4634     inverse: false,
4635     form: false,
4636     type: 'nav',
4637     navId : '',
4638     // private
4639     
4640     navItems : false, 
4641     
4642     getAutoCreate : function()
4643     {
4644         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4645         
4646         cfg = {
4647             tag : 'ul',
4648             cls: 'nav' 
4649         };
4650         if (Roo.bootstrap.version == 4) {
4651             if (['tabs','pills'].indexOf(this.type) != -1) {
4652                 cfg.cls += ' nav-' + this.type; 
4653             } else {
4654                 // trying to remove so header bar can right align top?
4655                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
4656                     // do not use on header bar... 
4657                     cfg.cls += ' navbar-nav';
4658                 }
4659             }
4660             
4661         } else {
4662             if (['tabs','pills'].indexOf(this.type) != -1) {
4663                 cfg.cls += ' nav-' + this.type
4664             } else {
4665                 if (this.type !== 'nav') {
4666                     Roo.log('nav type must be nav/tabs/pills')
4667                 }
4668                 cfg.cls += ' navbar-nav'
4669             }
4670         }
4671         
4672         if (this.parent() && this.parent().sidebar) {
4673             cfg = {
4674                 tag: 'ul',
4675                 cls: 'dashboard-menu sidebar-menu'
4676             };
4677             
4678             return cfg;
4679         }
4680         
4681         if (this.form === true) {
4682             cfg = {
4683                 tag: 'form',
4684                 cls: 'navbar-form form-inline'
4685             };
4686             //nav navbar-right ml-md-auto
4687             if (this.align === 'right') {
4688                 cfg.cls += ' navbar-right ml-md-auto';
4689             } else {
4690                 cfg.cls += ' navbar-left';
4691             }
4692         }
4693         
4694         if (this.align === 'right') {
4695             cfg.cls += ' navbar-right ml-md-auto';
4696         } else {
4697             cfg.cls += ' mr-auto';
4698         }
4699         
4700         if (this.inverse) {
4701             cfg.cls += ' navbar-inverse';
4702             
4703         }
4704         
4705         
4706         return cfg;
4707     },
4708     /**
4709     * sets the active Navigation item
4710     * @param {Roo.bootstrap.NavItem} the new current navitem
4711     */
4712     setActiveItem : function(item)
4713     {
4714         var prev = false;
4715         Roo.each(this.navItems, function(v){
4716             if (v == item) {
4717                 return ;
4718             }
4719             if (v.isActive()) {
4720                 v.setActive(false, true);
4721                 prev = v;
4722                 
4723             }
4724             
4725         });
4726
4727         item.setActive(true, true);
4728         this.fireEvent('changed', this, item, prev);
4729         
4730         
4731     },
4732     /**
4733     * gets the active Navigation item
4734     * @return {Roo.bootstrap.NavItem} the current navitem
4735     */
4736     getActive : function()
4737     {
4738         
4739         var prev = false;
4740         Roo.each(this.navItems, function(v){
4741             
4742             if (v.isActive()) {
4743                 prev = v;
4744                 
4745             }
4746             
4747         });
4748         return prev;
4749     },
4750     
4751     indexOfNav : function()
4752     {
4753         
4754         var prev = false;
4755         Roo.each(this.navItems, function(v,i){
4756             
4757             if (v.isActive()) {
4758                 prev = i;
4759                 
4760             }
4761             
4762         });
4763         return prev;
4764     },
4765     /**
4766     * adds a Navigation item
4767     * @param {Roo.bootstrap.NavItem} the navitem to add
4768     */
4769     addItem : function(cfg)
4770     {
4771         if (this.form && Roo.bootstrap.version == 4) {
4772             cfg.tag = 'div';
4773         }
4774         var cn = new Roo.bootstrap.NavItem(cfg);
4775         this.register(cn);
4776         cn.parentId = this.id;
4777         cn.onRender(this.el, null);
4778         return cn;
4779     },
4780     /**
4781     * register a Navigation item
4782     * @param {Roo.bootstrap.NavItem} the navitem to add
4783     */
4784     register : function(item)
4785     {
4786         this.navItems.push( item);
4787         item.navId = this.navId;
4788     
4789     },
4790     
4791     /**
4792     * clear all the Navigation item
4793     */
4794    
4795     clearAll : function()
4796     {
4797         this.navItems = [];
4798         this.el.dom.innerHTML = '';
4799     },
4800     
4801     getNavItem: function(tabId)
4802     {
4803         var ret = false;
4804         Roo.each(this.navItems, function(e) {
4805             if (e.tabId == tabId) {
4806                ret =  e;
4807                return false;
4808             }
4809             return true;
4810             
4811         });
4812         return ret;
4813     },
4814     
4815     setActiveNext : function()
4816     {
4817         var i = this.indexOfNav(this.getActive());
4818         if (i > this.navItems.length) {
4819             return;
4820         }
4821         this.setActiveItem(this.navItems[i+1]);
4822     },
4823     setActivePrev : function()
4824     {
4825         var i = this.indexOfNav(this.getActive());
4826         if (i  < 1) {
4827             return;
4828         }
4829         this.setActiveItem(this.navItems[i-1]);
4830     },
4831     clearWasActive : function(except) {
4832         Roo.each(this.navItems, function(e) {
4833             if (e.tabId != except.tabId && e.was_active) {
4834                e.was_active = false;
4835                return false;
4836             }
4837             return true;
4838             
4839         });
4840     },
4841     getWasActive : function ()
4842     {
4843         var r = false;
4844         Roo.each(this.navItems, function(e) {
4845             if (e.was_active) {
4846                r = e;
4847                return false;
4848             }
4849             return true;
4850             
4851         });
4852         return r;
4853     }
4854     
4855     
4856 });
4857
4858  
4859 Roo.apply(Roo.bootstrap.NavGroup, {
4860     
4861     groups: {},
4862      /**
4863     * register a Navigation Group
4864     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4865     */
4866     register : function(navgrp)
4867     {
4868         this.groups[navgrp.navId] = navgrp;
4869         
4870     },
4871     /**
4872     * fetch a Navigation Group based on the navigation ID
4873     * @param {string} the navgroup to add
4874     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4875     */
4876     get: function(navId) {
4877         if (typeof(this.groups[navId]) == 'undefined') {
4878             return false;
4879             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4880         }
4881         return this.groups[navId] ;
4882     }
4883     
4884     
4885     
4886 });
4887
4888  /*
4889  * - LGPL
4890  *
4891  * row
4892  * 
4893  */
4894
4895 /**
4896  * @class Roo.bootstrap.NavItem
4897  * @extends Roo.bootstrap.Component
4898  * Bootstrap Navbar.NavItem class
4899  * @cfg {String} href  link to
4900  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
4901
4902  * @cfg {String} html content of button
4903  * @cfg {String} badge text inside badge
4904  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4905  * @cfg {String} glyphicon DEPRICATED - use fa
4906  * @cfg {String} icon DEPRICATED - use fa
4907  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4908  * @cfg {Boolean} active Is item active
4909  * @cfg {Boolean} disabled Is item disabled
4910  
4911  * @cfg {Boolean} preventDefault (true | false) default false
4912  * @cfg {String} tabId the tab that this item activates.
4913  * @cfg {String} tagtype (a|span) render as a href or span?
4914  * @cfg {Boolean} animateRef (true|false) link to element default false  
4915   
4916  * @constructor
4917  * Create a new Navbar Item
4918  * @param {Object} config The config object
4919  */
4920 Roo.bootstrap.NavItem = function(config){
4921     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4922     this.addEvents({
4923         // raw events
4924         /**
4925          * @event click
4926          * The raw click event for the entire grid.
4927          * @param {Roo.EventObject} e
4928          */
4929         "click" : true,
4930          /**
4931             * @event changed
4932             * Fires when the active item active state changes
4933             * @param {Roo.bootstrap.NavItem} this
4934             * @param {boolean} state the new state
4935              
4936          */
4937         'changed': true,
4938         /**
4939             * @event scrollto
4940             * Fires when scroll to element
4941             * @param {Roo.bootstrap.NavItem} this
4942             * @param {Object} options
4943             * @param {Roo.EventObject} e
4944              
4945          */
4946         'scrollto': true
4947     });
4948    
4949 };
4950
4951 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4952     
4953     href: false,
4954     html: '',
4955     badge: '',
4956     icon: false,
4957     fa : false,
4958     glyphicon: false,
4959     active: false,
4960     preventDefault : false,
4961     tabId : false,
4962     tagtype : 'a',
4963     tag: 'li',
4964     disabled : false,
4965     animateRef : false,
4966     was_active : false,
4967     button_weight : '',
4968     button_outline : false,
4969     
4970     navLink: false,
4971     
4972     getAutoCreate : function(){
4973          
4974         var cfg = {
4975             tag: this.tag,
4976             cls: 'nav-item'
4977         };
4978         
4979         if (this.active) {
4980             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4981         }
4982         if (this.disabled) {
4983             cfg.cls += ' disabled';
4984         }
4985         
4986         // BS4 only?
4987         if (this.button_weight.length) {
4988             cfg.tag = this.href ? 'a' : 'button';
4989             cfg.html = this.html || '';
4990             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
4991             if (this.href) {
4992                 cfg.href = this.href;
4993             }
4994             if (this.fa) {
4995                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
4996             }
4997             
4998             // menu .. should add dropdown-menu class - so no need for carat..
4999             
5000             if (this.badge !== '') {
5001                  
5002                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5003             }
5004             return cfg;
5005         }
5006         
5007         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
5008             cfg.cn = [
5009                 {
5010                     tag: this.tagtype,
5011                     href : this.href || "#",
5012                     html: this.html || ''
5013                 }
5014             ];
5015             if (this.tagtype == 'a') {
5016                 cfg.cn[0].cls = 'nav-link';
5017             }
5018             if (this.icon) {
5019                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
5020             }
5021             if (this.fa) {
5022                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
5023             }
5024             if(this.glyphicon) {
5025                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
5026             }
5027             
5028             if (this.menu) {
5029                 
5030                 cfg.cn[0].html += " <span class='caret'></span>";
5031              
5032             }
5033             
5034             if (this.badge !== '') {
5035                  
5036                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5037             }
5038         }
5039         
5040         
5041         
5042         return cfg;
5043     },
5044     onRender : function(ct, position)
5045     {
5046        // Roo.log("Call onRender: " + this.xtype);
5047         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
5048             this.tag = 'div';
5049         }
5050         
5051         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
5052         this.navLink = this.el.select('.nav-link',true).first();
5053         return ret;
5054     },
5055       
5056     
5057     initEvents: function() 
5058     {
5059         if (typeof (this.menu) != 'undefined') {
5060             this.menu.parentType = this.xtype;
5061             this.menu.triggerEl = this.el;
5062             this.menu = this.addxtype(Roo.apply({}, this.menu));
5063         }
5064         
5065         this.el.select('a',true).on('click', this.onClick, this);
5066         
5067         if(this.tagtype == 'span'){
5068             this.el.select('span',true).on('click', this.onClick, this);
5069         }
5070        
5071         // at this point parent should be available..
5072         this.parent().register(this);
5073     },
5074     
5075     onClick : function(e)
5076     {
5077         if (e.getTarget('.dropdown-menu-item')) {
5078             // did you click on a menu itemm.... - then don't trigger onclick..
5079             return;
5080         }
5081         
5082         if(
5083                 this.preventDefault || 
5084                 this.href == '#' 
5085         ){
5086             Roo.log("NavItem - prevent Default?");
5087             e.preventDefault();
5088         }
5089         
5090         if (this.disabled) {
5091             return;
5092         }
5093         
5094         var tg = Roo.bootstrap.TabGroup.get(this.navId);
5095         if (tg && tg.transition) {
5096             Roo.log("waiting for the transitionend");
5097             return;
5098         }
5099         
5100         
5101         
5102         //Roo.log("fire event clicked");
5103         if(this.fireEvent('click', this, e) === false){
5104             return;
5105         };
5106         
5107         if(this.tagtype == 'span'){
5108             return;
5109         }
5110         
5111         //Roo.log(this.href);
5112         var ael = this.el.select('a',true).first();
5113         //Roo.log(ael);
5114         
5115         if(ael && this.animateRef && this.href.indexOf('#') > -1){
5116             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
5117             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
5118                 return; // ignore... - it's a 'hash' to another page.
5119             }
5120             Roo.log("NavItem - prevent Default?");
5121             e.preventDefault();
5122             this.scrollToElement(e);
5123         }
5124         
5125         
5126         var p =  this.parent();
5127    
5128         if (['tabs','pills'].indexOf(p.type)!==-1) {
5129             if (typeof(p.setActiveItem) !== 'undefined') {
5130                 p.setActiveItem(this);
5131             }
5132         }
5133         
5134         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
5135         if (p.parentType == 'NavHeaderbar' && !this.menu) {
5136             // remove the collapsed menu expand...
5137             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
5138         }
5139     },
5140     
5141     isActive: function () {
5142         return this.active
5143     },
5144     setActive : function(state, fire, is_was_active)
5145     {
5146         if (this.active && !state && this.navId) {
5147             this.was_active = true;
5148             var nv = Roo.bootstrap.NavGroup.get(this.navId);
5149             if (nv) {
5150                 nv.clearWasActive(this);
5151             }
5152             
5153         }
5154         this.active = state;
5155         
5156         if (!state ) {
5157             this.el.removeClass('active');
5158             this.navLink ? this.navLink.removeClass('active') : false;
5159         } else if (!this.el.hasClass('active')) {
5160             
5161             this.el.addClass('active');
5162             if (Roo.bootstrap.version == 4 && this.navLink ) {
5163                 this.navLink.addClass('active');
5164             }
5165             
5166         }
5167         if (fire) {
5168             this.fireEvent('changed', this, state);
5169         }
5170         
5171         // show a panel if it's registered and related..
5172         
5173         if (!this.navId || !this.tabId || !state || is_was_active) {
5174             return;
5175         }
5176         
5177         var tg = Roo.bootstrap.TabGroup.get(this.navId);
5178         if (!tg) {
5179             return;
5180         }
5181         var pan = tg.getPanelByName(this.tabId);
5182         if (!pan) {
5183             return;
5184         }
5185         // if we can not flip to new panel - go back to old nav highlight..
5186         if (false == tg.showPanel(pan)) {
5187             var nv = Roo.bootstrap.NavGroup.get(this.navId);
5188             if (nv) {
5189                 var onav = nv.getWasActive();
5190                 if (onav) {
5191                     onav.setActive(true, false, true);
5192                 }
5193             }
5194             
5195         }
5196         
5197         
5198         
5199     },
5200      // this should not be here...
5201     setDisabled : function(state)
5202     {
5203         this.disabled = state;
5204         if (!state ) {
5205             this.el.removeClass('disabled');
5206         } else if (!this.el.hasClass('disabled')) {
5207             this.el.addClass('disabled');
5208         }
5209         
5210     },
5211     
5212     /**
5213      * Fetch the element to display the tooltip on.
5214      * @return {Roo.Element} defaults to this.el
5215      */
5216     tooltipEl : function()
5217     {
5218         return this.el.select('' + this.tagtype + '', true).first();
5219     },
5220     
5221     scrollToElement : function(e)
5222     {
5223         var c = document.body;
5224         
5225         /*
5226          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
5227          */
5228         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
5229             c = document.documentElement;
5230         }
5231         
5232         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
5233         
5234         if(!target){
5235             return;
5236         }
5237
5238         var o = target.calcOffsetsTo(c);
5239         
5240         var options = {
5241             target : target,
5242             value : o[1]
5243         };
5244         
5245         this.fireEvent('scrollto', this, options, e);
5246         
5247         Roo.get(c).scrollTo('top', options.value, true);
5248         
5249         return;
5250     }
5251 });
5252  
5253
5254  /*
5255  * - LGPL
5256  *
5257  * sidebar item
5258  *
5259  *  li
5260  *    <span> icon </span>
5261  *    <span> text </span>
5262  *    <span>badge </span>
5263  */
5264
5265 /**
5266  * @class Roo.bootstrap.NavSidebarItem
5267  * @extends Roo.bootstrap.NavItem
5268  * Bootstrap Navbar.NavSidebarItem class
5269  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
5270  * {Boolean} open is the menu open
5271  * {Boolean} buttonView use button as the tigger el rather that a (default false)
5272  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5273  * {String} buttonSize (sm|md|lg)the extra classes for the button
5274  * {Boolean} showArrow show arrow next to the text (default true)
5275  * @constructor
5276  * Create a new Navbar Button
5277  * @param {Object} config The config object
5278  */
5279 Roo.bootstrap.NavSidebarItem = function(config){
5280     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5281     this.addEvents({
5282         // raw events
5283         /**
5284          * @event click
5285          * The raw click event for the entire grid.
5286          * @param {Roo.EventObject} e
5287          */
5288         "click" : true,
5289          /**
5290             * @event changed
5291             * Fires when the active item active state changes
5292             * @param {Roo.bootstrap.NavSidebarItem} this
5293             * @param {boolean} state the new state
5294              
5295          */
5296         'changed': true
5297     });
5298    
5299 };
5300
5301 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
5302     
5303     badgeWeight : 'default',
5304     
5305     open: false,
5306     
5307     buttonView : false,
5308     
5309     buttonWeight : 'default',
5310     
5311     buttonSize : 'md',
5312     
5313     showArrow : true,
5314     
5315     getAutoCreate : function(){
5316         
5317         
5318         var a = {
5319                 tag: 'a',
5320                 href : this.href || '#',
5321                 cls: '',
5322                 html : '',
5323                 cn : []
5324         };
5325         
5326         if(this.buttonView){
5327             a = {
5328                 tag: 'button',
5329                 href : this.href || '#',
5330                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5331                 html : this.html,
5332                 cn : []
5333             };
5334         }
5335         
5336         var cfg = {
5337             tag: 'li',
5338             cls: '',
5339             cn: [ a ]
5340         };
5341         
5342         if (this.active) {
5343             cfg.cls += ' active';
5344         }
5345         
5346         if (this.disabled) {
5347             cfg.cls += ' disabled';
5348         }
5349         if (this.open) {
5350             cfg.cls += ' open x-open';
5351         }
5352         // left icon..
5353         if (this.glyphicon || this.icon) {
5354             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5355             a.cn.push({ tag : 'i', cls : c }) ;
5356         }
5357         
5358         if(!this.buttonView){
5359             var span = {
5360                 tag: 'span',
5361                 html : this.html || ''
5362             };
5363
5364             a.cn.push(span);
5365             
5366         }
5367         
5368         if (this.badge !== '') {
5369             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5370         }
5371         
5372         if (this.menu) {
5373             
5374             if(this.showArrow){
5375                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5376             }
5377             
5378             a.cls += ' dropdown-toggle treeview' ;
5379         }
5380         
5381         return cfg;
5382     },
5383     
5384     initEvents : function()
5385     { 
5386         if (typeof (this.menu) != 'undefined') {
5387             this.menu.parentType = this.xtype;
5388             this.menu.triggerEl = this.el;
5389             this.menu = this.addxtype(Roo.apply({}, this.menu));
5390         }
5391         
5392         this.el.on('click', this.onClick, this);
5393         
5394         if(this.badge !== ''){
5395             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5396         }
5397         
5398     },
5399     
5400     onClick : function(e)
5401     {
5402         if(this.disabled){
5403             e.preventDefault();
5404             return;
5405         }
5406         
5407         if(this.preventDefault){
5408             e.preventDefault();
5409         }
5410         
5411         this.fireEvent('click', this, e);
5412     },
5413     
5414     disable : function()
5415     {
5416         this.setDisabled(true);
5417     },
5418     
5419     enable : function()
5420     {
5421         this.setDisabled(false);
5422     },
5423     
5424     setDisabled : function(state)
5425     {
5426         if(this.disabled == state){
5427             return;
5428         }
5429         
5430         this.disabled = state;
5431         
5432         if (state) {
5433             this.el.addClass('disabled');
5434             return;
5435         }
5436         
5437         this.el.removeClass('disabled');
5438         
5439         return;
5440     },
5441     
5442     setActive : function(state)
5443     {
5444         if(this.active == state){
5445             return;
5446         }
5447         
5448         this.active = state;
5449         
5450         if (state) {
5451             this.el.addClass('active');
5452             return;
5453         }
5454         
5455         this.el.removeClass('active');
5456         
5457         return;
5458     },
5459     
5460     isActive: function () 
5461     {
5462         return this.active;
5463     },
5464     
5465     setBadge : function(str)
5466     {
5467         if(!this.badgeEl){
5468             return;
5469         }
5470         
5471         this.badgeEl.dom.innerHTML = str;
5472     }
5473     
5474    
5475      
5476  
5477 });
5478  
5479
5480  /*
5481  * - LGPL
5482  *
5483  * row
5484  * 
5485  */
5486
5487 /**
5488  * @class Roo.bootstrap.Row
5489  * @extends Roo.bootstrap.Component
5490  * Bootstrap Row class (contains columns...)
5491  * 
5492  * @constructor
5493  * Create a new Row
5494  * @param {Object} config The config object
5495  */
5496
5497 Roo.bootstrap.Row = function(config){
5498     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5499 };
5500
5501 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5502     
5503     getAutoCreate : function(){
5504        return {
5505             cls: 'row clearfix'
5506        };
5507     }
5508     
5509     
5510 });
5511
5512  
5513
5514  /*
5515  * - LGPL
5516  *
5517  * element
5518  * 
5519  */
5520
5521 /**
5522  * @class Roo.bootstrap.Element
5523  * @extends Roo.bootstrap.Component
5524  * Bootstrap Element class
5525  * @cfg {String} html contents of the element
5526  * @cfg {String} tag tag of the element
5527  * @cfg {String} cls class of the element
5528  * @cfg {Boolean} preventDefault (true|false) default false
5529  * @cfg {Boolean} clickable (true|false) default false
5530  * 
5531  * @constructor
5532  * Create a new Element
5533  * @param {Object} config The config object
5534  */
5535
5536 Roo.bootstrap.Element = function(config){
5537     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5538     
5539     this.addEvents({
5540         // raw events
5541         /**
5542          * @event click
5543          * When a element is chick
5544          * @param {Roo.bootstrap.Element} this
5545          * @param {Roo.EventObject} e
5546          */
5547         "click" : true
5548     });
5549 };
5550
5551 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5552     
5553     tag: 'div',
5554     cls: '',
5555     html: '',
5556     preventDefault: false, 
5557     clickable: false,
5558     
5559     getAutoCreate : function(){
5560         
5561         var cfg = {
5562             tag: this.tag,
5563             // cls: this.cls, double assign in parent class Component.js :: onRender
5564             html: this.html
5565         };
5566         
5567         return cfg;
5568     },
5569     
5570     initEvents: function() 
5571     {
5572         Roo.bootstrap.Element.superclass.initEvents.call(this);
5573         
5574         if(this.clickable){
5575             this.el.on('click', this.onClick, this);
5576         }
5577         
5578     },
5579     
5580     onClick : function(e)
5581     {
5582         if(this.preventDefault){
5583             e.preventDefault();
5584         }
5585         
5586         this.fireEvent('click', this, e);
5587     },
5588     
5589     getValue : function()
5590     {
5591         return this.el.dom.innerHTML;
5592     },
5593     
5594     setValue : function(value)
5595     {
5596         this.el.dom.innerHTML = value;
5597     }
5598    
5599 });
5600
5601  
5602
5603  /*
5604  * - LGPL
5605  *
5606  * pagination
5607  * 
5608  */
5609
5610 /**
5611  * @class Roo.bootstrap.Pagination
5612  * @extends Roo.bootstrap.Component
5613  * Bootstrap Pagination class
5614  * @cfg {String} size xs | sm | md | lg
5615  * @cfg {Boolean} inverse false | true
5616  * 
5617  * @constructor
5618  * Create a new Pagination
5619  * @param {Object} config The config object
5620  */
5621
5622 Roo.bootstrap.Pagination = function(config){
5623     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5624 };
5625
5626 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5627     
5628     cls: false,
5629     size: false,
5630     inverse: false,
5631     
5632     getAutoCreate : function(){
5633         var cfg = {
5634             tag: 'ul',
5635                 cls: 'pagination'
5636         };
5637         if (this.inverse) {
5638             cfg.cls += ' inverse';
5639         }
5640         if (this.html) {
5641             cfg.html=this.html;
5642         }
5643         if (this.cls) {
5644             cfg.cls += " " + this.cls;
5645         }
5646         return cfg;
5647     }
5648    
5649 });
5650
5651  
5652
5653  /*
5654  * - LGPL
5655  *
5656  * Pagination item
5657  * 
5658  */
5659
5660
5661 /**
5662  * @class Roo.bootstrap.PaginationItem
5663  * @extends Roo.bootstrap.Component
5664  * Bootstrap PaginationItem class
5665  * @cfg {String} html text
5666  * @cfg {String} href the link
5667  * @cfg {Boolean} preventDefault (true | false) default true
5668  * @cfg {Boolean} active (true | false) default false
5669  * @cfg {Boolean} disabled default false
5670  * 
5671  * 
5672  * @constructor
5673  * Create a new PaginationItem
5674  * @param {Object} config The config object
5675  */
5676
5677
5678 Roo.bootstrap.PaginationItem = function(config){
5679     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5680     this.addEvents({
5681         // raw events
5682         /**
5683          * @event click
5684          * The raw click event for the entire grid.
5685          * @param {Roo.EventObject} e
5686          */
5687         "click" : true
5688     });
5689 };
5690
5691 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5692     
5693     href : false,
5694     html : false,
5695     preventDefault: true,
5696     active : false,
5697     cls : false,
5698     disabled: false,
5699     
5700     getAutoCreate : function(){
5701         var cfg= {
5702             tag: 'li',
5703             cn: [
5704                 {
5705                     tag : 'a',
5706                     href : this.href ? this.href : '#',
5707                     html : this.html ? this.html : ''
5708                 }
5709             ]
5710         };
5711         
5712         if(this.cls){
5713             cfg.cls = this.cls;
5714         }
5715         
5716         if(this.disabled){
5717             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5718         }
5719         
5720         if(this.active){
5721             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5722         }
5723         
5724         return cfg;
5725     },
5726     
5727     initEvents: function() {
5728         
5729         this.el.on('click', this.onClick, this);
5730         
5731     },
5732     onClick : function(e)
5733     {
5734         Roo.log('PaginationItem on click ');
5735         if(this.preventDefault){
5736             e.preventDefault();
5737         }
5738         
5739         if(this.disabled){
5740             return;
5741         }
5742         
5743         this.fireEvent('click', this, e);
5744     }
5745    
5746 });
5747
5748  
5749
5750  /*
5751  * - LGPL
5752  *
5753  * slider
5754  * 
5755  */
5756
5757
5758 /**
5759  * @class Roo.bootstrap.Slider
5760  * @extends Roo.bootstrap.Component
5761  * Bootstrap Slider class
5762  *    
5763  * @constructor
5764  * Create a new Slider
5765  * @param {Object} config The config object
5766  */
5767
5768 Roo.bootstrap.Slider = function(config){
5769     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5770 };
5771
5772 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5773     
5774     getAutoCreate : function(){
5775         
5776         var cfg = {
5777             tag: 'div',
5778             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5779             cn: [
5780                 {
5781                     tag: 'a',
5782                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5783                 }
5784             ]
5785         };
5786         
5787         return cfg;
5788     }
5789    
5790 });
5791
5792  /*
5793  * Based on:
5794  * Ext JS Library 1.1.1
5795  * Copyright(c) 2006-2007, Ext JS, LLC.
5796  *
5797  * Originally Released Under LGPL - original licence link has changed is not relivant.
5798  *
5799  * Fork - LGPL
5800  * <script type="text/javascript">
5801  */
5802  
5803
5804 /**
5805  * @class Roo.grid.ColumnModel
5806  * @extends Roo.util.Observable
5807  * This is the default implementation of a ColumnModel used by the Grid. It defines
5808  * the columns in the grid.
5809  * <br>Usage:<br>
5810  <pre><code>
5811  var colModel = new Roo.grid.ColumnModel([
5812         {header: "Ticker", width: 60, sortable: true, locked: true},
5813         {header: "Company Name", width: 150, sortable: true},
5814         {header: "Market Cap.", width: 100, sortable: true},
5815         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5816         {header: "Employees", width: 100, sortable: true, resizable: false}
5817  ]);
5818  </code></pre>
5819  * <p>
5820  
5821  * The config options listed for this class are options which may appear in each
5822  * individual column definition.
5823  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5824  * @constructor
5825  * @param {Object} config An Array of column config objects. See this class's
5826  * config objects for details.
5827 */
5828 Roo.grid.ColumnModel = function(config){
5829         /**
5830      * The config passed into the constructor
5831      */
5832     this.config = config;
5833     this.lookup = {};
5834
5835     // if no id, create one
5836     // if the column does not have a dataIndex mapping,
5837     // map it to the order it is in the config
5838     for(var i = 0, len = config.length; i < len; i++){
5839         var c = config[i];
5840         if(typeof c.dataIndex == "undefined"){
5841             c.dataIndex = i;
5842         }
5843         if(typeof c.renderer == "string"){
5844             c.renderer = Roo.util.Format[c.renderer];
5845         }
5846         if(typeof c.id == "undefined"){
5847             c.id = Roo.id();
5848         }
5849         if(c.editor && c.editor.xtype){
5850             c.editor  = Roo.factory(c.editor, Roo.grid);
5851         }
5852         if(c.editor && c.editor.isFormField){
5853             c.editor = new Roo.grid.GridEditor(c.editor);
5854         }
5855         this.lookup[c.id] = c;
5856     }
5857
5858     /**
5859      * The width of columns which have no width specified (defaults to 100)
5860      * @type Number
5861      */
5862     this.defaultWidth = 100;
5863
5864     /**
5865      * Default sortable of columns which have no sortable specified (defaults to false)
5866      * @type Boolean
5867      */
5868     this.defaultSortable = false;
5869
5870     this.addEvents({
5871         /**
5872              * @event widthchange
5873              * Fires when the width of a column changes.
5874              * @param {ColumnModel} this
5875              * @param {Number} columnIndex The column index
5876              * @param {Number} newWidth The new width
5877              */
5878             "widthchange": true,
5879         /**
5880              * @event headerchange
5881              * Fires when the text of a header changes.
5882              * @param {ColumnModel} this
5883              * @param {Number} columnIndex The column index
5884              * @param {Number} newText The new header text
5885              */
5886             "headerchange": true,
5887         /**
5888              * @event hiddenchange
5889              * Fires when a column is hidden or "unhidden".
5890              * @param {ColumnModel} this
5891              * @param {Number} columnIndex The column index
5892              * @param {Boolean} hidden true if hidden, false otherwise
5893              */
5894             "hiddenchange": true,
5895             /**
5896          * @event columnmoved
5897          * Fires when a column is moved.
5898          * @param {ColumnModel} this
5899          * @param {Number} oldIndex
5900          * @param {Number} newIndex
5901          */
5902         "columnmoved" : true,
5903         /**
5904          * @event columlockchange
5905          * Fires when a column's locked state is changed
5906          * @param {ColumnModel} this
5907          * @param {Number} colIndex
5908          * @param {Boolean} locked true if locked
5909          */
5910         "columnlockchange" : true
5911     });
5912     Roo.grid.ColumnModel.superclass.constructor.call(this);
5913 };
5914 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5915     /**
5916      * @cfg {String} header The header text to display in the Grid view.
5917      */
5918     /**
5919      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5920      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5921      * specified, the column's index is used as an index into the Record's data Array.
5922      */
5923     /**
5924      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5925      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5926      */
5927     /**
5928      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5929      * Defaults to the value of the {@link #defaultSortable} property.
5930      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5931      */
5932     /**
5933      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5934      */
5935     /**
5936      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5937      */
5938     /**
5939      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5940      */
5941     /**
5942      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5943      */
5944     /**
5945      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5946      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5947      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5948      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5949      */
5950        /**
5951      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5952      */
5953     /**
5954      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5955      */
5956     /**
5957      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5958      */
5959     /**
5960      * @cfg {String} cursor (Optional)
5961      */
5962     /**
5963      * @cfg {String} tooltip (Optional)
5964      */
5965     /**
5966      * @cfg {Number} xs (Optional)
5967      */
5968     /**
5969      * @cfg {Number} sm (Optional)
5970      */
5971     /**
5972      * @cfg {Number} md (Optional)
5973      */
5974     /**
5975      * @cfg {Number} lg (Optional)
5976      */
5977     /**
5978      * Returns the id of the column at the specified index.
5979      * @param {Number} index The column index
5980      * @return {String} the id
5981      */
5982     getColumnId : function(index){
5983         return this.config[index].id;
5984     },
5985
5986     /**
5987      * Returns the column for a specified id.
5988      * @param {String} id The column id
5989      * @return {Object} the column
5990      */
5991     getColumnById : function(id){
5992         return this.lookup[id];
5993     },
5994
5995     
5996     /**
5997      * Returns the column for a specified dataIndex.
5998      * @param {String} dataIndex The column dataIndex
5999      * @return {Object|Boolean} the column or false if not found
6000      */
6001     getColumnByDataIndex: function(dataIndex){
6002         var index = this.findColumnIndex(dataIndex);
6003         return index > -1 ? this.config[index] : false;
6004     },
6005     
6006     /**
6007      * Returns the index for a specified column id.
6008      * @param {String} id The column id
6009      * @return {Number} the index, or -1 if not found
6010      */
6011     getIndexById : function(id){
6012         for(var i = 0, len = this.config.length; i < len; i++){
6013             if(this.config[i].id == id){
6014                 return i;
6015             }
6016         }
6017         return -1;
6018     },
6019     
6020     /**
6021      * Returns the index for a specified column dataIndex.
6022      * @param {String} dataIndex The column dataIndex
6023      * @return {Number} the index, or -1 if not found
6024      */
6025     
6026     findColumnIndex : function(dataIndex){
6027         for(var i = 0, len = this.config.length; i < len; i++){
6028             if(this.config[i].dataIndex == dataIndex){
6029                 return i;
6030             }
6031         }
6032         return -1;
6033     },
6034     
6035     
6036     moveColumn : function(oldIndex, newIndex){
6037         var c = this.config[oldIndex];
6038         this.config.splice(oldIndex, 1);
6039         this.config.splice(newIndex, 0, c);
6040         this.dataMap = null;
6041         this.fireEvent("columnmoved", this, oldIndex, newIndex);
6042     },
6043
6044     isLocked : function(colIndex){
6045         return this.config[colIndex].locked === true;
6046     },
6047
6048     setLocked : function(colIndex, value, suppressEvent){
6049         if(this.isLocked(colIndex) == value){
6050             return;
6051         }
6052         this.config[colIndex].locked = value;
6053         if(!suppressEvent){
6054             this.fireEvent("columnlockchange", this, colIndex, value);
6055         }
6056     },
6057
6058     getTotalLockedWidth : function(){
6059         var totalWidth = 0;
6060         for(var i = 0; i < this.config.length; i++){
6061             if(this.isLocked(i) && !this.isHidden(i)){
6062                 this.totalWidth += this.getColumnWidth(i);
6063             }
6064         }
6065         return totalWidth;
6066     },
6067
6068     getLockedCount : function(){
6069         for(var i = 0, len = this.config.length; i < len; i++){
6070             if(!this.isLocked(i)){
6071                 return i;
6072             }
6073         }
6074         
6075         return this.config.length;
6076     },
6077
6078     /**
6079      * Returns the number of columns.
6080      * @return {Number}
6081      */
6082     getColumnCount : function(visibleOnly){
6083         if(visibleOnly === true){
6084             var c = 0;
6085             for(var i = 0, len = this.config.length; i < len; i++){
6086                 if(!this.isHidden(i)){
6087                     c++;
6088                 }
6089             }
6090             return c;
6091         }
6092         return this.config.length;
6093     },
6094
6095     /**
6096      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
6097      * @param {Function} fn
6098      * @param {Object} scope (optional)
6099      * @return {Array} result
6100      */
6101     getColumnsBy : function(fn, scope){
6102         var r = [];
6103         for(var i = 0, len = this.config.length; i < len; i++){
6104             var c = this.config[i];
6105             if(fn.call(scope||this, c, i) === true){
6106                 r[r.length] = c;
6107             }
6108         }
6109         return r;
6110     },
6111
6112     /**
6113      * Returns true if the specified column is sortable.
6114      * @param {Number} col The column index
6115      * @return {Boolean}
6116      */
6117     isSortable : function(col){
6118         if(typeof this.config[col].sortable == "undefined"){
6119             return this.defaultSortable;
6120         }
6121         return this.config[col].sortable;
6122     },
6123
6124     /**
6125      * Returns the rendering (formatting) function defined for the column.
6126      * @param {Number} col The column index.
6127      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
6128      */
6129     getRenderer : function(col){
6130         if(!this.config[col].renderer){
6131             return Roo.grid.ColumnModel.defaultRenderer;
6132         }
6133         return this.config[col].renderer;
6134     },
6135
6136     /**
6137      * Sets the rendering (formatting) function for a column.
6138      * @param {Number} col The column index
6139      * @param {Function} fn The function to use to process the cell's raw data
6140      * to return HTML markup for the grid view. The render function is called with
6141      * the following parameters:<ul>
6142      * <li>Data value.</li>
6143      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
6144      * <li>css A CSS style string to apply to the table cell.</li>
6145      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
6146      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
6147      * <li>Row index</li>
6148      * <li>Column index</li>
6149      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
6150      */
6151     setRenderer : function(col, fn){
6152         this.config[col].renderer = fn;
6153     },
6154
6155     /**
6156      * Returns the width for the specified column.
6157      * @param {Number} col The column index
6158      * @return {Number}
6159      */
6160     getColumnWidth : function(col){
6161         return this.config[col].width * 1 || this.defaultWidth;
6162     },
6163
6164     /**
6165      * Sets the width for a column.
6166      * @param {Number} col The column index
6167      * @param {Number} width The new width
6168      */
6169     setColumnWidth : function(col, width, suppressEvent){
6170         this.config[col].width = width;
6171         this.totalWidth = null;
6172         if(!suppressEvent){
6173              this.fireEvent("widthchange", this, col, width);
6174         }
6175     },
6176
6177     /**
6178      * Returns the total width of all columns.
6179      * @param {Boolean} includeHidden True to include hidden column widths
6180      * @return {Number}
6181      */
6182     getTotalWidth : function(includeHidden){
6183         if(!this.totalWidth){
6184             this.totalWidth = 0;
6185             for(var i = 0, len = this.config.length; i < len; i++){
6186                 if(includeHidden || !this.isHidden(i)){
6187                     this.totalWidth += this.getColumnWidth(i);
6188                 }
6189             }
6190         }
6191         return this.totalWidth;
6192     },
6193
6194     /**
6195      * Returns the header for the specified column.
6196      * @param {Number} col The column index
6197      * @return {String}
6198      */
6199     getColumnHeader : function(col){
6200         return this.config[col].header;
6201     },
6202
6203     /**
6204      * Sets the header for a column.
6205      * @param {Number} col The column index
6206      * @param {String} header The new header
6207      */
6208     setColumnHeader : function(col, header){
6209         this.config[col].header = header;
6210         this.fireEvent("headerchange", this, col, header);
6211     },
6212
6213     /**
6214      * Returns the tooltip for the specified column.
6215      * @param {Number} col The column index
6216      * @return {String}
6217      */
6218     getColumnTooltip : function(col){
6219             return this.config[col].tooltip;
6220     },
6221     /**
6222      * Sets the tooltip for a column.
6223      * @param {Number} col The column index
6224      * @param {String} tooltip The new tooltip
6225      */
6226     setColumnTooltip : function(col, tooltip){
6227             this.config[col].tooltip = tooltip;
6228     },
6229
6230     /**
6231      * Returns the dataIndex for the specified column.
6232      * @param {Number} col The column index
6233      * @return {Number}
6234      */
6235     getDataIndex : function(col){
6236         return this.config[col].dataIndex;
6237     },
6238
6239     /**
6240      * Sets the dataIndex for a column.
6241      * @param {Number} col The column index
6242      * @param {Number} dataIndex The new dataIndex
6243      */
6244     setDataIndex : function(col, dataIndex){
6245         this.config[col].dataIndex = dataIndex;
6246     },
6247
6248     
6249     
6250     /**
6251      * Returns true if the cell is editable.
6252      * @param {Number} colIndex The column index
6253      * @param {Number} rowIndex The row index - this is nto actually used..?
6254      * @return {Boolean}
6255      */
6256     isCellEditable : function(colIndex, rowIndex){
6257         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6258     },
6259
6260     /**
6261      * Returns the editor defined for the cell/column.
6262      * return false or null to disable editing.
6263      * @param {Number} colIndex The column index
6264      * @param {Number} rowIndex The row index
6265      * @return {Object}
6266      */
6267     getCellEditor : function(colIndex, rowIndex){
6268         return this.config[colIndex].editor;
6269     },
6270
6271     /**
6272      * Sets if a column is editable.
6273      * @param {Number} col The column index
6274      * @param {Boolean} editable True if the column is editable
6275      */
6276     setEditable : function(col, editable){
6277         this.config[col].editable = editable;
6278     },
6279
6280
6281     /**
6282      * Returns true if the column is hidden.
6283      * @param {Number} colIndex The column index
6284      * @return {Boolean}
6285      */
6286     isHidden : function(colIndex){
6287         return this.config[colIndex].hidden;
6288     },
6289
6290
6291     /**
6292      * Returns true if the column width cannot be changed
6293      */
6294     isFixed : function(colIndex){
6295         return this.config[colIndex].fixed;
6296     },
6297
6298     /**
6299      * Returns true if the column can be resized
6300      * @return {Boolean}
6301      */
6302     isResizable : function(colIndex){
6303         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6304     },
6305     /**
6306      * Sets if a column is hidden.
6307      * @param {Number} colIndex The column index
6308      * @param {Boolean} hidden True if the column is hidden
6309      */
6310     setHidden : function(colIndex, hidden){
6311         this.config[colIndex].hidden = hidden;
6312         this.totalWidth = null;
6313         this.fireEvent("hiddenchange", this, colIndex, hidden);
6314     },
6315
6316     /**
6317      * Sets the editor for a column.
6318      * @param {Number} col The column index
6319      * @param {Object} editor The editor object
6320      */
6321     setEditor : function(col, editor){
6322         this.config[col].editor = editor;
6323     }
6324 });
6325
6326 Roo.grid.ColumnModel.defaultRenderer = function(value)
6327 {
6328     if(typeof value == "object") {
6329         return value;
6330     }
6331         if(typeof value == "string" && value.length < 1){
6332             return "&#160;";
6333         }
6334     
6335         return String.format("{0}", value);
6336 };
6337
6338 // Alias for backwards compatibility
6339 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6340 /*
6341  * Based on:
6342  * Ext JS Library 1.1.1
6343  * Copyright(c) 2006-2007, Ext JS, LLC.
6344  *
6345  * Originally Released Under LGPL - original licence link has changed is not relivant.
6346  *
6347  * Fork - LGPL
6348  * <script type="text/javascript">
6349  */
6350  
6351 /**
6352  * @class Roo.LoadMask
6353  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6354  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6355  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6356  * element's UpdateManager load indicator and will be destroyed after the initial load.
6357  * @constructor
6358  * Create a new LoadMask
6359  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6360  * @param {Object} config The config object
6361  */
6362 Roo.LoadMask = function(el, config){
6363     this.el = Roo.get(el);
6364     Roo.apply(this, config);
6365     if(this.store){
6366         this.store.on('beforeload', this.onBeforeLoad, this);
6367         this.store.on('load', this.onLoad, this);
6368         this.store.on('loadexception', this.onLoadException, this);
6369         this.removeMask = false;
6370     }else{
6371         var um = this.el.getUpdateManager();
6372         um.showLoadIndicator = false; // disable the default indicator
6373         um.on('beforeupdate', this.onBeforeLoad, this);
6374         um.on('update', this.onLoad, this);
6375         um.on('failure', this.onLoad, this);
6376         this.removeMask = true;
6377     }
6378 };
6379
6380 Roo.LoadMask.prototype = {
6381     /**
6382      * @cfg {Boolean} removeMask
6383      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6384      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6385      */
6386     /**
6387      * @cfg {String} msg
6388      * The text to display in a centered loading message box (defaults to 'Loading...')
6389      */
6390     msg : 'Loading...',
6391     /**
6392      * @cfg {String} msgCls
6393      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6394      */
6395     msgCls : 'x-mask-loading',
6396
6397     /**
6398      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6399      * @type Boolean
6400      */
6401     disabled: false,
6402
6403     /**
6404      * Disables the mask to prevent it from being displayed
6405      */
6406     disable : function(){
6407        this.disabled = true;
6408     },
6409
6410     /**
6411      * Enables the mask so that it can be displayed
6412      */
6413     enable : function(){
6414         this.disabled = false;
6415     },
6416     
6417     onLoadException : function()
6418     {
6419         Roo.log(arguments);
6420         
6421         if (typeof(arguments[3]) != 'undefined') {
6422             Roo.MessageBox.alert("Error loading",arguments[3]);
6423         } 
6424         /*
6425         try {
6426             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6427                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6428             }   
6429         } catch(e) {
6430             
6431         }
6432         */
6433     
6434         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6435     },
6436     // private
6437     onLoad : function()
6438     {
6439         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6440     },
6441
6442     // private
6443     onBeforeLoad : function(){
6444         if(!this.disabled){
6445             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6446         }
6447     },
6448
6449     // private
6450     destroy : function(){
6451         if(this.store){
6452             this.store.un('beforeload', this.onBeforeLoad, this);
6453             this.store.un('load', this.onLoad, this);
6454             this.store.un('loadexception', this.onLoadException, this);
6455         }else{
6456             var um = this.el.getUpdateManager();
6457             um.un('beforeupdate', this.onBeforeLoad, this);
6458             um.un('update', this.onLoad, this);
6459             um.un('failure', this.onLoad, this);
6460         }
6461     }
6462 };/*
6463  * - LGPL
6464  *
6465  * table
6466  * 
6467  */
6468
6469 /**
6470  * @class Roo.bootstrap.Table
6471  * @extends Roo.bootstrap.Component
6472  * Bootstrap Table class
6473  * @cfg {String} cls table class
6474  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6475  * @cfg {String} bgcolor Specifies the background color for a table
6476  * @cfg {Number} border Specifies whether the table cells should have borders or not
6477  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6478  * @cfg {Number} cellspacing Specifies the space between cells
6479  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6480  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6481  * @cfg {String} sortable Specifies that the table should be sortable
6482  * @cfg {String} summary Specifies a summary of the content of a table
6483  * @cfg {Number} width Specifies the width of a table
6484  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6485  * 
6486  * @cfg {boolean} striped Should the rows be alternative striped
6487  * @cfg {boolean} bordered Add borders to the table
6488  * @cfg {boolean} hover Add hover highlighting
6489  * @cfg {boolean} condensed Format condensed
6490  * @cfg {boolean} responsive Format condensed
6491  * @cfg {Boolean} loadMask (true|false) default false
6492  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6493  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6494  * @cfg {Boolean} rowSelection (true|false) default false
6495  * @cfg {Boolean} cellSelection (true|false) default false
6496  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6497  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6498  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6499  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6500  
6501  * 
6502  * @constructor
6503  * Create a new Table
6504  * @param {Object} config The config object
6505  */
6506
6507 Roo.bootstrap.Table = function(config){
6508     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6509     
6510   
6511     
6512     // BC...
6513     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6514     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6515     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6516     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6517     
6518     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6519     if (this.sm) {
6520         this.sm.grid = this;
6521         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6522         this.sm = this.selModel;
6523         this.sm.xmodule = this.xmodule || false;
6524     }
6525     
6526     if (this.cm && typeof(this.cm.config) == 'undefined') {
6527         this.colModel = new Roo.grid.ColumnModel(this.cm);
6528         this.cm = this.colModel;
6529         this.cm.xmodule = this.xmodule || false;
6530     }
6531     if (this.store) {
6532         this.store= Roo.factory(this.store, Roo.data);
6533         this.ds = this.store;
6534         this.ds.xmodule = this.xmodule || false;
6535          
6536     }
6537     if (this.footer && this.store) {
6538         this.footer.dataSource = this.ds;
6539         this.footer = Roo.factory(this.footer);
6540     }
6541     
6542     /** @private */
6543     this.addEvents({
6544         /**
6545          * @event cellclick
6546          * Fires when a cell is clicked
6547          * @param {Roo.bootstrap.Table} this
6548          * @param {Roo.Element} el
6549          * @param {Number} rowIndex
6550          * @param {Number} columnIndex
6551          * @param {Roo.EventObject} e
6552          */
6553         "cellclick" : true,
6554         /**
6555          * @event celldblclick
6556          * Fires when a cell is double clicked
6557          * @param {Roo.bootstrap.Table} this
6558          * @param {Roo.Element} el
6559          * @param {Number} rowIndex
6560          * @param {Number} columnIndex
6561          * @param {Roo.EventObject} e
6562          */
6563         "celldblclick" : true,
6564         /**
6565          * @event rowclick
6566          * Fires when a row is clicked
6567          * @param {Roo.bootstrap.Table} this
6568          * @param {Roo.Element} el
6569          * @param {Number} rowIndex
6570          * @param {Roo.EventObject} e
6571          */
6572         "rowclick" : true,
6573         /**
6574          * @event rowdblclick
6575          * Fires when a row is double clicked
6576          * @param {Roo.bootstrap.Table} this
6577          * @param {Roo.Element} el
6578          * @param {Number} rowIndex
6579          * @param {Roo.EventObject} e
6580          */
6581         "rowdblclick" : true,
6582         /**
6583          * @event mouseover
6584          * Fires when a mouseover occur
6585          * @param {Roo.bootstrap.Table} this
6586          * @param {Roo.Element} el
6587          * @param {Number} rowIndex
6588          * @param {Number} columnIndex
6589          * @param {Roo.EventObject} e
6590          */
6591         "mouseover" : true,
6592         /**
6593          * @event mouseout
6594          * Fires when a mouseout occur
6595          * @param {Roo.bootstrap.Table} this
6596          * @param {Roo.Element} el
6597          * @param {Number} rowIndex
6598          * @param {Number} columnIndex
6599          * @param {Roo.EventObject} e
6600          */
6601         "mouseout" : true,
6602         /**
6603          * @event rowclass
6604          * Fires when a row is rendered, so you can change add a style to it.
6605          * @param {Roo.bootstrap.Table} this
6606          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6607          */
6608         'rowclass' : true,
6609           /**
6610          * @event rowsrendered
6611          * Fires when all the  rows have been rendered
6612          * @param {Roo.bootstrap.Table} this
6613          */
6614         'rowsrendered' : true,
6615         /**
6616          * @event contextmenu
6617          * The raw contextmenu event for the entire grid.
6618          * @param {Roo.EventObject} e
6619          */
6620         "contextmenu" : true,
6621         /**
6622          * @event rowcontextmenu
6623          * Fires when a row is right clicked
6624          * @param {Roo.bootstrap.Table} this
6625          * @param {Number} rowIndex
6626          * @param {Roo.EventObject} e
6627          */
6628         "rowcontextmenu" : true,
6629         /**
6630          * @event cellcontextmenu
6631          * Fires when a cell is right clicked
6632          * @param {Roo.bootstrap.Table} this
6633          * @param {Number} rowIndex
6634          * @param {Number} cellIndex
6635          * @param {Roo.EventObject} e
6636          */
6637          "cellcontextmenu" : true,
6638          /**
6639          * @event headercontextmenu
6640          * Fires when a header is right clicked
6641          * @param {Roo.bootstrap.Table} this
6642          * @param {Number} columnIndex
6643          * @param {Roo.EventObject} e
6644          */
6645         "headercontextmenu" : true
6646     });
6647 };
6648
6649 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6650     
6651     cls: false,
6652     align: false,
6653     bgcolor: false,
6654     border: false,
6655     cellpadding: false,
6656     cellspacing: false,
6657     frame: false,
6658     rules: false,
6659     sortable: false,
6660     summary: false,
6661     width: false,
6662     striped : false,
6663     scrollBody : false,
6664     bordered: false,
6665     hover:  false,
6666     condensed : false,
6667     responsive : false,
6668     sm : false,
6669     cm : false,
6670     store : false,
6671     loadMask : false,
6672     footerShow : true,
6673     headerShow : true,
6674   
6675     rowSelection : false,
6676     cellSelection : false,
6677     layout : false,
6678     
6679     // Roo.Element - the tbody
6680     mainBody: false,
6681     // Roo.Element - thead element
6682     mainHead: false,
6683     
6684     container: false, // used by gridpanel...
6685     
6686     lazyLoad : false,
6687     
6688     CSS : Roo.util.CSS,
6689     
6690     auto_hide_footer : false,
6691     
6692     getAutoCreate : function()
6693     {
6694         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6695         
6696         cfg = {
6697             tag: 'table',
6698             cls : 'table',
6699             cn : []
6700         };
6701         if (this.scrollBody) {
6702             cfg.cls += ' table-body-fixed';
6703         }    
6704         if (this.striped) {
6705             cfg.cls += ' table-striped';
6706         }
6707         
6708         if (this.hover) {
6709             cfg.cls += ' table-hover';
6710         }
6711         if (this.bordered) {
6712             cfg.cls += ' table-bordered';
6713         }
6714         if (this.condensed) {
6715             cfg.cls += ' table-condensed';
6716         }
6717         if (this.responsive) {
6718             cfg.cls += ' table-responsive';
6719         }
6720         
6721         if (this.cls) {
6722             cfg.cls+=  ' ' +this.cls;
6723         }
6724         
6725         // this lot should be simplifed...
6726         var _t = this;
6727         var cp = [
6728             'align',
6729             'bgcolor',
6730             'border',
6731             'cellpadding',
6732             'cellspacing',
6733             'frame',
6734             'rules',
6735             'sortable',
6736             'summary',
6737             'width'
6738         ].forEach(function(k) {
6739             if (_t[k]) {
6740                 cfg[k] = _t[k];
6741             }
6742         });
6743         
6744         
6745         if (this.layout) {
6746             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6747         }
6748         
6749         if(this.store || this.cm){
6750             if(this.headerShow){
6751                 cfg.cn.push(this.renderHeader());
6752             }
6753             
6754             cfg.cn.push(this.renderBody());
6755             
6756             if(this.footerShow){
6757                 cfg.cn.push(this.renderFooter());
6758             }
6759             // where does this come from?
6760             //cfg.cls+=  ' TableGrid';
6761         }
6762         
6763         return { cn : [ cfg ] };
6764     },
6765     
6766     initEvents : function()
6767     {   
6768         if(!this.store || !this.cm){
6769             return;
6770         }
6771         if (this.selModel) {
6772             this.selModel.initEvents();
6773         }
6774         
6775         
6776         //Roo.log('initEvents with ds!!!!');
6777         
6778         this.mainBody = this.el.select('tbody', true).first();
6779         this.mainHead = this.el.select('thead', true).first();
6780         this.mainFoot = this.el.select('tfoot', true).first();
6781         
6782         
6783         
6784         var _this = this;
6785         
6786         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6787             e.on('click', _this.sort, _this);
6788         });
6789         
6790         this.mainBody.on("click", this.onClick, this);
6791         this.mainBody.on("dblclick", this.onDblClick, this);
6792         
6793         // why is this done????? = it breaks dialogs??
6794         //this.parent().el.setStyle('position', 'relative');
6795         
6796         
6797         if (this.footer) {
6798             this.footer.parentId = this.id;
6799             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6800             
6801             if(this.lazyLoad){
6802                 this.el.select('tfoot tr td').first().addClass('hide');
6803             }
6804         } 
6805         
6806         if(this.loadMask) {
6807             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6808         }
6809         
6810         this.store.on('load', this.onLoad, this);
6811         this.store.on('beforeload', this.onBeforeLoad, this);
6812         this.store.on('update', this.onUpdate, this);
6813         this.store.on('add', this.onAdd, this);
6814         this.store.on("clear", this.clear, this);
6815         
6816         this.el.on("contextmenu", this.onContextMenu, this);
6817         
6818         this.mainBody.on('scroll', this.onBodyScroll, this);
6819         
6820         this.cm.on("headerchange", this.onHeaderChange, this);
6821         
6822         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6823         
6824     },
6825     
6826     onContextMenu : function(e, t)
6827     {
6828         this.processEvent("contextmenu", e);
6829     },
6830     
6831     processEvent : function(name, e)
6832     {
6833         if (name != 'touchstart' ) {
6834             this.fireEvent(name, e);    
6835         }
6836         
6837         var t = e.getTarget();
6838         
6839         var cell = Roo.get(t);
6840         
6841         if(!cell){
6842             return;
6843         }
6844         
6845         if(cell.findParent('tfoot', false, true)){
6846             return;
6847         }
6848         
6849         if(cell.findParent('thead', false, true)){
6850             
6851             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6852                 cell = Roo.get(t).findParent('th', false, true);
6853                 if (!cell) {
6854                     Roo.log("failed to find th in thead?");
6855                     Roo.log(e.getTarget());
6856                     return;
6857                 }
6858             }
6859             
6860             var cellIndex = cell.dom.cellIndex;
6861             
6862             var ename = name == 'touchstart' ? 'click' : name;
6863             this.fireEvent("header" + ename, this, cellIndex, e);
6864             
6865             return;
6866         }
6867         
6868         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6869             cell = Roo.get(t).findParent('td', false, true);
6870             if (!cell) {
6871                 Roo.log("failed to find th in tbody?");
6872                 Roo.log(e.getTarget());
6873                 return;
6874             }
6875         }
6876         
6877         var row = cell.findParent('tr', false, true);
6878         var cellIndex = cell.dom.cellIndex;
6879         var rowIndex = row.dom.rowIndex - 1;
6880         
6881         if(row !== false){
6882             
6883             this.fireEvent("row" + name, this, rowIndex, e);
6884             
6885             if(cell !== false){
6886             
6887                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6888             }
6889         }
6890         
6891     },
6892     
6893     onMouseover : function(e, el)
6894     {
6895         var cell = Roo.get(el);
6896         
6897         if(!cell){
6898             return;
6899         }
6900         
6901         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6902             cell = cell.findParent('td', false, true);
6903         }
6904         
6905         var row = cell.findParent('tr', false, true);
6906         var cellIndex = cell.dom.cellIndex;
6907         var rowIndex = row.dom.rowIndex - 1; // start from 0
6908         
6909         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6910         
6911     },
6912     
6913     onMouseout : function(e, el)
6914     {
6915         var cell = Roo.get(el);
6916         
6917         if(!cell){
6918             return;
6919         }
6920         
6921         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6922             cell = cell.findParent('td', false, true);
6923         }
6924         
6925         var row = cell.findParent('tr', false, true);
6926         var cellIndex = cell.dom.cellIndex;
6927         var rowIndex = row.dom.rowIndex - 1; // start from 0
6928         
6929         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6930         
6931     },
6932     
6933     onClick : function(e, el)
6934     {
6935         var cell = Roo.get(el);
6936         
6937         if(!cell || (!this.cellSelection && !this.rowSelection)){
6938             return;
6939         }
6940         
6941         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6942             cell = cell.findParent('td', false, true);
6943         }
6944         
6945         if(!cell || typeof(cell) == 'undefined'){
6946             return;
6947         }
6948         
6949         var row = cell.findParent('tr', false, true);
6950         
6951         if(!row || typeof(row) == 'undefined'){
6952             return;
6953         }
6954         
6955         var cellIndex = cell.dom.cellIndex;
6956         var rowIndex = this.getRowIndex(row);
6957         
6958         // why??? - should these not be based on SelectionModel?
6959         if(this.cellSelection){
6960             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6961         }
6962         
6963         if(this.rowSelection){
6964             this.fireEvent('rowclick', this, row, rowIndex, e);
6965         }
6966         
6967         
6968     },
6969         
6970     onDblClick : function(e,el)
6971     {
6972         var cell = Roo.get(el);
6973         
6974         if(!cell || (!this.cellSelection && !this.rowSelection)){
6975             return;
6976         }
6977         
6978         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6979             cell = cell.findParent('td', false, true);
6980         }
6981         
6982         if(!cell || typeof(cell) == 'undefined'){
6983             return;
6984         }
6985         
6986         var row = cell.findParent('tr', false, true);
6987         
6988         if(!row || typeof(row) == 'undefined'){
6989             return;
6990         }
6991         
6992         var cellIndex = cell.dom.cellIndex;
6993         var rowIndex = this.getRowIndex(row);
6994         
6995         if(this.cellSelection){
6996             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6997         }
6998         
6999         if(this.rowSelection){
7000             this.fireEvent('rowdblclick', this, row, rowIndex, e);
7001         }
7002     },
7003     
7004     sort : function(e,el)
7005     {
7006         var col = Roo.get(el);
7007         
7008         if(!col.hasClass('sortable')){
7009             return;
7010         }
7011         
7012         var sort = col.attr('sort');
7013         var dir = 'ASC';
7014         
7015         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
7016             dir = 'DESC';
7017         }
7018         
7019         this.store.sortInfo = {field : sort, direction : dir};
7020         
7021         if (this.footer) {
7022             Roo.log("calling footer first");
7023             this.footer.onClick('first');
7024         } else {
7025         
7026             this.store.load({ params : { start : 0 } });
7027         }
7028     },
7029     
7030     renderHeader : function()
7031     {
7032         var header = {
7033             tag: 'thead',
7034             cn : []
7035         };
7036         
7037         var cm = this.cm;
7038         this.totalWidth = 0;
7039         
7040         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7041             
7042             var config = cm.config[i];
7043             
7044             var c = {
7045                 tag: 'th',
7046                 cls : 'x-hcol-' + i,
7047                 style : '',
7048                 html: cm.getColumnHeader(i)
7049             };
7050             
7051             var hh = '';
7052             
7053             if(typeof(config.sortable) != 'undefined' && config.sortable){
7054                 c.cls = 'sortable';
7055                 c.html = '<i class="glyphicon"></i>' + c.html;
7056             }
7057             
7058             // could use BS4 hidden-..-down 
7059             
7060             if(typeof(config.lgHeader) != 'undefined'){
7061                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
7062             }
7063             
7064             if(typeof(config.mdHeader) != 'undefined'){
7065                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
7066             }
7067             
7068             if(typeof(config.smHeader) != 'undefined'){
7069                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
7070             }
7071             
7072             if(typeof(config.xsHeader) != 'undefined'){
7073                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
7074             }
7075             
7076             if(hh.length){
7077                 c.html = hh;
7078             }
7079             
7080             if(typeof(config.tooltip) != 'undefined'){
7081                 c.tooltip = config.tooltip;
7082             }
7083             
7084             if(typeof(config.colspan) != 'undefined'){
7085                 c.colspan = config.colspan;
7086             }
7087             
7088             if(typeof(config.hidden) != 'undefined' && config.hidden){
7089                 c.style += ' display:none;';
7090             }
7091             
7092             if(typeof(config.dataIndex) != 'undefined'){
7093                 c.sort = config.dataIndex;
7094             }
7095             
7096            
7097             
7098             if(typeof(config.align) != 'undefined' && config.align.length){
7099                 c.style += ' text-align:' + config.align + ';';
7100             }
7101             
7102             if(typeof(config.width) != 'undefined'){
7103                 c.style += ' width:' + config.width + 'px;';
7104                 this.totalWidth += config.width;
7105             } else {
7106                 this.totalWidth += 100; // assume minimum of 100 per column?
7107             }
7108             
7109             if(typeof(config.cls) != 'undefined'){
7110                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
7111             }
7112             
7113             ['xs','sm','md','lg'].map(function(size){
7114                 
7115                 if(typeof(config[size]) == 'undefined'){
7116                     return;
7117                 }
7118                  
7119                 if (!config[size]) { // 0 = hidden
7120                     // BS 4 '0' is treated as hide that column and below.
7121                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
7122                     return;
7123                 }
7124                 
7125                 c.cls += ' col-' + size + '-' + config[size] + (
7126                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
7127                 );
7128                 
7129                 
7130             });
7131             
7132             header.cn.push(c)
7133         }
7134         
7135         return header;
7136     },
7137     
7138     renderBody : function()
7139     {
7140         var body = {
7141             tag: 'tbody',
7142             cn : [
7143                 {
7144                     tag: 'tr',
7145                     cn : [
7146                         {
7147                             tag : 'td',
7148                             colspan :  this.cm.getColumnCount()
7149                         }
7150                     ]
7151                 }
7152             ]
7153         };
7154         
7155         return body;
7156     },
7157     
7158     renderFooter : function()
7159     {
7160         var footer = {
7161             tag: 'tfoot',
7162             cn : [
7163                 {
7164                     tag: 'tr',
7165                     cn : [
7166                         {
7167                             tag : 'td',
7168                             colspan :  this.cm.getColumnCount()
7169                         }
7170                     ]
7171                 }
7172             ]
7173         };
7174         
7175         return footer;
7176     },
7177     
7178     
7179     
7180     onLoad : function()
7181     {
7182 //        Roo.log('ds onload');
7183         this.clear();
7184         
7185         var _this = this;
7186         var cm = this.cm;
7187         var ds = this.store;
7188         
7189         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7190             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
7191             if (_this.store.sortInfo) {
7192                     
7193                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
7194                     e.select('i', true).addClass(['glyphicon-arrow-up']);
7195                 }
7196                 
7197                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
7198                     e.select('i', true).addClass(['glyphicon-arrow-down']);
7199                 }
7200             }
7201         });
7202         
7203         var tbody =  this.mainBody;
7204               
7205         if(ds.getCount() > 0){
7206             ds.data.each(function(d,rowIndex){
7207                 var row =  this.renderRow(cm, ds, rowIndex);
7208                 
7209                 tbody.createChild(row);
7210                 
7211                 var _this = this;
7212                 
7213                 if(row.cellObjects.length){
7214                     Roo.each(row.cellObjects, function(r){
7215                         _this.renderCellObject(r);
7216                     })
7217                 }
7218                 
7219             }, this);
7220         }
7221         
7222         var tfoot = this.el.select('tfoot', true).first();
7223         
7224         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
7225             
7226             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
7227             
7228             var total = this.ds.getTotalCount();
7229             
7230             if(this.footer.pageSize < total){
7231                 this.mainFoot.show();
7232             }
7233         }
7234         
7235         Roo.each(this.el.select('tbody td', true).elements, function(e){
7236             e.on('mouseover', _this.onMouseover, _this);
7237         });
7238         
7239         Roo.each(this.el.select('tbody td', true).elements, function(e){
7240             e.on('mouseout', _this.onMouseout, _this);
7241         });
7242         this.fireEvent('rowsrendered', this);
7243         
7244         this.autoSize();
7245     },
7246     
7247     
7248     onUpdate : function(ds,record)
7249     {
7250         this.refreshRow(record);
7251         this.autoSize();
7252     },
7253     
7254     onRemove : function(ds, record, index, isUpdate){
7255         if(isUpdate !== true){
7256             this.fireEvent("beforerowremoved", this, index, record);
7257         }
7258         var bt = this.mainBody.dom;
7259         
7260         var rows = this.el.select('tbody > tr', true).elements;
7261         
7262         if(typeof(rows[index]) != 'undefined'){
7263             bt.removeChild(rows[index].dom);
7264         }
7265         
7266 //        if(bt.rows[index]){
7267 //            bt.removeChild(bt.rows[index]);
7268 //        }
7269         
7270         if(isUpdate !== true){
7271             //this.stripeRows(index);
7272             //this.syncRowHeights(index, index);
7273             //this.layout();
7274             this.fireEvent("rowremoved", this, index, record);
7275         }
7276     },
7277     
7278     onAdd : function(ds, records, rowIndex)
7279     {
7280         //Roo.log('on Add called');
7281         // - note this does not handle multiple adding very well..
7282         var bt = this.mainBody.dom;
7283         for (var i =0 ; i < records.length;i++) {
7284             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7285             //Roo.log(records[i]);
7286             //Roo.log(this.store.getAt(rowIndex+i));
7287             this.insertRow(this.store, rowIndex + i, false);
7288             return;
7289         }
7290         
7291     },
7292     
7293     
7294     refreshRow : function(record){
7295         var ds = this.store, index;
7296         if(typeof record == 'number'){
7297             index = record;
7298             record = ds.getAt(index);
7299         }else{
7300             index = ds.indexOf(record);
7301         }
7302         this.insertRow(ds, index, true);
7303         this.autoSize();
7304         this.onRemove(ds, record, index+1, true);
7305         this.autoSize();
7306         //this.syncRowHeights(index, index);
7307         //this.layout();
7308         this.fireEvent("rowupdated", this, index, record);
7309     },
7310     
7311     insertRow : function(dm, rowIndex, isUpdate){
7312         
7313         if(!isUpdate){
7314             this.fireEvent("beforerowsinserted", this, rowIndex);
7315         }
7316             //var s = this.getScrollState();
7317         var row = this.renderRow(this.cm, this.store, rowIndex);
7318         // insert before rowIndex..
7319         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7320         
7321         var _this = this;
7322                 
7323         if(row.cellObjects.length){
7324             Roo.each(row.cellObjects, function(r){
7325                 _this.renderCellObject(r);
7326             })
7327         }
7328             
7329         if(!isUpdate){
7330             this.fireEvent("rowsinserted", this, rowIndex);
7331             //this.syncRowHeights(firstRow, lastRow);
7332             //this.stripeRows(firstRow);
7333             //this.layout();
7334         }
7335         
7336     },
7337     
7338     
7339     getRowDom : function(rowIndex)
7340     {
7341         var rows = this.el.select('tbody > tr', true).elements;
7342         
7343         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7344         
7345     },
7346     // returns the object tree for a tr..
7347   
7348     
7349     renderRow : function(cm, ds, rowIndex) 
7350     {
7351         var d = ds.getAt(rowIndex);
7352         
7353         var row = {
7354             tag : 'tr',
7355             cls : 'x-row-' + rowIndex,
7356             cn : []
7357         };
7358             
7359         var cellObjects = [];
7360         
7361         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7362             var config = cm.config[i];
7363             
7364             var renderer = cm.getRenderer(i);
7365             var value = '';
7366             var id = false;
7367             
7368             if(typeof(renderer) !== 'undefined'){
7369                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7370             }
7371             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7372             // and are rendered into the cells after the row is rendered - using the id for the element.
7373             
7374             if(typeof(value) === 'object'){
7375                 id = Roo.id();
7376                 cellObjects.push({
7377                     container : id,
7378                     cfg : value 
7379                 })
7380             }
7381             
7382             var rowcfg = {
7383                 record: d,
7384                 rowIndex : rowIndex,
7385                 colIndex : i,
7386                 rowClass : ''
7387             };
7388
7389             this.fireEvent('rowclass', this, rowcfg);
7390             
7391             var td = {
7392                 tag: 'td',
7393                 cls : rowcfg.rowClass + ' x-col-' + i,
7394                 style: '',
7395                 html: (typeof(value) === 'object') ? '' : value
7396             };
7397             
7398             if (id) {
7399                 td.id = id;
7400             }
7401             
7402             if(typeof(config.colspan) != 'undefined'){
7403                 td.colspan = config.colspan;
7404             }
7405             
7406             if(typeof(config.hidden) != 'undefined' && config.hidden){
7407                 td.style += ' display:none;';
7408             }
7409             
7410             if(typeof(config.align) != 'undefined' && config.align.length){
7411                 td.style += ' text-align:' + config.align + ';';
7412             }
7413             if(typeof(config.valign) != 'undefined' && config.valign.length){
7414                 td.style += ' vertical-align:' + config.valign + ';';
7415             }
7416             
7417             if(typeof(config.width) != 'undefined'){
7418                 td.style += ' width:' +  config.width + 'px;';
7419             }
7420             
7421             if(typeof(config.cursor) != 'undefined'){
7422                 td.style += ' cursor:' +  config.cursor + ';';
7423             }
7424             
7425             if(typeof(config.cls) != 'undefined'){
7426                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7427             }
7428             
7429             ['xs','sm','md','lg'].map(function(size){
7430                 
7431                 if(typeof(config[size]) == 'undefined'){
7432                     return;
7433                 }
7434                 
7435                 
7436                   
7437                 if (!config[size]) { // 0 = hidden
7438                     // BS 4 '0' is treated as hide that column and below.
7439                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
7440                     return;
7441                 }
7442                 
7443                 td.cls += ' col-' + size + '-' + config[size] + (
7444                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
7445                 );
7446                  
7447
7448             });
7449             
7450             row.cn.push(td);
7451            
7452         }
7453         
7454         row.cellObjects = cellObjects;
7455         
7456         return row;
7457           
7458     },
7459     
7460     
7461     
7462     onBeforeLoad : function()
7463     {
7464         
7465     },
7466      /**
7467      * Remove all rows
7468      */
7469     clear : function()
7470     {
7471         this.el.select('tbody', true).first().dom.innerHTML = '';
7472     },
7473     /**
7474      * Show or hide a row.
7475      * @param {Number} rowIndex to show or hide
7476      * @param {Boolean} state hide
7477      */
7478     setRowVisibility : function(rowIndex, state)
7479     {
7480         var bt = this.mainBody.dom;
7481         
7482         var rows = this.el.select('tbody > tr', true).elements;
7483         
7484         if(typeof(rows[rowIndex]) == 'undefined'){
7485             return;
7486         }
7487         rows[rowIndex].dom.style.display = state ? '' : 'none';
7488     },
7489     
7490     
7491     getSelectionModel : function(){
7492         if(!this.selModel){
7493             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7494         }
7495         return this.selModel;
7496     },
7497     /*
7498      * Render the Roo.bootstrap object from renderder
7499      */
7500     renderCellObject : function(r)
7501     {
7502         var _this = this;
7503         
7504         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7505         
7506         var t = r.cfg.render(r.container);
7507         
7508         if(r.cfg.cn){
7509             Roo.each(r.cfg.cn, function(c){
7510                 var child = {
7511                     container: t.getChildContainer(),
7512                     cfg: c
7513                 };
7514                 _this.renderCellObject(child);
7515             })
7516         }
7517     },
7518     
7519     getRowIndex : function(row)
7520     {
7521         var rowIndex = -1;
7522         
7523         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7524             if(el != row){
7525                 return;
7526             }
7527             
7528             rowIndex = index;
7529         });
7530         
7531         return rowIndex;
7532     },
7533      /**
7534      * Returns the grid's underlying element = used by panel.Grid
7535      * @return {Element} The element
7536      */
7537     getGridEl : function(){
7538         return this.el;
7539     },
7540      /**
7541      * Forces a resize - used by panel.Grid
7542      * @return {Element} The element
7543      */
7544     autoSize : function()
7545     {
7546         //var ctr = Roo.get(this.container.dom.parentElement);
7547         var ctr = Roo.get(this.el.dom);
7548         
7549         var thd = this.getGridEl().select('thead',true).first();
7550         var tbd = this.getGridEl().select('tbody', true).first();
7551         var tfd = this.getGridEl().select('tfoot', true).first();
7552         
7553         var cw = ctr.getWidth();
7554         
7555         if (tbd) {
7556             
7557             tbd.setWidth(ctr.getWidth());
7558             // if the body has a max height - and then scrolls - we should perhaps set up the height here
7559             // this needs fixing for various usage - currently only hydra job advers I think..
7560             //tdb.setHeight(
7561             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7562             //); 
7563             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7564             cw -= barsize;
7565         }
7566         cw = Math.max(cw, this.totalWidth);
7567         this.getGridEl().select('tr',true).setWidth(cw);
7568         // resize 'expandable coloumn?
7569         
7570         return; // we doe not have a view in this design..
7571         
7572     },
7573     onBodyScroll: function()
7574     {
7575         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7576         if(this.mainHead){
7577             this.mainHead.setStyle({
7578                 'position' : 'relative',
7579                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7580             });
7581         }
7582         
7583         if(this.lazyLoad){
7584             
7585             var scrollHeight = this.mainBody.dom.scrollHeight;
7586             
7587             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7588             
7589             var height = this.mainBody.getHeight();
7590             
7591             if(scrollHeight - height == scrollTop) {
7592                 
7593                 var total = this.ds.getTotalCount();
7594                 
7595                 if(this.footer.cursor + this.footer.pageSize < total){
7596                     
7597                     this.footer.ds.load({
7598                         params : {
7599                             start : this.footer.cursor + this.footer.pageSize,
7600                             limit : this.footer.pageSize
7601                         },
7602                         add : true
7603                     });
7604                 }
7605             }
7606             
7607         }
7608     },
7609     
7610     onHeaderChange : function()
7611     {
7612         var header = this.renderHeader();
7613         var table = this.el.select('table', true).first();
7614         
7615         this.mainHead.remove();
7616         this.mainHead = table.createChild(header, this.mainBody, false);
7617     },
7618     
7619     onHiddenChange : function(colModel, colIndex, hidden)
7620     {
7621         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7622         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7623         
7624         this.CSS.updateRule(thSelector, "display", "");
7625         this.CSS.updateRule(tdSelector, "display", "");
7626         
7627         if(hidden){
7628             this.CSS.updateRule(thSelector, "display", "none");
7629             this.CSS.updateRule(tdSelector, "display", "none");
7630         }
7631         
7632         this.onHeaderChange();
7633         this.onLoad();
7634     },
7635     
7636     setColumnWidth: function(col_index, width)
7637     {
7638         // width = "md-2 xs-2..."
7639         if(!this.colModel.config[col_index]) {
7640             return;
7641         }
7642         
7643         var w = width.split(" ");
7644         
7645         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7646         
7647         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7648         
7649         
7650         for(var j = 0; j < w.length; j++) {
7651             
7652             if(!w[j]) {
7653                 continue;
7654             }
7655             
7656             var size_cls = w[j].split("-");
7657             
7658             if(!Number.isInteger(size_cls[1] * 1)) {
7659                 continue;
7660             }
7661             
7662             if(!this.colModel.config[col_index][size_cls[0]]) {
7663                 continue;
7664             }
7665             
7666             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7667                 continue;
7668             }
7669             
7670             h_row[0].classList.replace(
7671                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7672                 "col-"+size_cls[0]+"-"+size_cls[1]
7673             );
7674             
7675             for(var i = 0; i < rows.length; i++) {
7676                 
7677                 var size_cls = w[j].split("-");
7678                 
7679                 if(!Number.isInteger(size_cls[1] * 1)) {
7680                     continue;
7681                 }
7682                 
7683                 if(!this.colModel.config[col_index][size_cls[0]]) {
7684                     continue;
7685                 }
7686                 
7687                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7688                     continue;
7689                 }
7690                 
7691                 rows[i].classList.replace(
7692                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7693                     "col-"+size_cls[0]+"-"+size_cls[1]
7694                 );
7695             }
7696             
7697             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7698         }
7699     }
7700 });
7701
7702  
7703
7704  /*
7705  * - LGPL
7706  *
7707  * table cell
7708  * 
7709  */
7710
7711 /**
7712  * @class Roo.bootstrap.TableCell
7713  * @extends Roo.bootstrap.Component
7714  * Bootstrap TableCell class
7715  * @cfg {String} html cell contain text
7716  * @cfg {String} cls cell class
7717  * @cfg {String} tag cell tag (td|th) default td
7718  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7719  * @cfg {String} align Aligns the content in a cell
7720  * @cfg {String} axis Categorizes cells
7721  * @cfg {String} bgcolor Specifies the background color of a cell
7722  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7723  * @cfg {Number} colspan Specifies the number of columns a cell should span
7724  * @cfg {String} headers Specifies one or more header cells a cell is related to
7725  * @cfg {Number} height Sets the height of a cell
7726  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7727  * @cfg {Number} rowspan Sets the number of rows a cell should span
7728  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7729  * @cfg {String} valign Vertical aligns the content in a cell
7730  * @cfg {Number} width Specifies the width of a cell
7731  * 
7732  * @constructor
7733  * Create a new TableCell
7734  * @param {Object} config The config object
7735  */
7736
7737 Roo.bootstrap.TableCell = function(config){
7738     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7739 };
7740
7741 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7742     
7743     html: false,
7744     cls: false,
7745     tag: false,
7746     abbr: false,
7747     align: false,
7748     axis: false,
7749     bgcolor: false,
7750     charoff: false,
7751     colspan: false,
7752     headers: false,
7753     height: false,
7754     nowrap: false,
7755     rowspan: false,
7756     scope: false,
7757     valign: false,
7758     width: false,
7759     
7760     
7761     getAutoCreate : function(){
7762         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7763         
7764         cfg = {
7765             tag: 'td'
7766         };
7767         
7768         if(this.tag){
7769             cfg.tag = this.tag;
7770         }
7771         
7772         if (this.html) {
7773             cfg.html=this.html
7774         }
7775         if (this.cls) {
7776             cfg.cls=this.cls
7777         }
7778         if (this.abbr) {
7779             cfg.abbr=this.abbr
7780         }
7781         if (this.align) {
7782             cfg.align=this.align
7783         }
7784         if (this.axis) {
7785             cfg.axis=this.axis
7786         }
7787         if (this.bgcolor) {
7788             cfg.bgcolor=this.bgcolor
7789         }
7790         if (this.charoff) {
7791             cfg.charoff=this.charoff
7792         }
7793         if (this.colspan) {
7794             cfg.colspan=this.colspan
7795         }
7796         if (this.headers) {
7797             cfg.headers=this.headers
7798         }
7799         if (this.height) {
7800             cfg.height=this.height
7801         }
7802         if (this.nowrap) {
7803             cfg.nowrap=this.nowrap
7804         }
7805         if (this.rowspan) {
7806             cfg.rowspan=this.rowspan
7807         }
7808         if (this.scope) {
7809             cfg.scope=this.scope
7810         }
7811         if (this.valign) {
7812             cfg.valign=this.valign
7813         }
7814         if (this.width) {
7815             cfg.width=this.width
7816         }
7817         
7818         
7819         return cfg;
7820     }
7821    
7822 });
7823
7824  
7825
7826  /*
7827  * - LGPL
7828  *
7829  * table row
7830  * 
7831  */
7832
7833 /**
7834  * @class Roo.bootstrap.TableRow
7835  * @extends Roo.bootstrap.Component
7836  * Bootstrap TableRow class
7837  * @cfg {String} cls row class
7838  * @cfg {String} align Aligns the content in a table row
7839  * @cfg {String} bgcolor Specifies a background color for a table row
7840  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7841  * @cfg {String} valign Vertical aligns the content in a table row
7842  * 
7843  * @constructor
7844  * Create a new TableRow
7845  * @param {Object} config The config object
7846  */
7847
7848 Roo.bootstrap.TableRow = function(config){
7849     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7850 };
7851
7852 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7853     
7854     cls: false,
7855     align: false,
7856     bgcolor: false,
7857     charoff: false,
7858     valign: false,
7859     
7860     getAutoCreate : function(){
7861         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7862         
7863         cfg = {
7864             tag: 'tr'
7865         };
7866             
7867         if(this.cls){
7868             cfg.cls = this.cls;
7869         }
7870         if(this.align){
7871             cfg.align = this.align;
7872         }
7873         if(this.bgcolor){
7874             cfg.bgcolor = this.bgcolor;
7875         }
7876         if(this.charoff){
7877             cfg.charoff = this.charoff;
7878         }
7879         if(this.valign){
7880             cfg.valign = this.valign;
7881         }
7882         
7883         return cfg;
7884     }
7885    
7886 });
7887
7888  
7889
7890  /*
7891  * - LGPL
7892  *
7893  * table body
7894  * 
7895  */
7896
7897 /**
7898  * @class Roo.bootstrap.TableBody
7899  * @extends Roo.bootstrap.Component
7900  * Bootstrap TableBody class
7901  * @cfg {String} cls element class
7902  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7903  * @cfg {String} align Aligns the content inside the element
7904  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7905  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7906  * 
7907  * @constructor
7908  * Create a new TableBody
7909  * @param {Object} config The config object
7910  */
7911
7912 Roo.bootstrap.TableBody = function(config){
7913     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7914 };
7915
7916 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7917     
7918     cls: false,
7919     tag: false,
7920     align: false,
7921     charoff: false,
7922     valign: false,
7923     
7924     getAutoCreate : function(){
7925         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7926         
7927         cfg = {
7928             tag: 'tbody'
7929         };
7930             
7931         if (this.cls) {
7932             cfg.cls=this.cls
7933         }
7934         if(this.tag){
7935             cfg.tag = this.tag;
7936         }
7937         
7938         if(this.align){
7939             cfg.align = this.align;
7940         }
7941         if(this.charoff){
7942             cfg.charoff = this.charoff;
7943         }
7944         if(this.valign){
7945             cfg.valign = this.valign;
7946         }
7947         
7948         return cfg;
7949     }
7950     
7951     
7952 //    initEvents : function()
7953 //    {
7954 //        
7955 //        if(!this.store){
7956 //            return;
7957 //        }
7958 //        
7959 //        this.store = Roo.factory(this.store, Roo.data);
7960 //        this.store.on('load', this.onLoad, this);
7961 //        
7962 //        this.store.load();
7963 //        
7964 //    },
7965 //    
7966 //    onLoad: function () 
7967 //    {   
7968 //        this.fireEvent('load', this);
7969 //    }
7970 //    
7971 //   
7972 });
7973
7974  
7975
7976  /*
7977  * Based on:
7978  * Ext JS Library 1.1.1
7979  * Copyright(c) 2006-2007, Ext JS, LLC.
7980  *
7981  * Originally Released Under LGPL - original licence link has changed is not relivant.
7982  *
7983  * Fork - LGPL
7984  * <script type="text/javascript">
7985  */
7986
7987 // as we use this in bootstrap.
7988 Roo.namespace('Roo.form');
7989  /**
7990  * @class Roo.form.Action
7991  * Internal Class used to handle form actions
7992  * @constructor
7993  * @param {Roo.form.BasicForm} el The form element or its id
7994  * @param {Object} config Configuration options
7995  */
7996
7997  
7998  
7999 // define the action interface
8000 Roo.form.Action = function(form, options){
8001     this.form = form;
8002     this.options = options || {};
8003 };
8004 /**
8005  * Client Validation Failed
8006  * @const 
8007  */
8008 Roo.form.Action.CLIENT_INVALID = 'client';
8009 /**
8010  * Server Validation Failed
8011  * @const 
8012  */
8013 Roo.form.Action.SERVER_INVALID = 'server';
8014  /**
8015  * Connect to Server Failed
8016  * @const 
8017  */
8018 Roo.form.Action.CONNECT_FAILURE = 'connect';
8019 /**
8020  * Reading Data from Server Failed
8021  * @const 
8022  */
8023 Roo.form.Action.LOAD_FAILURE = 'load';
8024
8025 Roo.form.Action.prototype = {
8026     type : 'default',
8027     failureType : undefined,
8028     response : undefined,
8029     result : undefined,
8030
8031     // interface method
8032     run : function(options){
8033
8034     },
8035
8036     // interface method
8037     success : function(response){
8038
8039     },
8040
8041     // interface method
8042     handleResponse : function(response){
8043
8044     },
8045
8046     // default connection failure
8047     failure : function(response){
8048         
8049         this.response = response;
8050         this.failureType = Roo.form.Action.CONNECT_FAILURE;
8051         this.form.afterAction(this, false);
8052     },
8053
8054     processResponse : function(response){
8055         this.response = response;
8056         if(!response.responseText){
8057             return true;
8058         }
8059         this.result = this.handleResponse(response);
8060         return this.result;
8061     },
8062
8063     // utility functions used internally
8064     getUrl : function(appendParams){
8065         var url = this.options.url || this.form.url || this.form.el.dom.action;
8066         if(appendParams){
8067             var p = this.getParams();
8068             if(p){
8069                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
8070             }
8071         }
8072         return url;
8073     },
8074
8075     getMethod : function(){
8076         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
8077     },
8078
8079     getParams : function(){
8080         var bp = this.form.baseParams;
8081         var p = this.options.params;
8082         if(p){
8083             if(typeof p == "object"){
8084                 p = Roo.urlEncode(Roo.applyIf(p, bp));
8085             }else if(typeof p == 'string' && bp){
8086                 p += '&' + Roo.urlEncode(bp);
8087             }
8088         }else if(bp){
8089             p = Roo.urlEncode(bp);
8090         }
8091         return p;
8092     },
8093
8094     createCallback : function(){
8095         return {
8096             success: this.success,
8097             failure: this.failure,
8098             scope: this,
8099             timeout: (this.form.timeout*1000),
8100             upload: this.form.fileUpload ? this.success : undefined
8101         };
8102     }
8103 };
8104
8105 Roo.form.Action.Submit = function(form, options){
8106     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
8107 };
8108
8109 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
8110     type : 'submit',
8111
8112     haveProgress : false,
8113     uploadComplete : false,
8114     
8115     // uploadProgress indicator.
8116     uploadProgress : function()
8117     {
8118         if (!this.form.progressUrl) {
8119             return;
8120         }
8121         
8122         if (!this.haveProgress) {
8123             Roo.MessageBox.progress("Uploading", "Uploading");
8124         }
8125         if (this.uploadComplete) {
8126            Roo.MessageBox.hide();
8127            return;
8128         }
8129         
8130         this.haveProgress = true;
8131    
8132         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
8133         
8134         var c = new Roo.data.Connection();
8135         c.request({
8136             url : this.form.progressUrl,
8137             params: {
8138                 id : uid
8139             },
8140             method: 'GET',
8141             success : function(req){
8142                //console.log(data);
8143                 var rdata = false;
8144                 var edata;
8145                 try  {
8146                    rdata = Roo.decode(req.responseText)
8147                 } catch (e) {
8148                     Roo.log("Invalid data from server..");
8149                     Roo.log(edata);
8150                     return;
8151                 }
8152                 if (!rdata || !rdata.success) {
8153                     Roo.log(rdata);
8154                     Roo.MessageBox.alert(Roo.encode(rdata));
8155                     return;
8156                 }
8157                 var data = rdata.data;
8158                 
8159                 if (this.uploadComplete) {
8160                    Roo.MessageBox.hide();
8161                    return;
8162                 }
8163                    
8164                 if (data){
8165                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
8166                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
8167                     );
8168                 }
8169                 this.uploadProgress.defer(2000,this);
8170             },
8171        
8172             failure: function(data) {
8173                 Roo.log('progress url failed ');
8174                 Roo.log(data);
8175             },
8176             scope : this
8177         });
8178            
8179     },
8180     
8181     
8182     run : function()
8183     {
8184         // run get Values on the form, so it syncs any secondary forms.
8185         this.form.getValues();
8186         
8187         var o = this.options;
8188         var method = this.getMethod();
8189         var isPost = method == 'POST';
8190         if(o.clientValidation === false || this.form.isValid()){
8191             
8192             if (this.form.progressUrl) {
8193                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
8194                     (new Date() * 1) + '' + Math.random());
8195                     
8196             } 
8197             
8198             
8199             Roo.Ajax.request(Roo.apply(this.createCallback(), {
8200                 form:this.form.el.dom,
8201                 url:this.getUrl(!isPost),
8202                 method: method,
8203                 params:isPost ? this.getParams() : null,
8204                 isUpload: this.form.fileUpload,
8205                 formData : this.form.formData
8206             }));
8207             
8208             this.uploadProgress();
8209
8210         }else if (o.clientValidation !== false){ // client validation failed
8211             this.failureType = Roo.form.Action.CLIENT_INVALID;
8212             this.form.afterAction(this, false);
8213         }
8214     },
8215
8216     success : function(response)
8217     {
8218         this.uploadComplete= true;
8219         if (this.haveProgress) {
8220             Roo.MessageBox.hide();
8221         }
8222         
8223         
8224         var result = this.processResponse(response);
8225         if(result === true || result.success){
8226             this.form.afterAction(this, true);
8227             return;
8228         }
8229         if(result.errors){
8230             this.form.markInvalid(result.errors);
8231             this.failureType = Roo.form.Action.SERVER_INVALID;
8232         }
8233         this.form.afterAction(this, false);
8234     },
8235     failure : function(response)
8236     {
8237         this.uploadComplete= true;
8238         if (this.haveProgress) {
8239             Roo.MessageBox.hide();
8240         }
8241         
8242         this.response = response;
8243         this.failureType = Roo.form.Action.CONNECT_FAILURE;
8244         this.form.afterAction(this, false);
8245     },
8246     
8247     handleResponse : function(response){
8248         if(this.form.errorReader){
8249             var rs = this.form.errorReader.read(response);
8250             var errors = [];
8251             if(rs.records){
8252                 for(var i = 0, len = rs.records.length; i < len; i++) {
8253                     var r = rs.records[i];
8254                     errors[i] = r.data;
8255                 }
8256             }
8257             if(errors.length < 1){
8258                 errors = null;
8259             }
8260             return {
8261                 success : rs.success,
8262                 errors : errors
8263             };
8264         }
8265         var ret = false;
8266         try {
8267             ret = Roo.decode(response.responseText);
8268         } catch (e) {
8269             ret = {
8270                 success: false,
8271                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8272                 errors : []
8273             };
8274         }
8275         return ret;
8276         
8277     }
8278 });
8279
8280
8281 Roo.form.Action.Load = function(form, options){
8282     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8283     this.reader = this.form.reader;
8284 };
8285
8286 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8287     type : 'load',
8288
8289     run : function(){
8290         
8291         Roo.Ajax.request(Roo.apply(
8292                 this.createCallback(), {
8293                     method:this.getMethod(),
8294                     url:this.getUrl(false),
8295                     params:this.getParams()
8296         }));
8297     },
8298
8299     success : function(response){
8300         
8301         var result = this.processResponse(response);
8302         if(result === true || !result.success || !result.data){
8303             this.failureType = Roo.form.Action.LOAD_FAILURE;
8304             this.form.afterAction(this, false);
8305             return;
8306         }
8307         this.form.clearInvalid();
8308         this.form.setValues(result.data);
8309         this.form.afterAction(this, true);
8310     },
8311
8312     handleResponse : function(response){
8313         if(this.form.reader){
8314             var rs = this.form.reader.read(response);
8315             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8316             return {
8317                 success : rs.success,
8318                 data : data
8319             };
8320         }
8321         return Roo.decode(response.responseText);
8322     }
8323 });
8324
8325 Roo.form.Action.ACTION_TYPES = {
8326     'load' : Roo.form.Action.Load,
8327     'submit' : Roo.form.Action.Submit
8328 };/*
8329  * - LGPL
8330  *
8331  * form
8332  *
8333  */
8334
8335 /**
8336  * @class Roo.bootstrap.Form
8337  * @extends Roo.bootstrap.Component
8338  * Bootstrap Form class
8339  * @cfg {String} method  GET | POST (default POST)
8340  * @cfg {String} labelAlign top | left (default top)
8341  * @cfg {String} align left  | right - for navbars
8342  * @cfg {Boolean} loadMask load mask when submit (default true)
8343
8344  *
8345  * @constructor
8346  * Create a new Form
8347  * @param {Object} config The config object
8348  */
8349
8350
8351 Roo.bootstrap.Form = function(config){
8352     
8353     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8354     
8355     Roo.bootstrap.Form.popover.apply();
8356     
8357     this.addEvents({
8358         /**
8359          * @event clientvalidation
8360          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8361          * @param {Form} this
8362          * @param {Boolean} valid true if the form has passed client-side validation
8363          */
8364         clientvalidation: true,
8365         /**
8366          * @event beforeaction
8367          * Fires before any action is performed. Return false to cancel the action.
8368          * @param {Form} this
8369          * @param {Action} action The action to be performed
8370          */
8371         beforeaction: true,
8372         /**
8373          * @event actionfailed
8374          * Fires when an action fails.
8375          * @param {Form} this
8376          * @param {Action} action The action that failed
8377          */
8378         actionfailed : true,
8379         /**
8380          * @event actioncomplete
8381          * Fires when an action is completed.
8382          * @param {Form} this
8383          * @param {Action} action The action that completed
8384          */
8385         actioncomplete : true
8386     });
8387 };
8388
8389 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8390
8391      /**
8392      * @cfg {String} method
8393      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8394      */
8395     method : 'POST',
8396     /**
8397      * @cfg {String} url
8398      * The URL to use for form actions if one isn't supplied in the action options.
8399      */
8400     /**
8401      * @cfg {Boolean} fileUpload
8402      * Set to true if this form is a file upload.
8403      */
8404
8405     /**
8406      * @cfg {Object} baseParams
8407      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8408      */
8409
8410     /**
8411      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8412      */
8413     timeout: 30,
8414     /**
8415      * @cfg {Sting} align (left|right) for navbar forms
8416      */
8417     align : 'left',
8418
8419     // private
8420     activeAction : null,
8421
8422     /**
8423      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8424      * element by passing it or its id or mask the form itself by passing in true.
8425      * @type Mixed
8426      */
8427     waitMsgTarget : false,
8428
8429     loadMask : true,
8430     
8431     /**
8432      * @cfg {Boolean} errorMask (true|false) default false
8433      */
8434     errorMask : false,
8435     
8436     /**
8437      * @cfg {Number} maskOffset Default 100
8438      */
8439     maskOffset : 100,
8440     
8441     /**
8442      * @cfg {Boolean} maskBody
8443      */
8444     maskBody : false,
8445
8446     getAutoCreate : function(){
8447
8448         var cfg = {
8449             tag: 'form',
8450             method : this.method || 'POST',
8451             id : this.id || Roo.id(),
8452             cls : ''
8453         };
8454         if (this.parent().xtype.match(/^Nav/)) {
8455             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8456
8457         }
8458
8459         if (this.labelAlign == 'left' ) {
8460             cfg.cls += ' form-horizontal';
8461         }
8462
8463
8464         return cfg;
8465     },
8466     initEvents : function()
8467     {
8468         this.el.on('submit', this.onSubmit, this);
8469         // this was added as random key presses on the form where triggering form submit.
8470         this.el.on('keypress', function(e) {
8471             if (e.getCharCode() != 13) {
8472                 return true;
8473             }
8474             // we might need to allow it for textareas.. and some other items.
8475             // check e.getTarget().
8476
8477             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8478                 return true;
8479             }
8480
8481             Roo.log("keypress blocked");
8482
8483             e.preventDefault();
8484             return false;
8485         });
8486         
8487     },
8488     // private
8489     onSubmit : function(e){
8490         e.stopEvent();
8491     },
8492
8493      /**
8494      * Returns true if client-side validation on the form is successful.
8495      * @return Boolean
8496      */
8497     isValid : function(){
8498         var items = this.getItems();
8499         var valid = true;
8500         var target = false;
8501         
8502         items.each(function(f){
8503             
8504             if(f.validate()){
8505                 return;
8506             }
8507             
8508             Roo.log('invalid field: ' + f.name);
8509             
8510             valid = false;
8511
8512             if(!target && f.el.isVisible(true)){
8513                 target = f;
8514             }
8515            
8516         });
8517         
8518         if(this.errorMask && !valid){
8519             Roo.bootstrap.Form.popover.mask(this, target);
8520         }
8521         
8522         return valid;
8523     },
8524     
8525     /**
8526      * Returns true if any fields in this form have changed since their original load.
8527      * @return Boolean
8528      */
8529     isDirty : function(){
8530         var dirty = false;
8531         var items = this.getItems();
8532         items.each(function(f){
8533            if(f.isDirty()){
8534                dirty = true;
8535                return false;
8536            }
8537            return true;
8538         });
8539         return dirty;
8540     },
8541      /**
8542      * Performs a predefined action (submit or load) or custom actions you define on this form.
8543      * @param {String} actionName The name of the action type
8544      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8545      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8546      * accept other config options):
8547      * <pre>
8548 Property          Type             Description
8549 ----------------  ---------------  ----------------------------------------------------------------------------------
8550 url               String           The url for the action (defaults to the form's url)
8551 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8552 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8553 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8554                                    validate the form on the client (defaults to false)
8555      * </pre>
8556      * @return {BasicForm} this
8557      */
8558     doAction : function(action, options){
8559         if(typeof action == 'string'){
8560             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8561         }
8562         if(this.fireEvent('beforeaction', this, action) !== false){
8563             this.beforeAction(action);
8564             action.run.defer(100, action);
8565         }
8566         return this;
8567     },
8568
8569     // private
8570     beforeAction : function(action){
8571         var o = action.options;
8572         
8573         if(this.loadMask){
8574             
8575             if(this.maskBody){
8576                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8577             } else {
8578                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8579             }
8580         }
8581         // not really supported yet.. ??
8582
8583         //if(this.waitMsgTarget === true){
8584         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8585         //}else if(this.waitMsgTarget){
8586         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8587         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8588         //}else {
8589         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8590        // }
8591
8592     },
8593
8594     // private
8595     afterAction : function(action, success){
8596         this.activeAction = null;
8597         var o = action.options;
8598
8599         if(this.loadMask){
8600             
8601             if(this.maskBody){
8602                 Roo.get(document.body).unmask();
8603             } else {
8604                 this.el.unmask();
8605             }
8606         }
8607         
8608         //if(this.waitMsgTarget === true){
8609 //            this.el.unmask();
8610         //}else if(this.waitMsgTarget){
8611         //    this.waitMsgTarget.unmask();
8612         //}else{
8613         //    Roo.MessageBox.updateProgress(1);
8614         //    Roo.MessageBox.hide();
8615        // }
8616         //
8617         if(success){
8618             if(o.reset){
8619                 this.reset();
8620             }
8621             Roo.callback(o.success, o.scope, [this, action]);
8622             this.fireEvent('actioncomplete', this, action);
8623
8624         }else{
8625
8626             // failure condition..
8627             // we have a scenario where updates need confirming.
8628             // eg. if a locking scenario exists..
8629             // we look for { errors : { needs_confirm : true }} in the response.
8630             if (
8631                 (typeof(action.result) != 'undefined')  &&
8632                 (typeof(action.result.errors) != 'undefined')  &&
8633                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8634            ){
8635                 var _t = this;
8636                 Roo.log("not supported yet");
8637                  /*
8638
8639                 Roo.MessageBox.confirm(
8640                     "Change requires confirmation",
8641                     action.result.errorMsg,
8642                     function(r) {
8643                         if (r != 'yes') {
8644                             return;
8645                         }
8646                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8647                     }
8648
8649                 );
8650                 */
8651
8652
8653                 return;
8654             }
8655
8656             Roo.callback(o.failure, o.scope, [this, action]);
8657             // show an error message if no failed handler is set..
8658             if (!this.hasListener('actionfailed')) {
8659                 Roo.log("need to add dialog support");
8660                 /*
8661                 Roo.MessageBox.alert("Error",
8662                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8663                         action.result.errorMsg :
8664                         "Saving Failed, please check your entries or try again"
8665                 );
8666                 */
8667             }
8668
8669             this.fireEvent('actionfailed', this, action);
8670         }
8671
8672     },
8673     /**
8674      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8675      * @param {String} id The value to search for
8676      * @return Field
8677      */
8678     findField : function(id){
8679         var items = this.getItems();
8680         var field = items.get(id);
8681         if(!field){
8682              items.each(function(f){
8683                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8684                     field = f;
8685                     return false;
8686                 }
8687                 return true;
8688             });
8689         }
8690         return field || null;
8691     },
8692      /**
8693      * Mark fields in this form invalid in bulk.
8694      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8695      * @return {BasicForm} this
8696      */
8697     markInvalid : function(errors){
8698         if(errors instanceof Array){
8699             for(var i = 0, len = errors.length; i < len; i++){
8700                 var fieldError = errors[i];
8701                 var f = this.findField(fieldError.id);
8702                 if(f){
8703                     f.markInvalid(fieldError.msg);
8704                 }
8705             }
8706         }else{
8707             var field, id;
8708             for(id in errors){
8709                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8710                     field.markInvalid(errors[id]);
8711                 }
8712             }
8713         }
8714         //Roo.each(this.childForms || [], function (f) {
8715         //    f.markInvalid(errors);
8716         //});
8717
8718         return this;
8719     },
8720
8721     /**
8722      * Set values for fields in this form in bulk.
8723      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8724      * @return {BasicForm} this
8725      */
8726     setValues : function(values){
8727         if(values instanceof Array){ // array of objects
8728             for(var i = 0, len = values.length; i < len; i++){
8729                 var v = values[i];
8730                 var f = this.findField(v.id);
8731                 if(f){
8732                     f.setValue(v.value);
8733                     if(this.trackResetOnLoad){
8734                         f.originalValue = f.getValue();
8735                     }
8736                 }
8737             }
8738         }else{ // object hash
8739             var field, id;
8740             for(id in values){
8741                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8742
8743                     if (field.setFromData &&
8744                         field.valueField &&
8745                         field.displayField &&
8746                         // combos' with local stores can
8747                         // be queried via setValue()
8748                         // to set their value..
8749                         (field.store && !field.store.isLocal)
8750                         ) {
8751                         // it's a combo
8752                         var sd = { };
8753                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8754                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8755                         field.setFromData(sd);
8756
8757                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8758                         
8759                         field.setFromData(values);
8760                         
8761                     } else {
8762                         field.setValue(values[id]);
8763                     }
8764
8765
8766                     if(this.trackResetOnLoad){
8767                         field.originalValue = field.getValue();
8768                     }
8769                 }
8770             }
8771         }
8772
8773         //Roo.each(this.childForms || [], function (f) {
8774         //    f.setValues(values);
8775         //});
8776
8777         return this;
8778     },
8779
8780     /**
8781      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8782      * they are returned as an array.
8783      * @param {Boolean} asString
8784      * @return {Object}
8785      */
8786     getValues : function(asString){
8787         //if (this.childForms) {
8788             // copy values from the child forms
8789         //    Roo.each(this.childForms, function (f) {
8790         //        this.setValues(f.getValues());
8791         //    }, this);
8792         //}
8793
8794
8795
8796         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8797         if(asString === true){
8798             return fs;
8799         }
8800         return Roo.urlDecode(fs);
8801     },
8802
8803     /**
8804      * Returns the fields in this form as an object with key/value pairs.
8805      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8806      * @return {Object}
8807      */
8808     getFieldValues : function(with_hidden)
8809     {
8810         var items = this.getItems();
8811         var ret = {};
8812         items.each(function(f){
8813             
8814             if (!f.getName()) {
8815                 return;
8816             }
8817             
8818             var v = f.getValue();
8819             
8820             if (f.inputType =='radio') {
8821                 if (typeof(ret[f.getName()]) == 'undefined') {
8822                     ret[f.getName()] = ''; // empty..
8823                 }
8824
8825                 if (!f.el.dom.checked) {
8826                     return;
8827
8828                 }
8829                 v = f.el.dom.value;
8830
8831             }
8832             
8833             if(f.xtype == 'MoneyField'){
8834                 ret[f.currencyName] = f.getCurrency();
8835             }
8836
8837             // not sure if this supported any more..
8838             if ((typeof(v) == 'object') && f.getRawValue) {
8839                 v = f.getRawValue() ; // dates..
8840             }
8841             // combo boxes where name != hiddenName...
8842             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8843                 ret[f.name] = f.getRawValue();
8844             }
8845             ret[f.getName()] = v;
8846         });
8847
8848         return ret;
8849     },
8850
8851     /**
8852      * Clears all invalid messages in this form.
8853      * @return {BasicForm} this
8854      */
8855     clearInvalid : function(){
8856         var items = this.getItems();
8857
8858         items.each(function(f){
8859            f.clearInvalid();
8860         });
8861
8862         return this;
8863     },
8864
8865     /**
8866      * Resets this form.
8867      * @return {BasicForm} this
8868      */
8869     reset : function(){
8870         var items = this.getItems();
8871         items.each(function(f){
8872             f.reset();
8873         });
8874
8875         Roo.each(this.childForms || [], function (f) {
8876             f.reset();
8877         });
8878
8879
8880         return this;
8881     },
8882     
8883     getItems : function()
8884     {
8885         var r=new Roo.util.MixedCollection(false, function(o){
8886             return o.id || (o.id = Roo.id());
8887         });
8888         var iter = function(el) {
8889             if (el.inputEl) {
8890                 r.add(el);
8891             }
8892             if (!el.items) {
8893                 return;
8894             }
8895             Roo.each(el.items,function(e) {
8896                 iter(e);
8897             });
8898         };
8899
8900         iter(this);
8901         return r;
8902     },
8903     
8904     hideFields : function(items)
8905     {
8906         Roo.each(items, function(i){
8907             
8908             var f = this.findField(i);
8909             
8910             if(!f){
8911                 return;
8912             }
8913             
8914             f.hide();
8915             
8916         }, this);
8917     },
8918     
8919     showFields : function(items)
8920     {
8921         Roo.each(items, function(i){
8922             
8923             var f = this.findField(i);
8924             
8925             if(!f){
8926                 return;
8927             }
8928             
8929             f.show();
8930             
8931         }, this);
8932     }
8933
8934 });
8935
8936 Roo.apply(Roo.bootstrap.Form, {
8937     
8938     popover : {
8939         
8940         padding : 5,
8941         
8942         isApplied : false,
8943         
8944         isMasked : false,
8945         
8946         form : false,
8947         
8948         target : false,
8949         
8950         toolTip : false,
8951         
8952         intervalID : false,
8953         
8954         maskEl : false,
8955         
8956         apply : function()
8957         {
8958             if(this.isApplied){
8959                 return;
8960             }
8961             
8962             this.maskEl = {
8963                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8964                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8965                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8966                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8967             };
8968             
8969             this.maskEl.top.enableDisplayMode("block");
8970             this.maskEl.left.enableDisplayMode("block");
8971             this.maskEl.bottom.enableDisplayMode("block");
8972             this.maskEl.right.enableDisplayMode("block");
8973             
8974             this.toolTip = new Roo.bootstrap.Tooltip({
8975                 cls : 'roo-form-error-popover',
8976                 alignment : {
8977                     'left' : ['r-l', [-2,0], 'right'],
8978                     'right' : ['l-r', [2,0], 'left'],
8979                     'bottom' : ['tl-bl', [0,2], 'top'],
8980                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8981                 }
8982             });
8983             
8984             this.toolTip.render(Roo.get(document.body));
8985
8986             this.toolTip.el.enableDisplayMode("block");
8987             
8988             Roo.get(document.body).on('click', function(){
8989                 this.unmask();
8990             }, this);
8991             
8992             Roo.get(document.body).on('touchstart', function(){
8993                 this.unmask();
8994             }, this);
8995             
8996             this.isApplied = true
8997         },
8998         
8999         mask : function(form, target)
9000         {
9001             this.form = form;
9002             
9003             this.target = target;
9004             
9005             if(!this.form.errorMask || !target.el){
9006                 return;
9007             }
9008             
9009             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
9010             
9011             Roo.log(scrollable);
9012             
9013             var ot = this.target.el.calcOffsetsTo(scrollable);
9014             
9015             var scrollTo = ot[1] - this.form.maskOffset;
9016             
9017             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
9018             
9019             scrollable.scrollTo('top', scrollTo);
9020             
9021             var box = this.target.el.getBox();
9022             Roo.log(box);
9023             var zIndex = Roo.bootstrap.Modal.zIndex++;
9024
9025             
9026             this.maskEl.top.setStyle('position', 'absolute');
9027             this.maskEl.top.setStyle('z-index', zIndex);
9028             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
9029             this.maskEl.top.setLeft(0);
9030             this.maskEl.top.setTop(0);
9031             this.maskEl.top.show();
9032             
9033             this.maskEl.left.setStyle('position', 'absolute');
9034             this.maskEl.left.setStyle('z-index', zIndex);
9035             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
9036             this.maskEl.left.setLeft(0);
9037             this.maskEl.left.setTop(box.y - this.padding);
9038             this.maskEl.left.show();
9039
9040             this.maskEl.bottom.setStyle('position', 'absolute');
9041             this.maskEl.bottom.setStyle('z-index', zIndex);
9042             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
9043             this.maskEl.bottom.setLeft(0);
9044             this.maskEl.bottom.setTop(box.bottom + this.padding);
9045             this.maskEl.bottom.show();
9046
9047             this.maskEl.right.setStyle('position', 'absolute');
9048             this.maskEl.right.setStyle('z-index', zIndex);
9049             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
9050             this.maskEl.right.setLeft(box.right + this.padding);
9051             this.maskEl.right.setTop(box.y - this.padding);
9052             this.maskEl.right.show();
9053
9054             this.toolTip.bindEl = this.target.el;
9055
9056             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
9057
9058             var tip = this.target.blankText;
9059
9060             if(this.target.getValue() !== '' ) {
9061                 
9062                 if (this.target.invalidText.length) {
9063                     tip = this.target.invalidText;
9064                 } else if (this.target.regexText.length){
9065                     tip = this.target.regexText;
9066                 }
9067             }
9068
9069             this.toolTip.show(tip);
9070
9071             this.intervalID = window.setInterval(function() {
9072                 Roo.bootstrap.Form.popover.unmask();
9073             }, 10000);
9074
9075             window.onwheel = function(){ return false;};
9076             
9077             (function(){ this.isMasked = true; }).defer(500, this);
9078             
9079         },
9080         
9081         unmask : function()
9082         {
9083             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
9084                 return;
9085             }
9086             
9087             this.maskEl.top.setStyle('position', 'absolute');
9088             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
9089             this.maskEl.top.hide();
9090
9091             this.maskEl.left.setStyle('position', 'absolute');
9092             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
9093             this.maskEl.left.hide();
9094
9095             this.maskEl.bottom.setStyle('position', 'absolute');
9096             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
9097             this.maskEl.bottom.hide();
9098
9099             this.maskEl.right.setStyle('position', 'absolute');
9100             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
9101             this.maskEl.right.hide();
9102             
9103             this.toolTip.hide();
9104             
9105             this.toolTip.el.hide();
9106             
9107             window.onwheel = function(){ return true;};
9108             
9109             if(this.intervalID){
9110                 window.clearInterval(this.intervalID);
9111                 this.intervalID = false;
9112             }
9113             
9114             this.isMasked = false;
9115             
9116         }
9117         
9118     }
9119     
9120 });
9121
9122 /*
9123  * Based on:
9124  * Ext JS Library 1.1.1
9125  * Copyright(c) 2006-2007, Ext JS, LLC.
9126  *
9127  * Originally Released Under LGPL - original licence link has changed is not relivant.
9128  *
9129  * Fork - LGPL
9130  * <script type="text/javascript">
9131  */
9132 /**
9133  * @class Roo.form.VTypes
9134  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
9135  * @singleton
9136  */
9137 Roo.form.VTypes = function(){
9138     // closure these in so they are only created once.
9139     var alpha = /^[a-zA-Z_]+$/;
9140     var alphanum = /^[a-zA-Z0-9_]+$/;
9141     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
9142     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
9143
9144     // All these messages and functions are configurable
9145     return {
9146         /**
9147          * The function used to validate email addresses
9148          * @param {String} value The email address
9149          */
9150         'email' : function(v){
9151             return email.test(v);
9152         },
9153         /**
9154          * The error text to display when the email validation function returns false
9155          * @type String
9156          */
9157         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
9158         /**
9159          * The keystroke filter mask to be applied on email input
9160          * @type RegExp
9161          */
9162         'emailMask' : /[a-z0-9_\.\-@]/i,
9163
9164         /**
9165          * The function used to validate URLs
9166          * @param {String} value The URL
9167          */
9168         'url' : function(v){
9169             return url.test(v);
9170         },
9171         /**
9172          * The error text to display when the url validation function returns false
9173          * @type String
9174          */
9175         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
9176         
9177         /**
9178          * The function used to validate alpha values
9179          * @param {String} value The value
9180          */
9181         'alpha' : function(v){
9182             return alpha.test(v);
9183         },
9184         /**
9185          * The error text to display when the alpha validation function returns false
9186          * @type String
9187          */
9188         'alphaText' : 'This field should only contain letters and _',
9189         /**
9190          * The keystroke filter mask to be applied on alpha input
9191          * @type RegExp
9192          */
9193         'alphaMask' : /[a-z_]/i,
9194
9195         /**
9196          * The function used to validate alphanumeric values
9197          * @param {String} value The value
9198          */
9199         'alphanum' : function(v){
9200             return alphanum.test(v);
9201         },
9202         /**
9203          * The error text to display when the alphanumeric validation function returns false
9204          * @type String
9205          */
9206         'alphanumText' : 'This field should only contain letters, numbers and _',
9207         /**
9208          * The keystroke filter mask to be applied on alphanumeric input
9209          * @type RegExp
9210          */
9211         'alphanumMask' : /[a-z0-9_]/i
9212     };
9213 }();/*
9214  * - LGPL
9215  *
9216  * Input
9217  * 
9218  */
9219
9220 /**
9221  * @class Roo.bootstrap.Input
9222  * @extends Roo.bootstrap.Component
9223  * Bootstrap Input class
9224  * @cfg {Boolean} disabled is it disabled
9225  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
9226  * @cfg {String} name name of the input
9227  * @cfg {string} fieldLabel - the label associated
9228  * @cfg {string} placeholder - placeholder to put in text.
9229  * @cfg {string}  before - input group add on before
9230  * @cfg {string} after - input group add on after
9231  * @cfg {string} size - (lg|sm) or leave empty..
9232  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
9233  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
9234  * @cfg {Number} md colspan out of 12 for computer-sized screens
9235  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
9236  * @cfg {string} value default value of the input
9237  * @cfg {Number} labelWidth set the width of label 
9238  * @cfg {Number} labellg set the width of label (1-12)
9239  * @cfg {Number} labelmd set the width of label (1-12)
9240  * @cfg {Number} labelsm set the width of label (1-12)
9241  * @cfg {Number} labelxs set the width of label (1-12)
9242  * @cfg {String} labelAlign (top|left)
9243  * @cfg {Boolean} readOnly Specifies that the field should be read-only
9244  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9245  * @cfg {String} indicatorpos (left|right) default left
9246  * @cfg {String} capture (user|camera) use for file input only. (default empty)
9247  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9248
9249  * @cfg {String} align (left|center|right) Default left
9250  * @cfg {Boolean} forceFeedback (true|false) Default false
9251  * 
9252  * @constructor
9253  * Create a new Input
9254  * @param {Object} config The config object
9255  */
9256
9257 Roo.bootstrap.Input = function(config){
9258     
9259     Roo.bootstrap.Input.superclass.constructor.call(this, config);
9260     
9261     this.addEvents({
9262         /**
9263          * @event focus
9264          * Fires when this field receives input focus.
9265          * @param {Roo.form.Field} this
9266          */
9267         focus : true,
9268         /**
9269          * @event blur
9270          * Fires when this field loses input focus.
9271          * @param {Roo.form.Field} this
9272          */
9273         blur : true,
9274         /**
9275          * @event specialkey
9276          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9277          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9278          * @param {Roo.form.Field} this
9279          * @param {Roo.EventObject} e The event object
9280          */
9281         specialkey : true,
9282         /**
9283          * @event change
9284          * Fires just before the field blurs if the field value has changed.
9285          * @param {Roo.form.Field} this
9286          * @param {Mixed} newValue The new value
9287          * @param {Mixed} oldValue The original value
9288          */
9289         change : true,
9290         /**
9291          * @event invalid
9292          * Fires after the field has been marked as invalid.
9293          * @param {Roo.form.Field} this
9294          * @param {String} msg The validation message
9295          */
9296         invalid : true,
9297         /**
9298          * @event valid
9299          * Fires after the field has been validated with no errors.
9300          * @param {Roo.form.Field} this
9301          */
9302         valid : true,
9303          /**
9304          * @event keyup
9305          * Fires after the key up
9306          * @param {Roo.form.Field} this
9307          * @param {Roo.EventObject}  e The event Object
9308          */
9309         keyup : true
9310     });
9311 };
9312
9313 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
9314      /**
9315      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9316       automatic validation (defaults to "keyup").
9317      */
9318     validationEvent : "keyup",
9319      /**
9320      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9321      */
9322     validateOnBlur : true,
9323     /**
9324      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9325      */
9326     validationDelay : 250,
9327      /**
9328      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9329      */
9330     focusClass : "x-form-focus",  // not needed???
9331     
9332        
9333     /**
9334      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9335      */
9336     invalidClass : "has-warning",
9337     
9338     /**
9339      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9340      */
9341     validClass : "has-success",
9342     
9343     /**
9344      * @cfg {Boolean} hasFeedback (true|false) default true
9345      */
9346     hasFeedback : true,
9347     
9348     /**
9349      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9350      */
9351     invalidFeedbackClass : "glyphicon-warning-sign",
9352     
9353     /**
9354      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9355      */
9356     validFeedbackClass : "glyphicon-ok",
9357     
9358     /**
9359      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9360      */
9361     selectOnFocus : false,
9362     
9363      /**
9364      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9365      */
9366     maskRe : null,
9367        /**
9368      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9369      */
9370     vtype : null,
9371     
9372       /**
9373      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9374      */
9375     disableKeyFilter : false,
9376     
9377        /**
9378      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9379      */
9380     disabled : false,
9381      /**
9382      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9383      */
9384     allowBlank : true,
9385     /**
9386      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9387      */
9388     blankText : "Please complete this mandatory field",
9389     
9390      /**
9391      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9392      */
9393     minLength : 0,
9394     /**
9395      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9396      */
9397     maxLength : Number.MAX_VALUE,
9398     /**
9399      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9400      */
9401     minLengthText : "The minimum length for this field is {0}",
9402     /**
9403      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9404      */
9405     maxLengthText : "The maximum length for this field is {0}",
9406   
9407     
9408     /**
9409      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9410      * If available, this function will be called only after the basic validators all return true, and will be passed the
9411      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9412      */
9413     validator : null,
9414     /**
9415      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9416      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9417      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9418      */
9419     regex : null,
9420     /**
9421      * @cfg {String} regexText -- Depricated - use Invalid Text
9422      */
9423     regexText : "",
9424     
9425     /**
9426      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9427      */
9428     invalidText : "",
9429     
9430     
9431     
9432     autocomplete: false,
9433     
9434     
9435     fieldLabel : '',
9436     inputType : 'text',
9437     
9438     name : false,
9439     placeholder: false,
9440     before : false,
9441     after : false,
9442     size : false,
9443     hasFocus : false,
9444     preventMark: false,
9445     isFormField : true,
9446     value : '',
9447     labelWidth : 2,
9448     labelAlign : false,
9449     readOnly : false,
9450     align : false,
9451     formatedValue : false,
9452     forceFeedback : false,
9453     
9454     indicatorpos : 'left',
9455     
9456     labellg : 0,
9457     labelmd : 0,
9458     labelsm : 0,
9459     labelxs : 0,
9460     
9461     capture : '',
9462     accept : '',
9463     
9464     parentLabelAlign : function()
9465     {
9466         var parent = this;
9467         while (parent.parent()) {
9468             parent = parent.parent();
9469             if (typeof(parent.labelAlign) !='undefined') {
9470                 return parent.labelAlign;
9471             }
9472         }
9473         return 'left';
9474         
9475     },
9476     
9477     getAutoCreate : function()
9478     {
9479         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9480         
9481         var id = Roo.id();
9482         
9483         var cfg = {};
9484         
9485         if(this.inputType != 'hidden'){
9486             cfg.cls = 'form-group' //input-group
9487         }
9488         
9489         var input =  {
9490             tag: 'input',
9491             id : id,
9492             type : this.inputType,
9493             value : this.value,
9494             cls : 'form-control',
9495             placeholder : this.placeholder || '',
9496             autocomplete : this.autocomplete || 'new-password'
9497         };
9498         
9499         if(this.capture.length){
9500             input.capture = this.capture;
9501         }
9502         
9503         if(this.accept.length){
9504             input.accept = this.accept + "/*";
9505         }
9506         
9507         if(this.align){
9508             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9509         }
9510         
9511         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9512             input.maxLength = this.maxLength;
9513         }
9514         
9515         if (this.disabled) {
9516             input.disabled=true;
9517         }
9518         
9519         if (this.readOnly) {
9520             input.readonly=true;
9521         }
9522         
9523         if (this.name) {
9524             input.name = this.name;
9525         }
9526         
9527         if (this.size) {
9528             input.cls += ' input-' + this.size;
9529         }
9530         
9531         var settings=this;
9532         ['xs','sm','md','lg'].map(function(size){
9533             if (settings[size]) {
9534                 cfg.cls += ' col-' + size + '-' + settings[size];
9535             }
9536         });
9537         
9538         var inputblock = input;
9539         
9540         var feedback = {
9541             tag: 'span',
9542             cls: 'glyphicon form-control-feedback'
9543         };
9544             
9545         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9546             
9547             inputblock = {
9548                 cls : 'has-feedback',
9549                 cn :  [
9550                     input,
9551                     feedback
9552                 ] 
9553             };  
9554         }
9555         
9556         if (this.before || this.after) {
9557             
9558             inputblock = {
9559                 cls : 'input-group',
9560                 cn :  [] 
9561             };
9562             
9563             if (this.before && typeof(this.before) == 'string') {
9564                 
9565                 inputblock.cn.push({
9566                     tag :'span',
9567                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9568                     html : this.before
9569                 });
9570             }
9571             if (this.before && typeof(this.before) == 'object') {
9572                 this.before = Roo.factory(this.before);
9573                 
9574                 inputblock.cn.push({
9575                     tag :'span',
9576                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9577                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9578                 });
9579             }
9580             
9581             inputblock.cn.push(input);
9582             
9583             if (this.after && typeof(this.after) == 'string') {
9584                 inputblock.cn.push({
9585                     tag :'span',
9586                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9587                     html : this.after
9588                 });
9589             }
9590             if (this.after && typeof(this.after) == 'object') {
9591                 this.after = Roo.factory(this.after);
9592                 
9593                 inputblock.cn.push({
9594                     tag :'span',
9595                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9596                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9597                 });
9598             }
9599             
9600             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9601                 inputblock.cls += ' has-feedback';
9602                 inputblock.cn.push(feedback);
9603             }
9604         };
9605         var indicator = {
9606             tag : 'i',
9607             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9608             tooltip : 'This field is required'
9609         };
9610         if (Roo.bootstrap.version == 4) {
9611             indicator = {
9612                 tag : 'i',
9613                 style : 'display-none'
9614             };
9615         }
9616         if (align ==='left' && this.fieldLabel.length) {
9617             
9618             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9619             
9620             cfg.cn = [
9621                 indicator,
9622                 {
9623                     tag: 'label',
9624                     'for' :  id,
9625                     cls : 'control-label col-form-label',
9626                     html : this.fieldLabel
9627
9628                 },
9629                 {
9630                     cls : "", 
9631                     cn: [
9632                         inputblock
9633                     ]
9634                 }
9635             ];
9636             
9637             var labelCfg = cfg.cn[1];
9638             var contentCfg = cfg.cn[2];
9639             
9640             if(this.indicatorpos == 'right'){
9641                 cfg.cn = [
9642                     {
9643                         tag: 'label',
9644                         'for' :  id,
9645                         cls : 'control-label col-form-label',
9646                         cn : [
9647                             {
9648                                 tag : 'span',
9649                                 html : this.fieldLabel
9650                             },
9651                             indicator
9652                         ]
9653                     },
9654                     {
9655                         cls : "",
9656                         cn: [
9657                             inputblock
9658                         ]
9659                     }
9660
9661                 ];
9662                 
9663                 labelCfg = cfg.cn[0];
9664                 contentCfg = cfg.cn[1];
9665             
9666             }
9667             
9668             if(this.labelWidth > 12){
9669                 labelCfg.style = "width: " + this.labelWidth + 'px';
9670             }
9671             
9672             if(this.labelWidth < 13 && this.labelmd == 0){
9673                 this.labelmd = this.labelWidth;
9674             }
9675             
9676             if(this.labellg > 0){
9677                 labelCfg.cls += ' col-lg-' + this.labellg;
9678                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9679             }
9680             
9681             if(this.labelmd > 0){
9682                 labelCfg.cls += ' col-md-' + this.labelmd;
9683                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9684             }
9685             
9686             if(this.labelsm > 0){
9687                 labelCfg.cls += ' col-sm-' + this.labelsm;
9688                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9689             }
9690             
9691             if(this.labelxs > 0){
9692                 labelCfg.cls += ' col-xs-' + this.labelxs;
9693                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9694             }
9695             
9696             
9697         } else if ( this.fieldLabel.length) {
9698                 
9699             cfg.cn = [
9700                 {
9701                     tag : 'i',
9702                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9703                     tooltip : 'This field is required'
9704                 },
9705                 {
9706                     tag: 'label',
9707                    //cls : 'input-group-addon',
9708                     html : this.fieldLabel
9709
9710                 },
9711
9712                inputblock
9713
9714            ];
9715            
9716            if(this.indicatorpos == 'right'){
9717                 
9718                 cfg.cn = [
9719                     {
9720                         tag: 'label',
9721                        //cls : 'input-group-addon',
9722                         html : this.fieldLabel
9723
9724                     },
9725                     {
9726                         tag : 'i',
9727                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9728                         tooltip : 'This field is required'
9729                     },
9730
9731                    inputblock
9732
9733                ];
9734
9735             }
9736
9737         } else {
9738             
9739             cfg.cn = [
9740
9741                     inputblock
9742
9743             ];
9744                 
9745                 
9746         };
9747         
9748         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9749            cfg.cls += ' navbar-form';
9750         }
9751         
9752         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9753             // on BS4 we do this only if not form 
9754             cfg.cls += ' navbar-form';
9755             cfg.tag = 'li';
9756         }
9757         
9758         return cfg;
9759         
9760     },
9761     /**
9762      * return the real input element.
9763      */
9764     inputEl: function ()
9765     {
9766         return this.el.select('input.form-control',true).first();
9767     },
9768     
9769     tooltipEl : function()
9770     {
9771         return this.inputEl();
9772     },
9773     
9774     indicatorEl : function()
9775     {
9776         if (Roo.bootstrap.version == 4) {
9777             return false; // not enabled in v4 yet.
9778         }
9779         
9780         var indicator = this.el.select('i.roo-required-indicator',true).first();
9781         
9782         if(!indicator){
9783             return false;
9784         }
9785         
9786         return indicator;
9787         
9788     },
9789     
9790     setDisabled : function(v)
9791     {
9792         var i  = this.inputEl().dom;
9793         if (!v) {
9794             i.removeAttribute('disabled');
9795             return;
9796             
9797         }
9798         i.setAttribute('disabled','true');
9799     },
9800     initEvents : function()
9801     {
9802           
9803         this.inputEl().on("keydown" , this.fireKey,  this);
9804         this.inputEl().on("focus", this.onFocus,  this);
9805         this.inputEl().on("blur", this.onBlur,  this);
9806         
9807         this.inputEl().relayEvent('keyup', this);
9808         
9809         this.indicator = this.indicatorEl();
9810         
9811         if(this.indicator){
9812             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9813         }
9814  
9815         // reference to original value for reset
9816         this.originalValue = this.getValue();
9817         //Roo.form.TextField.superclass.initEvents.call(this);
9818         if(this.validationEvent == 'keyup'){
9819             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9820             this.inputEl().on('keyup', this.filterValidation, this);
9821         }
9822         else if(this.validationEvent !== false){
9823             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9824         }
9825         
9826         if(this.selectOnFocus){
9827             this.on("focus", this.preFocus, this);
9828             
9829         }
9830         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9831             this.inputEl().on("keypress", this.filterKeys, this);
9832         } else {
9833             this.inputEl().relayEvent('keypress', this);
9834         }
9835        /* if(this.grow){
9836             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9837             this.el.on("click", this.autoSize,  this);
9838         }
9839         */
9840         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9841             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9842         }
9843         
9844         if (typeof(this.before) == 'object') {
9845             this.before.render(this.el.select('.roo-input-before',true).first());
9846         }
9847         if (typeof(this.after) == 'object') {
9848             this.after.render(this.el.select('.roo-input-after',true).first());
9849         }
9850         
9851         this.inputEl().on('change', this.onChange, this);
9852         
9853     },
9854     filterValidation : function(e){
9855         if(!e.isNavKeyPress()){
9856             this.validationTask.delay(this.validationDelay);
9857         }
9858     },
9859      /**
9860      * Validates the field value
9861      * @return {Boolean} True if the value is valid, else false
9862      */
9863     validate : function(){
9864         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9865         if(this.disabled || this.validateValue(this.getRawValue())){
9866             this.markValid();
9867             return true;
9868         }
9869         
9870         this.markInvalid();
9871         return false;
9872     },
9873     
9874     
9875     /**
9876      * Validates a value according to the field's validation rules and marks the field as invalid
9877      * if the validation fails
9878      * @param {Mixed} value The value to validate
9879      * @return {Boolean} True if the value is valid, else false
9880      */
9881     validateValue : function(value)
9882     {
9883         if(this.getVisibilityEl().hasClass('hidden')){
9884             return true;
9885         }
9886         
9887         if(value.length < 1)  { // if it's blank
9888             if(this.allowBlank){
9889                 return true;
9890             }
9891             return false;
9892         }
9893         
9894         if(value.length < this.minLength){
9895             return false;
9896         }
9897         if(value.length > this.maxLength){
9898             return false;
9899         }
9900         if(this.vtype){
9901             var vt = Roo.form.VTypes;
9902             if(!vt[this.vtype](value, this)){
9903                 return false;
9904             }
9905         }
9906         if(typeof this.validator == "function"){
9907             var msg = this.validator(value);
9908             if(msg !== true){
9909                 return false;
9910             }
9911             if (typeof(msg) == 'string') {
9912                 this.invalidText = msg;
9913             }
9914         }
9915         
9916         if(this.regex && !this.regex.test(value)){
9917             return false;
9918         }
9919         
9920         return true;
9921     },
9922     
9923      // private
9924     fireKey : function(e){
9925         //Roo.log('field ' + e.getKey());
9926         if(e.isNavKeyPress()){
9927             this.fireEvent("specialkey", this, e);
9928         }
9929     },
9930     focus : function (selectText){
9931         if(this.rendered){
9932             this.inputEl().focus();
9933             if(selectText === true){
9934                 this.inputEl().dom.select();
9935             }
9936         }
9937         return this;
9938     } ,
9939     
9940     onFocus : function(){
9941         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9942            // this.el.addClass(this.focusClass);
9943         }
9944         if(!this.hasFocus){
9945             this.hasFocus = true;
9946             this.startValue = this.getValue();
9947             this.fireEvent("focus", this);
9948         }
9949     },
9950     
9951     beforeBlur : Roo.emptyFn,
9952
9953     
9954     // private
9955     onBlur : function(){
9956         this.beforeBlur();
9957         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9958             //this.el.removeClass(this.focusClass);
9959         }
9960         this.hasFocus = false;
9961         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9962             this.validate();
9963         }
9964         var v = this.getValue();
9965         if(String(v) !== String(this.startValue)){
9966             this.fireEvent('change', this, v, this.startValue);
9967         }
9968         this.fireEvent("blur", this);
9969     },
9970     
9971     onChange : function(e)
9972     {
9973         var v = this.getValue();
9974         if(String(v) !== String(this.startValue)){
9975             this.fireEvent('change', this, v, this.startValue);
9976         }
9977         
9978     },
9979     
9980     /**
9981      * Resets the current field value to the originally loaded value and clears any validation messages
9982      */
9983     reset : function(){
9984         this.setValue(this.originalValue);
9985         this.validate();
9986     },
9987      /**
9988      * Returns the name of the field
9989      * @return {Mixed} name The name field
9990      */
9991     getName: function(){
9992         return this.name;
9993     },
9994      /**
9995      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9996      * @return {Mixed} value The field value
9997      */
9998     getValue : function(){
9999         
10000         var v = this.inputEl().getValue();
10001         
10002         return v;
10003     },
10004     /**
10005      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
10006      * @return {Mixed} value The field value
10007      */
10008     getRawValue : function(){
10009         var v = this.inputEl().getValue();
10010         
10011         return v;
10012     },
10013     
10014     /**
10015      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
10016      * @param {Mixed} value The value to set
10017      */
10018     setRawValue : function(v){
10019         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10020     },
10021     
10022     selectText : function(start, end){
10023         var v = this.getRawValue();
10024         if(v.length > 0){
10025             start = start === undefined ? 0 : start;
10026             end = end === undefined ? v.length : end;
10027             var d = this.inputEl().dom;
10028             if(d.setSelectionRange){
10029                 d.setSelectionRange(start, end);
10030             }else if(d.createTextRange){
10031                 var range = d.createTextRange();
10032                 range.moveStart("character", start);
10033                 range.moveEnd("character", v.length-end);
10034                 range.select();
10035             }
10036         }
10037     },
10038     
10039     /**
10040      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
10041      * @param {Mixed} value The value to set
10042      */
10043     setValue : function(v){
10044         this.value = v;
10045         if(this.rendered){
10046             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10047             this.validate();
10048         }
10049     },
10050     
10051     /*
10052     processValue : function(value){
10053         if(this.stripCharsRe){
10054             var newValue = value.replace(this.stripCharsRe, '');
10055             if(newValue !== value){
10056                 this.setRawValue(newValue);
10057                 return newValue;
10058             }
10059         }
10060         return value;
10061     },
10062   */
10063     preFocus : function(){
10064         
10065         if(this.selectOnFocus){
10066             this.inputEl().dom.select();
10067         }
10068     },
10069     filterKeys : function(e){
10070         var k = e.getKey();
10071         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
10072             return;
10073         }
10074         var c = e.getCharCode(), cc = String.fromCharCode(c);
10075         if(Roo.isIE && (e.isSpecialKey() || !cc)){
10076             return;
10077         }
10078         if(!this.maskRe.test(cc)){
10079             e.stopEvent();
10080         }
10081     },
10082      /**
10083      * Clear any invalid styles/messages for this field
10084      */
10085     clearInvalid : function(){
10086         
10087         if(!this.el || this.preventMark){ // not rendered
10088             return;
10089         }
10090         
10091         
10092         this.el.removeClass([this.invalidClass, 'is-invalid']);
10093         
10094         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10095             
10096             var feedback = this.el.select('.form-control-feedback', true).first();
10097             
10098             if(feedback){
10099                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10100             }
10101             
10102         }
10103         
10104         if(this.indicator){
10105             this.indicator.removeClass('visible');
10106             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10107         }
10108         
10109         this.fireEvent('valid', this);
10110     },
10111     
10112      /**
10113      * Mark this field as valid
10114      */
10115     markValid : function()
10116     {
10117         if(!this.el  || this.preventMark){ // not rendered...
10118             return;
10119         }
10120         
10121         this.el.removeClass([this.invalidClass, this.validClass]);
10122         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10123
10124         var feedback = this.el.select('.form-control-feedback', true).first();
10125             
10126         if(feedback){
10127             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10128         }
10129         
10130         if(this.indicator){
10131             this.indicator.removeClass('visible');
10132             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10133         }
10134         
10135         if(this.disabled){
10136             return;
10137         }
10138         
10139         if(this.allowBlank && !this.getRawValue().length){
10140             return;
10141         }
10142         if (Roo.bootstrap.version == 3) {
10143             this.el.addClass(this.validClass);
10144         } else {
10145             this.inputEl().addClass('is-valid');
10146         }
10147
10148         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10149             
10150             var feedback = this.el.select('.form-control-feedback', true).first();
10151             
10152             if(feedback){
10153                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10154                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10155             }
10156             
10157         }
10158         
10159         this.fireEvent('valid', this);
10160     },
10161     
10162      /**
10163      * Mark this field as invalid
10164      * @param {String} msg The validation message
10165      */
10166     markInvalid : function(msg)
10167     {
10168         if(!this.el  || this.preventMark){ // not rendered
10169             return;
10170         }
10171         
10172         this.el.removeClass([this.invalidClass, this.validClass]);
10173         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10174         
10175         var feedback = this.el.select('.form-control-feedback', true).first();
10176             
10177         if(feedback){
10178             this.el.select('.form-control-feedback', true).first().removeClass(
10179                     [this.invalidFeedbackClass, this.validFeedbackClass]);
10180         }
10181
10182         if(this.disabled){
10183             return;
10184         }
10185         
10186         if(this.allowBlank && !this.getRawValue().length){
10187             return;
10188         }
10189         
10190         if(this.indicator){
10191             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10192             this.indicator.addClass('visible');
10193         }
10194         if (Roo.bootstrap.version == 3) {
10195             this.el.addClass(this.invalidClass);
10196         } else {
10197             this.inputEl().addClass('is-invalid');
10198         }
10199         
10200         
10201         
10202         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10203             
10204             var feedback = this.el.select('.form-control-feedback', true).first();
10205             
10206             if(feedback){
10207                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10208                 
10209                 if(this.getValue().length || this.forceFeedback){
10210                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10211                 }
10212                 
10213             }
10214             
10215         }
10216         
10217         this.fireEvent('invalid', this, msg);
10218     },
10219     // private
10220     SafariOnKeyDown : function(event)
10221     {
10222         // this is a workaround for a password hang bug on chrome/ webkit.
10223         if (this.inputEl().dom.type != 'password') {
10224             return;
10225         }
10226         
10227         var isSelectAll = false;
10228         
10229         if(this.inputEl().dom.selectionEnd > 0){
10230             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
10231         }
10232         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
10233             event.preventDefault();
10234             this.setValue('');
10235             return;
10236         }
10237         
10238         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
10239             
10240             event.preventDefault();
10241             // this is very hacky as keydown always get's upper case.
10242             //
10243             var cc = String.fromCharCode(event.getCharCode());
10244             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
10245             
10246         }
10247     },
10248     adjustWidth : function(tag, w){
10249         tag = tag.toLowerCase();
10250         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10251             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10252                 if(tag == 'input'){
10253                     return w + 2;
10254                 }
10255                 if(tag == 'textarea'){
10256                     return w-2;
10257                 }
10258             }else if(Roo.isOpera){
10259                 if(tag == 'input'){
10260                     return w + 2;
10261                 }
10262                 if(tag == 'textarea'){
10263                     return w-2;
10264                 }
10265             }
10266         }
10267         return w;
10268     },
10269     
10270     setFieldLabel : function(v)
10271     {
10272         if(!this.rendered){
10273             return;
10274         }
10275         
10276         if(this.indicatorEl()){
10277             var ar = this.el.select('label > span',true);
10278             
10279             if (ar.elements.length) {
10280                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10281                 this.fieldLabel = v;
10282                 return;
10283             }
10284             
10285             var br = this.el.select('label',true);
10286             
10287             if(br.elements.length) {
10288                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10289                 this.fieldLabel = v;
10290                 return;
10291             }
10292             
10293             Roo.log('Cannot Found any of label > span || label in input');
10294             return;
10295         }
10296         
10297         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10298         this.fieldLabel = v;
10299         
10300         
10301     }
10302 });
10303
10304  
10305 /*
10306  * - LGPL
10307  *
10308  * Input
10309  * 
10310  */
10311
10312 /**
10313  * @class Roo.bootstrap.TextArea
10314  * @extends Roo.bootstrap.Input
10315  * Bootstrap TextArea class
10316  * @cfg {Number} cols Specifies the visible width of a text area
10317  * @cfg {Number} rows Specifies the visible number of lines in a text area
10318  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10319  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10320  * @cfg {string} html text
10321  * 
10322  * @constructor
10323  * Create a new TextArea
10324  * @param {Object} config The config object
10325  */
10326
10327 Roo.bootstrap.TextArea = function(config){
10328     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10329    
10330 };
10331
10332 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
10333      
10334     cols : false,
10335     rows : 5,
10336     readOnly : false,
10337     warp : 'soft',
10338     resize : false,
10339     value: false,
10340     html: false,
10341     
10342     getAutoCreate : function(){
10343         
10344         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10345         
10346         var id = Roo.id();
10347         
10348         var cfg = {};
10349         
10350         if(this.inputType != 'hidden'){
10351             cfg.cls = 'form-group' //input-group
10352         }
10353         
10354         var input =  {
10355             tag: 'textarea',
10356             id : id,
10357             warp : this.warp,
10358             rows : this.rows,
10359             value : this.value || '',
10360             html: this.html || '',
10361             cls : 'form-control',
10362             placeholder : this.placeholder || '' 
10363             
10364         };
10365         
10366         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10367             input.maxLength = this.maxLength;
10368         }
10369         
10370         if(this.resize){
10371             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10372         }
10373         
10374         if(this.cols){
10375             input.cols = this.cols;
10376         }
10377         
10378         if (this.readOnly) {
10379             input.readonly = true;
10380         }
10381         
10382         if (this.name) {
10383             input.name = this.name;
10384         }
10385         
10386         if (this.size) {
10387             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10388         }
10389         
10390         var settings=this;
10391         ['xs','sm','md','lg'].map(function(size){
10392             if (settings[size]) {
10393                 cfg.cls += ' col-' + size + '-' + settings[size];
10394             }
10395         });
10396         
10397         var inputblock = input;
10398         
10399         if(this.hasFeedback && !this.allowBlank){
10400             
10401             var feedback = {
10402                 tag: 'span',
10403                 cls: 'glyphicon form-control-feedback'
10404             };
10405
10406             inputblock = {
10407                 cls : 'has-feedback',
10408                 cn :  [
10409                     input,
10410                     feedback
10411                 ] 
10412             };  
10413         }
10414         
10415         
10416         if (this.before || this.after) {
10417             
10418             inputblock = {
10419                 cls : 'input-group',
10420                 cn :  [] 
10421             };
10422             if (this.before) {
10423                 inputblock.cn.push({
10424                     tag :'span',
10425                     cls : 'input-group-addon',
10426                     html : this.before
10427                 });
10428             }
10429             
10430             inputblock.cn.push(input);
10431             
10432             if(this.hasFeedback && !this.allowBlank){
10433                 inputblock.cls += ' has-feedback';
10434                 inputblock.cn.push(feedback);
10435             }
10436             
10437             if (this.after) {
10438                 inputblock.cn.push({
10439                     tag :'span',
10440                     cls : 'input-group-addon',
10441                     html : this.after
10442                 });
10443             }
10444             
10445         }
10446         
10447         if (align ==='left' && this.fieldLabel.length) {
10448             cfg.cn = [
10449                 {
10450                     tag: 'label',
10451                     'for' :  id,
10452                     cls : 'control-label',
10453                     html : this.fieldLabel
10454                 },
10455                 {
10456                     cls : "",
10457                     cn: [
10458                         inputblock
10459                     ]
10460                 }
10461
10462             ];
10463             
10464             if(this.labelWidth > 12){
10465                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10466             }
10467
10468             if(this.labelWidth < 13 && this.labelmd == 0){
10469                 this.labelmd = this.labelWidth;
10470             }
10471
10472             if(this.labellg > 0){
10473                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10474                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10475             }
10476
10477             if(this.labelmd > 0){
10478                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10479                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10480             }
10481
10482             if(this.labelsm > 0){
10483                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10484                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10485             }
10486
10487             if(this.labelxs > 0){
10488                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10489                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10490             }
10491             
10492         } else if ( this.fieldLabel.length) {
10493             cfg.cn = [
10494
10495                {
10496                    tag: 'label',
10497                    //cls : 'input-group-addon',
10498                    html : this.fieldLabel
10499
10500                },
10501
10502                inputblock
10503
10504            ];
10505
10506         } else {
10507
10508             cfg.cn = [
10509
10510                 inputblock
10511
10512             ];
10513                 
10514         }
10515         
10516         if (this.disabled) {
10517             input.disabled=true;
10518         }
10519         
10520         return cfg;
10521         
10522     },
10523     /**
10524      * return the real textarea element.
10525      */
10526     inputEl: function ()
10527     {
10528         return this.el.select('textarea.form-control',true).first();
10529     },
10530     
10531     /**
10532      * Clear any invalid styles/messages for this field
10533      */
10534     clearInvalid : function()
10535     {
10536         
10537         if(!this.el || this.preventMark){ // not rendered
10538             return;
10539         }
10540         
10541         var label = this.el.select('label', true).first();
10542         var icon = this.el.select('i.fa-star', true).first();
10543         
10544         if(label && icon){
10545             icon.remove();
10546         }
10547         this.el.removeClass( this.validClass);
10548         this.inputEl().removeClass('is-invalid');
10549          
10550         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10551             
10552             var feedback = this.el.select('.form-control-feedback', true).first();
10553             
10554             if(feedback){
10555                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10556             }
10557             
10558         }
10559         
10560         this.fireEvent('valid', this);
10561     },
10562     
10563      /**
10564      * Mark this field as valid
10565      */
10566     markValid : function()
10567     {
10568         if(!this.el  || this.preventMark){ // not rendered
10569             return;
10570         }
10571         
10572         this.el.removeClass([this.invalidClass, this.validClass]);
10573         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10574         
10575         var feedback = this.el.select('.form-control-feedback', true).first();
10576             
10577         if(feedback){
10578             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10579         }
10580
10581         if(this.disabled || this.allowBlank){
10582             return;
10583         }
10584         
10585         var label = this.el.select('label', true).first();
10586         var icon = this.el.select('i.fa-star', true).first();
10587         
10588         if(label && icon){
10589             icon.remove();
10590         }
10591         if (Roo.bootstrap.version == 3) {
10592             this.el.addClass(this.validClass);
10593         } else {
10594             this.inputEl().addClass('is-valid');
10595         }
10596         
10597         
10598         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10599             
10600             var feedback = this.el.select('.form-control-feedback', true).first();
10601             
10602             if(feedback){
10603                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10604                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10605             }
10606             
10607         }
10608         
10609         this.fireEvent('valid', this);
10610     },
10611     
10612      /**
10613      * Mark this field as invalid
10614      * @param {String} msg The validation message
10615      */
10616     markInvalid : function(msg)
10617     {
10618         if(!this.el  || this.preventMark){ // not rendered
10619             return;
10620         }
10621         
10622         this.el.removeClass([this.invalidClass, this.validClass]);
10623         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10624         
10625         var feedback = this.el.select('.form-control-feedback', true).first();
10626             
10627         if(feedback){
10628             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10629         }
10630
10631         if(this.disabled || this.allowBlank){
10632             return;
10633         }
10634         
10635         var label = this.el.select('label', true).first();
10636         var icon = this.el.select('i.fa-star', true).first();
10637         
10638         if(!this.getValue().length && label && !icon){
10639             this.el.createChild({
10640                 tag : 'i',
10641                 cls : 'text-danger fa fa-lg fa-star',
10642                 tooltip : 'This field is required',
10643                 style : 'margin-right:5px;'
10644             }, label, true);
10645         }
10646         
10647         if (Roo.bootstrap.version == 3) {
10648             this.el.addClass(this.invalidClass);
10649         } else {
10650             this.inputEl().addClass('is-invalid');
10651         }
10652         
10653         // fixme ... this may be depricated need to test..
10654         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10655             
10656             var feedback = this.el.select('.form-control-feedback', true).first();
10657             
10658             if(feedback){
10659                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10660                 
10661                 if(this.getValue().length || this.forceFeedback){
10662                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10663                 }
10664                 
10665             }
10666             
10667         }
10668         
10669         this.fireEvent('invalid', this, msg);
10670     }
10671 });
10672
10673  
10674 /*
10675  * - LGPL
10676  *
10677  * trigger field - base class for combo..
10678  * 
10679  */
10680  
10681 /**
10682  * @class Roo.bootstrap.TriggerField
10683  * @extends Roo.bootstrap.Input
10684  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10685  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10686  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10687  * for which you can provide a custom implementation.  For example:
10688  * <pre><code>
10689 var trigger = new Roo.bootstrap.TriggerField();
10690 trigger.onTriggerClick = myTriggerFn;
10691 trigger.applyTo('my-field');
10692 </code></pre>
10693  *
10694  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10695  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10696  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10697  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10698  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
10699
10700  * @constructor
10701  * Create a new TriggerField.
10702  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10703  * to the base TextField)
10704  */
10705 Roo.bootstrap.TriggerField = function(config){
10706     this.mimicing = false;
10707     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10708 };
10709
10710 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10711     /**
10712      * @cfg {String} triggerClass A CSS class to apply to the trigger
10713      */
10714      /**
10715      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10716      */
10717     hideTrigger:false,
10718
10719     /**
10720      * @cfg {Boolean} removable (true|false) special filter default false
10721      */
10722     removable : false,
10723     
10724     /** @cfg {Boolean} grow @hide */
10725     /** @cfg {Number} growMin @hide */
10726     /** @cfg {Number} growMax @hide */
10727
10728     /**
10729      * @hide 
10730      * @method
10731      */
10732     autoSize: Roo.emptyFn,
10733     // private
10734     monitorTab : true,
10735     // private
10736     deferHeight : true,
10737
10738     
10739     actionMode : 'wrap',
10740     
10741     caret : false,
10742     
10743     
10744     getAutoCreate : function(){
10745        
10746         var align = this.labelAlign || this.parentLabelAlign();
10747         
10748         var id = Roo.id();
10749         
10750         var cfg = {
10751             cls: 'form-group' //input-group
10752         };
10753         
10754         
10755         var input =  {
10756             tag: 'input',
10757             id : id,
10758             type : this.inputType,
10759             cls : 'form-control',
10760             autocomplete: 'new-password',
10761             placeholder : this.placeholder || '' 
10762             
10763         };
10764         if (this.name) {
10765             input.name = this.name;
10766         }
10767         if (this.size) {
10768             input.cls += ' input-' + this.size;
10769         }
10770         
10771         if (this.disabled) {
10772             input.disabled=true;
10773         }
10774         
10775         var inputblock = input;
10776         
10777         if(this.hasFeedback && !this.allowBlank){
10778             
10779             var feedback = {
10780                 tag: 'span',
10781                 cls: 'glyphicon form-control-feedback'
10782             };
10783             
10784             if(this.removable && !this.editable && !this.tickable){
10785                 inputblock = {
10786                     cls : 'has-feedback',
10787                     cn :  [
10788                         inputblock,
10789                         {
10790                             tag: 'button',
10791                             html : 'x',
10792                             cls : 'roo-combo-removable-btn close'
10793                         },
10794                         feedback
10795                     ] 
10796                 };
10797             } else {
10798                 inputblock = {
10799                     cls : 'has-feedback',
10800                     cn :  [
10801                         inputblock,
10802                         feedback
10803                     ] 
10804                 };
10805             }
10806
10807         } else {
10808             if(this.removable && !this.editable && !this.tickable){
10809                 inputblock = {
10810                     cls : 'roo-removable',
10811                     cn :  [
10812                         inputblock,
10813                         {
10814                             tag: 'button',
10815                             html : 'x',
10816                             cls : 'roo-combo-removable-btn close'
10817                         }
10818                     ] 
10819                 };
10820             }
10821         }
10822         
10823         if (this.before || this.after) {
10824             
10825             inputblock = {
10826                 cls : 'input-group',
10827                 cn :  [] 
10828             };
10829             if (this.before) {
10830                 inputblock.cn.push({
10831                     tag :'span',
10832                     cls : 'input-group-addon input-group-prepend input-group-text',
10833                     html : this.before
10834                 });
10835             }
10836             
10837             inputblock.cn.push(input);
10838             
10839             if(this.hasFeedback && !this.allowBlank){
10840                 inputblock.cls += ' has-feedback';
10841                 inputblock.cn.push(feedback);
10842             }
10843             
10844             if (this.after) {
10845                 inputblock.cn.push({
10846                     tag :'span',
10847                     cls : 'input-group-addon input-group-append input-group-text',
10848                     html : this.after
10849                 });
10850             }
10851             
10852         };
10853         
10854       
10855         
10856         var ibwrap = inputblock;
10857         
10858         if(this.multiple){
10859             ibwrap = {
10860                 tag: 'ul',
10861                 cls: 'roo-select2-choices',
10862                 cn:[
10863                     {
10864                         tag: 'li',
10865                         cls: 'roo-select2-search-field',
10866                         cn: [
10867
10868                             inputblock
10869                         ]
10870                     }
10871                 ]
10872             };
10873                 
10874         }
10875         
10876         var combobox = {
10877             cls: 'roo-select2-container input-group',
10878             cn: [
10879                  {
10880                     tag: 'input',
10881                     type : 'hidden',
10882                     cls: 'form-hidden-field'
10883                 },
10884                 ibwrap
10885             ]
10886         };
10887         
10888         if(!this.multiple && this.showToggleBtn){
10889             
10890             var caret = {
10891                         tag: 'span',
10892                         cls: 'caret'
10893              };
10894             if (this.caret != false) {
10895                 caret = {
10896                      tag: 'i',
10897                      cls: 'fa fa-' + this.caret
10898                 };
10899                 
10900             }
10901             
10902             combobox.cn.push({
10903                 tag :'span',
10904                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10905                 cn : [
10906                     Roo.bootstrap.version == 3 ? caret : '',
10907                     {
10908                         tag: 'span',
10909                         cls: 'combobox-clear',
10910                         cn  : [
10911                             {
10912                                 tag : 'i',
10913                                 cls: 'icon-remove'
10914                             }
10915                         ]
10916                     }
10917                 ]
10918
10919             })
10920         }
10921         
10922         if(this.multiple){
10923             combobox.cls += ' roo-select2-container-multi';
10924         }
10925          var indicator = {
10926             tag : 'i',
10927             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10928             tooltip : 'This field is required'
10929         };
10930         if (Roo.bootstrap.version == 4) {
10931             indicator = {
10932                 tag : 'i',
10933                 style : 'display:none'
10934             };
10935         }
10936         
10937         
10938         if (align ==='left' && this.fieldLabel.length) {
10939             
10940             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10941
10942             cfg.cn = [
10943                 indicator,
10944                 {
10945                     tag: 'label',
10946                     'for' :  id,
10947                     cls : 'control-label',
10948                     html : this.fieldLabel
10949
10950                 },
10951                 {
10952                     cls : "", 
10953                     cn: [
10954                         combobox
10955                     ]
10956                 }
10957
10958             ];
10959             
10960             var labelCfg = cfg.cn[1];
10961             var contentCfg = cfg.cn[2];
10962             
10963             if(this.indicatorpos == 'right'){
10964                 cfg.cn = [
10965                     {
10966                         tag: 'label',
10967                         'for' :  id,
10968                         cls : 'control-label',
10969                         cn : [
10970                             {
10971                                 tag : 'span',
10972                                 html : this.fieldLabel
10973                             },
10974                             indicator
10975                         ]
10976                     },
10977                     {
10978                         cls : "", 
10979                         cn: [
10980                             combobox
10981                         ]
10982                     }
10983
10984                 ];
10985                 
10986                 labelCfg = cfg.cn[0];
10987                 contentCfg = cfg.cn[1];
10988             }
10989             
10990             if(this.labelWidth > 12){
10991                 labelCfg.style = "width: " + this.labelWidth + 'px';
10992             }
10993             
10994             if(this.labelWidth < 13 && this.labelmd == 0){
10995                 this.labelmd = this.labelWidth;
10996             }
10997             
10998             if(this.labellg > 0){
10999                 labelCfg.cls += ' col-lg-' + this.labellg;
11000                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11001             }
11002             
11003             if(this.labelmd > 0){
11004                 labelCfg.cls += ' col-md-' + this.labelmd;
11005                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11006             }
11007             
11008             if(this.labelsm > 0){
11009                 labelCfg.cls += ' col-sm-' + this.labelsm;
11010                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11011             }
11012             
11013             if(this.labelxs > 0){
11014                 labelCfg.cls += ' col-xs-' + this.labelxs;
11015                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11016             }
11017             
11018         } else if ( this.fieldLabel.length) {
11019 //                Roo.log(" label");
11020             cfg.cn = [
11021                 indicator,
11022                {
11023                    tag: 'label',
11024                    //cls : 'input-group-addon',
11025                    html : this.fieldLabel
11026
11027                },
11028
11029                combobox
11030
11031             ];
11032             
11033             if(this.indicatorpos == 'right'){
11034                 
11035                 cfg.cn = [
11036                     {
11037                        tag: 'label',
11038                        cn : [
11039                            {
11040                                tag : 'span',
11041                                html : this.fieldLabel
11042                            },
11043                            indicator
11044                        ]
11045
11046                     },
11047                     combobox
11048
11049                 ];
11050
11051             }
11052
11053         } else {
11054             
11055 //                Roo.log(" no label && no align");
11056                 cfg = combobox
11057                      
11058                 
11059         }
11060         
11061         var settings=this;
11062         ['xs','sm','md','lg'].map(function(size){
11063             if (settings[size]) {
11064                 cfg.cls += ' col-' + size + '-' + settings[size];
11065             }
11066         });
11067         
11068         return cfg;
11069         
11070     },
11071     
11072     
11073     
11074     // private
11075     onResize : function(w, h){
11076 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
11077 //        if(typeof w == 'number'){
11078 //            var x = w - this.trigger.getWidth();
11079 //            this.inputEl().setWidth(this.adjustWidth('input', x));
11080 //            this.trigger.setStyle('left', x+'px');
11081 //        }
11082     },
11083
11084     // private
11085     adjustSize : Roo.BoxComponent.prototype.adjustSize,
11086
11087     // private
11088     getResizeEl : function(){
11089         return this.inputEl();
11090     },
11091
11092     // private
11093     getPositionEl : function(){
11094         return this.inputEl();
11095     },
11096
11097     // private
11098     alignErrorIcon : function(){
11099         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
11100     },
11101
11102     // private
11103     initEvents : function(){
11104         
11105         this.createList();
11106         
11107         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
11108         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
11109         if(!this.multiple && this.showToggleBtn){
11110             this.trigger = this.el.select('span.dropdown-toggle',true).first();
11111             if(this.hideTrigger){
11112                 this.trigger.setDisplayed(false);
11113             }
11114             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
11115         }
11116         
11117         if(this.multiple){
11118             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
11119         }
11120         
11121         if(this.removable && !this.editable && !this.tickable){
11122             var close = this.closeTriggerEl();
11123             
11124             if(close){
11125                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
11126                 close.on('click', this.removeBtnClick, this, close);
11127             }
11128         }
11129         
11130         //this.trigger.addClassOnOver('x-form-trigger-over');
11131         //this.trigger.addClassOnClick('x-form-trigger-click');
11132         
11133         //if(!this.width){
11134         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
11135         //}
11136     },
11137     
11138     closeTriggerEl : function()
11139     {
11140         var close = this.el.select('.roo-combo-removable-btn', true).first();
11141         return close ? close : false;
11142     },
11143     
11144     removeBtnClick : function(e, h, el)
11145     {
11146         e.preventDefault();
11147         
11148         if(this.fireEvent("remove", this) !== false){
11149             this.reset();
11150             this.fireEvent("afterremove", this)
11151         }
11152     },
11153     
11154     createList : function()
11155     {
11156         this.list = Roo.get(document.body).createChild({
11157             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
11158             cls: 'typeahead typeahead-long dropdown-menu',
11159             style: 'display:none'
11160         });
11161         
11162         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
11163         
11164     },
11165
11166     // private
11167     initTrigger : function(){
11168        
11169     },
11170
11171     // private
11172     onDestroy : function(){
11173         if(this.trigger){
11174             this.trigger.removeAllListeners();
11175           //  this.trigger.remove();
11176         }
11177         //if(this.wrap){
11178         //    this.wrap.remove();
11179         //}
11180         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
11181     },
11182
11183     // private
11184     onFocus : function(){
11185         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
11186         /*
11187         if(!this.mimicing){
11188             this.wrap.addClass('x-trigger-wrap-focus');
11189             this.mimicing = true;
11190             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
11191             if(this.monitorTab){
11192                 this.el.on("keydown", this.checkTab, this);
11193             }
11194         }
11195         */
11196     },
11197
11198     // private
11199     checkTab : function(e){
11200         if(e.getKey() == e.TAB){
11201             this.triggerBlur();
11202         }
11203     },
11204
11205     // private
11206     onBlur : function(){
11207         // do nothing
11208     },
11209
11210     // private
11211     mimicBlur : function(e, t){
11212         /*
11213         if(!this.wrap.contains(t) && this.validateBlur()){
11214             this.triggerBlur();
11215         }
11216         */
11217     },
11218
11219     // private
11220     triggerBlur : function(){
11221         this.mimicing = false;
11222         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
11223         if(this.monitorTab){
11224             this.el.un("keydown", this.checkTab, this);
11225         }
11226         //this.wrap.removeClass('x-trigger-wrap-focus');
11227         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
11228     },
11229
11230     // private
11231     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
11232     validateBlur : function(e, t){
11233         return true;
11234     },
11235
11236     // private
11237     onDisable : function(){
11238         this.inputEl().dom.disabled = true;
11239         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
11240         //if(this.wrap){
11241         //    this.wrap.addClass('x-item-disabled');
11242         //}
11243     },
11244
11245     // private
11246     onEnable : function(){
11247         this.inputEl().dom.disabled = false;
11248         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11249         //if(this.wrap){
11250         //    this.el.removeClass('x-item-disabled');
11251         //}
11252     },
11253
11254     // private
11255     onShow : function(){
11256         var ae = this.getActionEl();
11257         
11258         if(ae){
11259             ae.dom.style.display = '';
11260             ae.dom.style.visibility = 'visible';
11261         }
11262     },
11263
11264     // private
11265     
11266     onHide : function(){
11267         var ae = this.getActionEl();
11268         ae.dom.style.display = 'none';
11269     },
11270
11271     /**
11272      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
11273      * by an implementing function.
11274      * @method
11275      * @param {EventObject} e
11276      */
11277     onTriggerClick : Roo.emptyFn
11278 });
11279  /*
11280  * Based on:
11281  * Ext JS Library 1.1.1
11282  * Copyright(c) 2006-2007, Ext JS, LLC.
11283  *
11284  * Originally Released Under LGPL - original licence link has changed is not relivant.
11285  *
11286  * Fork - LGPL
11287  * <script type="text/javascript">
11288  */
11289
11290
11291 /**
11292  * @class Roo.data.SortTypes
11293  * @singleton
11294  * Defines the default sorting (casting?) comparison functions used when sorting data.
11295  */
11296 Roo.data.SortTypes = {
11297     /**
11298      * Default sort that does nothing
11299      * @param {Mixed} s The value being converted
11300      * @return {Mixed} The comparison value
11301      */
11302     none : function(s){
11303         return s;
11304     },
11305     
11306     /**
11307      * The regular expression used to strip tags
11308      * @type {RegExp}
11309      * @property
11310      */
11311     stripTagsRE : /<\/?[^>]+>/gi,
11312     
11313     /**
11314      * Strips all HTML tags to sort on text only
11315      * @param {Mixed} s The value being converted
11316      * @return {String} The comparison value
11317      */
11318     asText : function(s){
11319         return String(s).replace(this.stripTagsRE, "");
11320     },
11321     
11322     /**
11323      * Strips all HTML tags to sort on text only - Case insensitive
11324      * @param {Mixed} s The value being converted
11325      * @return {String} The comparison value
11326      */
11327     asUCText : function(s){
11328         return String(s).toUpperCase().replace(this.stripTagsRE, "");
11329     },
11330     
11331     /**
11332      * Case insensitive string
11333      * @param {Mixed} s The value being converted
11334      * @return {String} The comparison value
11335      */
11336     asUCString : function(s) {
11337         return String(s).toUpperCase();
11338     },
11339     
11340     /**
11341      * Date sorting
11342      * @param {Mixed} s The value being converted
11343      * @return {Number} The comparison value
11344      */
11345     asDate : function(s) {
11346         if(!s){
11347             return 0;
11348         }
11349         if(s instanceof Date){
11350             return s.getTime();
11351         }
11352         return Date.parse(String(s));
11353     },
11354     
11355     /**
11356      * Float sorting
11357      * @param {Mixed} s The value being converted
11358      * @return {Float} The comparison value
11359      */
11360     asFloat : function(s) {
11361         var val = parseFloat(String(s).replace(/,/g, ""));
11362         if(isNaN(val)) {
11363             val = 0;
11364         }
11365         return val;
11366     },
11367     
11368     /**
11369      * Integer sorting
11370      * @param {Mixed} s The value being converted
11371      * @return {Number} The comparison value
11372      */
11373     asInt : function(s) {
11374         var val = parseInt(String(s).replace(/,/g, ""));
11375         if(isNaN(val)) {
11376             val = 0;
11377         }
11378         return val;
11379     }
11380 };/*
11381  * Based on:
11382  * Ext JS Library 1.1.1
11383  * Copyright(c) 2006-2007, Ext JS, LLC.
11384  *
11385  * Originally Released Under LGPL - original licence link has changed is not relivant.
11386  *
11387  * Fork - LGPL
11388  * <script type="text/javascript">
11389  */
11390
11391 /**
11392 * @class Roo.data.Record
11393  * Instances of this class encapsulate both record <em>definition</em> information, and record
11394  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11395  * to access Records cached in an {@link Roo.data.Store} object.<br>
11396  * <p>
11397  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11398  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11399  * objects.<br>
11400  * <p>
11401  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11402  * @constructor
11403  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11404  * {@link #create}. The parameters are the same.
11405  * @param {Array} data An associative Array of data values keyed by the field name.
11406  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11407  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11408  * not specified an integer id is generated.
11409  */
11410 Roo.data.Record = function(data, id){
11411     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11412     this.data = data;
11413 };
11414
11415 /**
11416  * Generate a constructor for a specific record layout.
11417  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11418  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11419  * Each field definition object may contain the following properties: <ul>
11420  * <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,
11421  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11422  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11423  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11424  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11425  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11426  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11427  * this may be omitted.</p></li>
11428  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11429  * <ul><li>auto (Default, implies no conversion)</li>
11430  * <li>string</li>
11431  * <li>int</li>
11432  * <li>float</li>
11433  * <li>boolean</li>
11434  * <li>date</li></ul></p></li>
11435  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11436  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11437  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11438  * by the Reader into an object that will be stored in the Record. It is passed the
11439  * following parameters:<ul>
11440  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11441  * </ul></p></li>
11442  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11443  * </ul>
11444  * <br>usage:<br><pre><code>
11445 var TopicRecord = Roo.data.Record.create(
11446     {name: 'title', mapping: 'topic_title'},
11447     {name: 'author', mapping: 'username'},
11448     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11449     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11450     {name: 'lastPoster', mapping: 'user2'},
11451     {name: 'excerpt', mapping: 'post_text'}
11452 );
11453
11454 var myNewRecord = new TopicRecord({
11455     title: 'Do my job please',
11456     author: 'noobie',
11457     totalPosts: 1,
11458     lastPost: new Date(),
11459     lastPoster: 'Animal',
11460     excerpt: 'No way dude!'
11461 });
11462 myStore.add(myNewRecord);
11463 </code></pre>
11464  * @method create
11465  * @static
11466  */
11467 Roo.data.Record.create = function(o){
11468     var f = function(){
11469         f.superclass.constructor.apply(this, arguments);
11470     };
11471     Roo.extend(f, Roo.data.Record);
11472     var p = f.prototype;
11473     p.fields = new Roo.util.MixedCollection(false, function(field){
11474         return field.name;
11475     });
11476     for(var i = 0, len = o.length; i < len; i++){
11477         p.fields.add(new Roo.data.Field(o[i]));
11478     }
11479     f.getField = function(name){
11480         return p.fields.get(name);  
11481     };
11482     return f;
11483 };
11484
11485 Roo.data.Record.AUTO_ID = 1000;
11486 Roo.data.Record.EDIT = 'edit';
11487 Roo.data.Record.REJECT = 'reject';
11488 Roo.data.Record.COMMIT = 'commit';
11489
11490 Roo.data.Record.prototype = {
11491     /**
11492      * Readonly flag - true if this record has been modified.
11493      * @type Boolean
11494      */
11495     dirty : false,
11496     editing : false,
11497     error: null,
11498     modified: null,
11499
11500     // private
11501     join : function(store){
11502         this.store = store;
11503     },
11504
11505     /**
11506      * Set the named field to the specified value.
11507      * @param {String} name The name of the field to set.
11508      * @param {Object} value The value to set the field to.
11509      */
11510     set : function(name, value){
11511         if(this.data[name] == value){
11512             return;
11513         }
11514         this.dirty = true;
11515         if(!this.modified){
11516             this.modified = {};
11517         }
11518         if(typeof this.modified[name] == 'undefined'){
11519             this.modified[name] = this.data[name];
11520         }
11521         this.data[name] = value;
11522         if(!this.editing && this.store){
11523             this.store.afterEdit(this);
11524         }       
11525     },
11526
11527     /**
11528      * Get the value of the named field.
11529      * @param {String} name The name of the field to get the value of.
11530      * @return {Object} The value of the field.
11531      */
11532     get : function(name){
11533         return this.data[name]; 
11534     },
11535
11536     // private
11537     beginEdit : function(){
11538         this.editing = true;
11539         this.modified = {}; 
11540     },
11541
11542     // private
11543     cancelEdit : function(){
11544         this.editing = false;
11545         delete this.modified;
11546     },
11547
11548     // private
11549     endEdit : function(){
11550         this.editing = false;
11551         if(this.dirty && this.store){
11552             this.store.afterEdit(this);
11553         }
11554     },
11555
11556     /**
11557      * Usually called by the {@link Roo.data.Store} which owns the Record.
11558      * Rejects all changes made to the Record since either creation, or the last commit operation.
11559      * Modified fields are reverted to their original values.
11560      * <p>
11561      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11562      * of reject operations.
11563      */
11564     reject : function(){
11565         var m = this.modified;
11566         for(var n in m){
11567             if(typeof m[n] != "function"){
11568                 this.data[n] = m[n];
11569             }
11570         }
11571         this.dirty = false;
11572         delete this.modified;
11573         this.editing = false;
11574         if(this.store){
11575             this.store.afterReject(this);
11576         }
11577     },
11578
11579     /**
11580      * Usually called by the {@link Roo.data.Store} which owns the Record.
11581      * Commits all changes made to the Record since either creation, or the last commit operation.
11582      * <p>
11583      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11584      * of commit operations.
11585      */
11586     commit : function(){
11587         this.dirty = false;
11588         delete this.modified;
11589         this.editing = false;
11590         if(this.store){
11591             this.store.afterCommit(this);
11592         }
11593     },
11594
11595     // private
11596     hasError : function(){
11597         return this.error != null;
11598     },
11599
11600     // private
11601     clearError : function(){
11602         this.error = null;
11603     },
11604
11605     /**
11606      * Creates a copy of this record.
11607      * @param {String} id (optional) A new record id if you don't want to use this record's id
11608      * @return {Record}
11609      */
11610     copy : function(newId) {
11611         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11612     }
11613 };/*
11614  * Based on:
11615  * Ext JS Library 1.1.1
11616  * Copyright(c) 2006-2007, Ext JS, LLC.
11617  *
11618  * Originally Released Under LGPL - original licence link has changed is not relivant.
11619  *
11620  * Fork - LGPL
11621  * <script type="text/javascript">
11622  */
11623
11624
11625
11626 /**
11627  * @class Roo.data.Store
11628  * @extends Roo.util.Observable
11629  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11630  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11631  * <p>
11632  * 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
11633  * has no knowledge of the format of the data returned by the Proxy.<br>
11634  * <p>
11635  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11636  * instances from the data object. These records are cached and made available through accessor functions.
11637  * @constructor
11638  * Creates a new Store.
11639  * @param {Object} config A config object containing the objects needed for the Store to access data,
11640  * and read the data into Records.
11641  */
11642 Roo.data.Store = function(config){
11643     this.data = new Roo.util.MixedCollection(false);
11644     this.data.getKey = function(o){
11645         return o.id;
11646     };
11647     this.baseParams = {};
11648     // private
11649     this.paramNames = {
11650         "start" : "start",
11651         "limit" : "limit",
11652         "sort" : "sort",
11653         "dir" : "dir",
11654         "multisort" : "_multisort"
11655     };
11656
11657     if(config && config.data){
11658         this.inlineData = config.data;
11659         delete config.data;
11660     }
11661
11662     Roo.apply(this, config);
11663     
11664     if(this.reader){ // reader passed
11665         this.reader = Roo.factory(this.reader, Roo.data);
11666         this.reader.xmodule = this.xmodule || false;
11667         if(!this.recordType){
11668             this.recordType = this.reader.recordType;
11669         }
11670         if(this.reader.onMetaChange){
11671             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11672         }
11673     }
11674
11675     if(this.recordType){
11676         this.fields = this.recordType.prototype.fields;
11677     }
11678     this.modified = [];
11679
11680     this.addEvents({
11681         /**
11682          * @event datachanged
11683          * Fires when the data cache has changed, and a widget which is using this Store
11684          * as a Record cache should refresh its view.
11685          * @param {Store} this
11686          */
11687         datachanged : true,
11688         /**
11689          * @event metachange
11690          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11691          * @param {Store} this
11692          * @param {Object} meta The JSON metadata
11693          */
11694         metachange : true,
11695         /**
11696          * @event add
11697          * Fires when Records have been added to the Store
11698          * @param {Store} this
11699          * @param {Roo.data.Record[]} records The array of Records added
11700          * @param {Number} index The index at which the record(s) were added
11701          */
11702         add : true,
11703         /**
11704          * @event remove
11705          * Fires when a Record has been removed from the Store
11706          * @param {Store} this
11707          * @param {Roo.data.Record} record The Record that was removed
11708          * @param {Number} index The index at which the record was removed
11709          */
11710         remove : true,
11711         /**
11712          * @event update
11713          * Fires when a Record has been updated
11714          * @param {Store} this
11715          * @param {Roo.data.Record} record The Record that was updated
11716          * @param {String} operation The update operation being performed.  Value may be one of:
11717          * <pre><code>
11718  Roo.data.Record.EDIT
11719  Roo.data.Record.REJECT
11720  Roo.data.Record.COMMIT
11721          * </code></pre>
11722          */
11723         update : true,
11724         /**
11725          * @event clear
11726          * Fires when the data cache has been cleared.
11727          * @param {Store} this
11728          */
11729         clear : true,
11730         /**
11731          * @event beforeload
11732          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11733          * the load action will be canceled.
11734          * @param {Store} this
11735          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11736          */
11737         beforeload : true,
11738         /**
11739          * @event beforeloadadd
11740          * Fires after a new set of Records has been loaded.
11741          * @param {Store} this
11742          * @param {Roo.data.Record[]} records The Records that were loaded
11743          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11744          */
11745         beforeloadadd : true,
11746         /**
11747          * @event load
11748          * Fires after a new set of Records has been loaded, before they are added to the store.
11749          * @param {Store} this
11750          * @param {Roo.data.Record[]} records The Records that were loaded
11751          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11752          * @params {Object} return from reader
11753          */
11754         load : true,
11755         /**
11756          * @event loadexception
11757          * Fires if an exception occurs in the Proxy during loading.
11758          * Called with the signature of the Proxy's "loadexception" event.
11759          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11760          * 
11761          * @param {Proxy} 
11762          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11763          * @param {Object} load options 
11764          * @param {Object} jsonData from your request (normally this contains the Exception)
11765          */
11766         loadexception : true
11767     });
11768     
11769     if(this.proxy){
11770         this.proxy = Roo.factory(this.proxy, Roo.data);
11771         this.proxy.xmodule = this.xmodule || false;
11772         this.relayEvents(this.proxy,  ["loadexception"]);
11773     }
11774     this.sortToggle = {};
11775     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11776
11777     Roo.data.Store.superclass.constructor.call(this);
11778
11779     if(this.inlineData){
11780         this.loadData(this.inlineData);
11781         delete this.inlineData;
11782     }
11783 };
11784
11785 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11786      /**
11787     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11788     * without a remote query - used by combo/forms at present.
11789     */
11790     
11791     /**
11792     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11793     */
11794     /**
11795     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11796     */
11797     /**
11798     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11799     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11800     */
11801     /**
11802     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11803     * on any HTTP request
11804     */
11805     /**
11806     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11807     */
11808     /**
11809     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11810     */
11811     multiSort: false,
11812     /**
11813     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11814     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11815     */
11816     remoteSort : false,
11817
11818     /**
11819     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11820      * loaded or when a record is removed. (defaults to false).
11821     */
11822     pruneModifiedRecords : false,
11823
11824     // private
11825     lastOptions : null,
11826
11827     /**
11828      * Add Records to the Store and fires the add event.
11829      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11830      */
11831     add : function(records){
11832         records = [].concat(records);
11833         for(var i = 0, len = records.length; i < len; i++){
11834             records[i].join(this);
11835         }
11836         var index = this.data.length;
11837         this.data.addAll(records);
11838         this.fireEvent("add", this, records, index);
11839     },
11840
11841     /**
11842      * Remove a Record from the Store and fires the remove event.
11843      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11844      */
11845     remove : function(record){
11846         var index = this.data.indexOf(record);
11847         this.data.removeAt(index);
11848  
11849         if(this.pruneModifiedRecords){
11850             this.modified.remove(record);
11851         }
11852         this.fireEvent("remove", this, record, index);
11853     },
11854
11855     /**
11856      * Remove all Records from the Store and fires the clear event.
11857      */
11858     removeAll : function(){
11859         this.data.clear();
11860         if(this.pruneModifiedRecords){
11861             this.modified = [];
11862         }
11863         this.fireEvent("clear", this);
11864     },
11865
11866     /**
11867      * Inserts Records to the Store at the given index and fires the add event.
11868      * @param {Number} index The start index at which to insert the passed Records.
11869      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11870      */
11871     insert : function(index, records){
11872         records = [].concat(records);
11873         for(var i = 0, len = records.length; i < len; i++){
11874             this.data.insert(index, records[i]);
11875             records[i].join(this);
11876         }
11877         this.fireEvent("add", this, records, index);
11878     },
11879
11880     /**
11881      * Get the index within the cache of the passed Record.
11882      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11883      * @return {Number} The index of the passed Record. Returns -1 if not found.
11884      */
11885     indexOf : function(record){
11886         return this.data.indexOf(record);
11887     },
11888
11889     /**
11890      * Get the index within the cache of the Record with the passed id.
11891      * @param {String} id The id of the Record to find.
11892      * @return {Number} The index of the Record. Returns -1 if not found.
11893      */
11894     indexOfId : function(id){
11895         return this.data.indexOfKey(id);
11896     },
11897
11898     /**
11899      * Get the Record with the specified id.
11900      * @param {String} id The id of the Record to find.
11901      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11902      */
11903     getById : function(id){
11904         return this.data.key(id);
11905     },
11906
11907     /**
11908      * Get the Record at the specified index.
11909      * @param {Number} index The index of the Record to find.
11910      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11911      */
11912     getAt : function(index){
11913         return this.data.itemAt(index);
11914     },
11915
11916     /**
11917      * Returns a range of Records between specified indices.
11918      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11919      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11920      * @return {Roo.data.Record[]} An array of Records
11921      */
11922     getRange : function(start, end){
11923         return this.data.getRange(start, end);
11924     },
11925
11926     // private
11927     storeOptions : function(o){
11928         o = Roo.apply({}, o);
11929         delete o.callback;
11930         delete o.scope;
11931         this.lastOptions = o;
11932     },
11933
11934     /**
11935      * Loads the Record cache from the configured Proxy using the configured Reader.
11936      * <p>
11937      * If using remote paging, then the first load call must specify the <em>start</em>
11938      * and <em>limit</em> properties in the options.params property to establish the initial
11939      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11940      * <p>
11941      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11942      * and this call will return before the new data has been loaded. Perform any post-processing
11943      * in a callback function, or in a "load" event handler.</strong>
11944      * <p>
11945      * @param {Object} options An object containing properties which control loading options:<ul>
11946      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11947      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11948      * passed the following arguments:<ul>
11949      * <li>r : Roo.data.Record[]</li>
11950      * <li>options: Options object from the load call</li>
11951      * <li>success: Boolean success indicator</li></ul></li>
11952      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11953      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11954      * </ul>
11955      */
11956     load : function(options){
11957         options = options || {};
11958         if(this.fireEvent("beforeload", this, options) !== false){
11959             this.storeOptions(options);
11960             var p = Roo.apply(options.params || {}, this.baseParams);
11961             // if meta was not loaded from remote source.. try requesting it.
11962             if (!this.reader.metaFromRemote) {
11963                 p._requestMeta = 1;
11964             }
11965             if(this.sortInfo && this.remoteSort){
11966                 var pn = this.paramNames;
11967                 p[pn["sort"]] = this.sortInfo.field;
11968                 p[pn["dir"]] = this.sortInfo.direction;
11969             }
11970             if (this.multiSort) {
11971                 var pn = this.paramNames;
11972                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11973             }
11974             
11975             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11976         }
11977     },
11978
11979     /**
11980      * Reloads the Record cache from the configured Proxy using the configured Reader and
11981      * the options from the last load operation performed.
11982      * @param {Object} options (optional) An object containing properties which may override the options
11983      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11984      * the most recently used options are reused).
11985      */
11986     reload : function(options){
11987         this.load(Roo.applyIf(options||{}, this.lastOptions));
11988     },
11989
11990     // private
11991     // Called as a callback by the Reader during a load operation.
11992     loadRecords : function(o, options, success){
11993         if(!o || success === false){
11994             if(success !== false){
11995                 this.fireEvent("load", this, [], options, o);
11996             }
11997             if(options.callback){
11998                 options.callback.call(options.scope || this, [], options, false);
11999             }
12000             return;
12001         }
12002         // if data returned failure - throw an exception.
12003         if (o.success === false) {
12004             // show a message if no listener is registered.
12005             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
12006                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
12007             }
12008             // loadmask wil be hooked into this..
12009             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
12010             return;
12011         }
12012         var r = o.records, t = o.totalRecords || r.length;
12013         
12014         this.fireEvent("beforeloadadd", this, r, options, o);
12015         
12016         if(!options || options.add !== true){
12017             if(this.pruneModifiedRecords){
12018                 this.modified = [];
12019             }
12020             for(var i = 0, len = r.length; i < len; i++){
12021                 r[i].join(this);
12022             }
12023             if(this.snapshot){
12024                 this.data = this.snapshot;
12025                 delete this.snapshot;
12026             }
12027             this.data.clear();
12028             this.data.addAll(r);
12029             this.totalLength = t;
12030             this.applySort();
12031             this.fireEvent("datachanged", this);
12032         }else{
12033             this.totalLength = Math.max(t, this.data.length+r.length);
12034             this.add(r);
12035         }
12036         
12037         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
12038                 
12039             var e = new Roo.data.Record({});
12040
12041             e.set(this.parent.displayField, this.parent.emptyTitle);
12042             e.set(this.parent.valueField, '');
12043
12044             this.insert(0, e);
12045         }
12046             
12047         this.fireEvent("load", this, r, options, o);
12048         if(options.callback){
12049             options.callback.call(options.scope || this, r, options, true);
12050         }
12051     },
12052
12053
12054     /**
12055      * Loads data from a passed data block. A Reader which understands the format of the data
12056      * must have been configured in the constructor.
12057      * @param {Object} data The data block from which to read the Records.  The format of the data expected
12058      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
12059      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
12060      */
12061     loadData : function(o, append){
12062         var r = this.reader.readRecords(o);
12063         this.loadRecords(r, {add: append}, true);
12064     },
12065     
12066      /**
12067      * using 'cn' the nested child reader read the child array into it's child stores.
12068      * @param {Object} rec The record with a 'children array
12069      */
12070     loadDataFromChildren : function(rec)
12071     {
12072         this.loadData(this.reader.toLoadData(rec));
12073     },
12074     
12075
12076     /**
12077      * Gets the number of cached records.
12078      * <p>
12079      * <em>If using paging, this may not be the total size of the dataset. If the data object
12080      * used by the Reader contains the dataset size, then the getTotalCount() function returns
12081      * the data set size</em>
12082      */
12083     getCount : function(){
12084         return this.data.length || 0;
12085     },
12086
12087     /**
12088      * Gets the total number of records in the dataset as returned by the server.
12089      * <p>
12090      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
12091      * the dataset size</em>
12092      */
12093     getTotalCount : function(){
12094         return this.totalLength || 0;
12095     },
12096
12097     /**
12098      * Returns the sort state of the Store as an object with two properties:
12099      * <pre><code>
12100  field {String} The name of the field by which the Records are sorted
12101  direction {String} The sort order, "ASC" or "DESC"
12102      * </code></pre>
12103      */
12104     getSortState : function(){
12105         return this.sortInfo;
12106     },
12107
12108     // private
12109     applySort : function(){
12110         if(this.sortInfo && !this.remoteSort){
12111             var s = this.sortInfo, f = s.field;
12112             var st = this.fields.get(f).sortType;
12113             var fn = function(r1, r2){
12114                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
12115                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
12116             };
12117             this.data.sort(s.direction, fn);
12118             if(this.snapshot && this.snapshot != this.data){
12119                 this.snapshot.sort(s.direction, fn);
12120             }
12121         }
12122     },
12123
12124     /**
12125      * Sets the default sort column and order to be used by the next load operation.
12126      * @param {String} fieldName The name of the field to sort by.
12127      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12128      */
12129     setDefaultSort : function(field, dir){
12130         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
12131     },
12132
12133     /**
12134      * Sort the Records.
12135      * If remote sorting is used, the sort is performed on the server, and the cache is
12136      * reloaded. If local sorting is used, the cache is sorted internally.
12137      * @param {String} fieldName The name of the field to sort by.
12138      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12139      */
12140     sort : function(fieldName, dir){
12141         var f = this.fields.get(fieldName);
12142         if(!dir){
12143             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
12144             
12145             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
12146                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
12147             }else{
12148                 dir = f.sortDir;
12149             }
12150         }
12151         this.sortToggle[f.name] = dir;
12152         this.sortInfo = {field: f.name, direction: dir};
12153         if(!this.remoteSort){
12154             this.applySort();
12155             this.fireEvent("datachanged", this);
12156         }else{
12157             this.load(this.lastOptions);
12158         }
12159     },
12160
12161     /**
12162      * Calls the specified function for each of the Records in the cache.
12163      * @param {Function} fn The function to call. The Record is passed as the first parameter.
12164      * Returning <em>false</em> aborts and exits the iteration.
12165      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
12166      */
12167     each : function(fn, scope){
12168         this.data.each(fn, scope);
12169     },
12170
12171     /**
12172      * Gets all records modified since the last commit.  Modified records are persisted across load operations
12173      * (e.g., during paging).
12174      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
12175      */
12176     getModifiedRecords : function(){
12177         return this.modified;
12178     },
12179
12180     // private
12181     createFilterFn : function(property, value, anyMatch){
12182         if(!value.exec){ // not a regex
12183             value = String(value);
12184             if(value.length == 0){
12185                 return false;
12186             }
12187             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
12188         }
12189         return function(r){
12190             return value.test(r.data[property]);
12191         };
12192     },
12193
12194     /**
12195      * Sums the value of <i>property</i> for each record between start and end and returns the result.
12196      * @param {String} property A field on your records
12197      * @param {Number} start The record index to start at (defaults to 0)
12198      * @param {Number} end The last record index to include (defaults to length - 1)
12199      * @return {Number} The sum
12200      */
12201     sum : function(property, start, end){
12202         var rs = this.data.items, v = 0;
12203         start = start || 0;
12204         end = (end || end === 0) ? end : rs.length-1;
12205
12206         for(var i = start; i <= end; i++){
12207             v += (rs[i].data[property] || 0);
12208         }
12209         return v;
12210     },
12211
12212     /**
12213      * Filter the records by a specified property.
12214      * @param {String} field A field on your records
12215      * @param {String/RegExp} value Either a string that the field
12216      * should start with or a RegExp to test against the field
12217      * @param {Boolean} anyMatch True to match any part not just the beginning
12218      */
12219     filter : function(property, value, anyMatch){
12220         var fn = this.createFilterFn(property, value, anyMatch);
12221         return fn ? this.filterBy(fn) : this.clearFilter();
12222     },
12223
12224     /**
12225      * Filter by a function. The specified function will be called with each
12226      * record in this data source. If the function returns true the record is included,
12227      * otherwise it is filtered.
12228      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12229      * @param {Object} scope (optional) The scope of the function (defaults to this)
12230      */
12231     filterBy : function(fn, scope){
12232         this.snapshot = this.snapshot || this.data;
12233         this.data = this.queryBy(fn, scope||this);
12234         this.fireEvent("datachanged", this);
12235     },
12236
12237     /**
12238      * Query the records by a specified property.
12239      * @param {String} field A field on your records
12240      * @param {String/RegExp} value Either a string that the field
12241      * should start with or a RegExp to test against the field
12242      * @param {Boolean} anyMatch True to match any part not just the beginning
12243      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12244      */
12245     query : function(property, value, anyMatch){
12246         var fn = this.createFilterFn(property, value, anyMatch);
12247         return fn ? this.queryBy(fn) : this.data.clone();
12248     },
12249
12250     /**
12251      * Query by a function. The specified function will be called with each
12252      * record in this data source. If the function returns true the record is included
12253      * in the results.
12254      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12255      * @param {Object} scope (optional) The scope of the function (defaults to this)
12256       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12257      **/
12258     queryBy : function(fn, scope){
12259         var data = this.snapshot || this.data;
12260         return data.filterBy(fn, scope||this);
12261     },
12262
12263     /**
12264      * Collects unique values for a particular dataIndex from this store.
12265      * @param {String} dataIndex The property to collect
12266      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12267      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12268      * @return {Array} An array of the unique values
12269      **/
12270     collect : function(dataIndex, allowNull, bypassFilter){
12271         var d = (bypassFilter === true && this.snapshot) ?
12272                 this.snapshot.items : this.data.items;
12273         var v, sv, r = [], l = {};
12274         for(var i = 0, len = d.length; i < len; i++){
12275             v = d[i].data[dataIndex];
12276             sv = String(v);
12277             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12278                 l[sv] = true;
12279                 r[r.length] = v;
12280             }
12281         }
12282         return r;
12283     },
12284
12285     /**
12286      * Revert to a view of the Record cache with no filtering applied.
12287      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12288      */
12289     clearFilter : function(suppressEvent){
12290         if(this.snapshot && this.snapshot != this.data){
12291             this.data = this.snapshot;
12292             delete this.snapshot;
12293             if(suppressEvent !== true){
12294                 this.fireEvent("datachanged", this);
12295             }
12296         }
12297     },
12298
12299     // private
12300     afterEdit : function(record){
12301         if(this.modified.indexOf(record) == -1){
12302             this.modified.push(record);
12303         }
12304         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12305     },
12306     
12307     // private
12308     afterReject : function(record){
12309         this.modified.remove(record);
12310         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12311     },
12312
12313     // private
12314     afterCommit : function(record){
12315         this.modified.remove(record);
12316         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12317     },
12318
12319     /**
12320      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12321      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12322      */
12323     commitChanges : function(){
12324         var m = this.modified.slice(0);
12325         this.modified = [];
12326         for(var i = 0, len = m.length; i < len; i++){
12327             m[i].commit();
12328         }
12329     },
12330
12331     /**
12332      * Cancel outstanding changes on all changed records.
12333      */
12334     rejectChanges : function(){
12335         var m = this.modified.slice(0);
12336         this.modified = [];
12337         for(var i = 0, len = m.length; i < len; i++){
12338             m[i].reject();
12339         }
12340     },
12341
12342     onMetaChange : function(meta, rtype, o){
12343         this.recordType = rtype;
12344         this.fields = rtype.prototype.fields;
12345         delete this.snapshot;
12346         this.sortInfo = meta.sortInfo || this.sortInfo;
12347         this.modified = [];
12348         this.fireEvent('metachange', this, this.reader.meta);
12349     },
12350     
12351     moveIndex : function(data, type)
12352     {
12353         var index = this.indexOf(data);
12354         
12355         var newIndex = index + type;
12356         
12357         this.remove(data);
12358         
12359         this.insert(newIndex, data);
12360         
12361     }
12362 });/*
12363  * Based on:
12364  * Ext JS Library 1.1.1
12365  * Copyright(c) 2006-2007, Ext JS, LLC.
12366  *
12367  * Originally Released Under LGPL - original licence link has changed is not relivant.
12368  *
12369  * Fork - LGPL
12370  * <script type="text/javascript">
12371  */
12372
12373 /**
12374  * @class Roo.data.SimpleStore
12375  * @extends Roo.data.Store
12376  * Small helper class to make creating Stores from Array data easier.
12377  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12378  * @cfg {Array} fields An array of field definition objects, or field name strings.
12379  * @cfg {Object} an existing reader (eg. copied from another store)
12380  * @cfg {Array} data The multi-dimensional array of data
12381  * @constructor
12382  * @param {Object} config
12383  */
12384 Roo.data.SimpleStore = function(config)
12385 {
12386     Roo.data.SimpleStore.superclass.constructor.call(this, {
12387         isLocal : true,
12388         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
12389                 id: config.id
12390             },
12391             Roo.data.Record.create(config.fields)
12392         ),
12393         proxy : new Roo.data.MemoryProxy(config.data)
12394     });
12395     this.load();
12396 };
12397 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12398  * Based on:
12399  * Ext JS Library 1.1.1
12400  * Copyright(c) 2006-2007, Ext JS, LLC.
12401  *
12402  * Originally Released Under LGPL - original licence link has changed is not relivant.
12403  *
12404  * Fork - LGPL
12405  * <script type="text/javascript">
12406  */
12407
12408 /**
12409 /**
12410  * @extends Roo.data.Store
12411  * @class Roo.data.JsonStore
12412  * Small helper class to make creating Stores for JSON data easier. <br/>
12413 <pre><code>
12414 var store = new Roo.data.JsonStore({
12415     url: 'get-images.php',
12416     root: 'images',
12417     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12418 });
12419 </code></pre>
12420  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12421  * JsonReader and HttpProxy (unless inline data is provided).</b>
12422  * @cfg {Array} fields An array of field definition objects, or field name strings.
12423  * @constructor
12424  * @param {Object} config
12425  */
12426 Roo.data.JsonStore = function(c){
12427     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12428         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12429         reader: new Roo.data.JsonReader(c, c.fields)
12430     }));
12431 };
12432 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12433  * Based on:
12434  * Ext JS Library 1.1.1
12435  * Copyright(c) 2006-2007, Ext JS, LLC.
12436  *
12437  * Originally Released Under LGPL - original licence link has changed is not relivant.
12438  *
12439  * Fork - LGPL
12440  * <script type="text/javascript">
12441  */
12442
12443  
12444 Roo.data.Field = function(config){
12445     if(typeof config == "string"){
12446         config = {name: config};
12447     }
12448     Roo.apply(this, config);
12449     
12450     if(!this.type){
12451         this.type = "auto";
12452     }
12453     
12454     var st = Roo.data.SortTypes;
12455     // named sortTypes are supported, here we look them up
12456     if(typeof this.sortType == "string"){
12457         this.sortType = st[this.sortType];
12458     }
12459     
12460     // set default sortType for strings and dates
12461     if(!this.sortType){
12462         switch(this.type){
12463             case "string":
12464                 this.sortType = st.asUCString;
12465                 break;
12466             case "date":
12467                 this.sortType = st.asDate;
12468                 break;
12469             default:
12470                 this.sortType = st.none;
12471         }
12472     }
12473
12474     // define once
12475     var stripRe = /[\$,%]/g;
12476
12477     // prebuilt conversion function for this field, instead of
12478     // switching every time we're reading a value
12479     if(!this.convert){
12480         var cv, dateFormat = this.dateFormat;
12481         switch(this.type){
12482             case "":
12483             case "auto":
12484             case undefined:
12485                 cv = function(v){ return v; };
12486                 break;
12487             case "string":
12488                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12489                 break;
12490             case "int":
12491                 cv = function(v){
12492                     return v !== undefined && v !== null && v !== '' ?
12493                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12494                     };
12495                 break;
12496             case "float":
12497                 cv = function(v){
12498                     return v !== undefined && v !== null && v !== '' ?
12499                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12500                     };
12501                 break;
12502             case "bool":
12503             case "boolean":
12504                 cv = function(v){ return v === true || v === "true" || v == 1; };
12505                 break;
12506             case "date":
12507                 cv = function(v){
12508                     if(!v){
12509                         return '';
12510                     }
12511                     if(v instanceof Date){
12512                         return v;
12513                     }
12514                     if(dateFormat){
12515                         if(dateFormat == "timestamp"){
12516                             return new Date(v*1000);
12517                         }
12518                         return Date.parseDate(v, dateFormat);
12519                     }
12520                     var parsed = Date.parse(v);
12521                     return parsed ? new Date(parsed) : null;
12522                 };
12523              break;
12524             
12525         }
12526         this.convert = cv;
12527     }
12528 };
12529
12530 Roo.data.Field.prototype = {
12531     dateFormat: null,
12532     defaultValue: "",
12533     mapping: null,
12534     sortType : null,
12535     sortDir : "ASC"
12536 };/*
12537  * Based on:
12538  * Ext JS Library 1.1.1
12539  * Copyright(c) 2006-2007, Ext JS, LLC.
12540  *
12541  * Originally Released Under LGPL - original licence link has changed is not relivant.
12542  *
12543  * Fork - LGPL
12544  * <script type="text/javascript">
12545  */
12546  
12547 // Base class for reading structured data from a data source.  This class is intended to be
12548 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12549
12550 /**
12551  * @class Roo.data.DataReader
12552  * Base class for reading structured data from a data source.  This class is intended to be
12553  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12554  */
12555
12556 Roo.data.DataReader = function(meta, recordType){
12557     
12558     this.meta = meta;
12559     
12560     this.recordType = recordType instanceof Array ? 
12561         Roo.data.Record.create(recordType) : recordType;
12562 };
12563
12564 Roo.data.DataReader.prototype = {
12565     
12566     
12567     readerType : 'Data',
12568      /**
12569      * Create an empty record
12570      * @param {Object} data (optional) - overlay some values
12571      * @return {Roo.data.Record} record created.
12572      */
12573     newRow :  function(d) {
12574         var da =  {};
12575         this.recordType.prototype.fields.each(function(c) {
12576             switch( c.type) {
12577                 case 'int' : da[c.name] = 0; break;
12578                 case 'date' : da[c.name] = new Date(); break;
12579                 case 'float' : da[c.name] = 0.0; break;
12580                 case 'boolean' : da[c.name] = false; break;
12581                 default : da[c.name] = ""; break;
12582             }
12583             
12584         });
12585         return new this.recordType(Roo.apply(da, d));
12586     }
12587     
12588     
12589 };/*
12590  * Based on:
12591  * Ext JS Library 1.1.1
12592  * Copyright(c) 2006-2007, Ext JS, LLC.
12593  *
12594  * Originally Released Under LGPL - original licence link has changed is not relivant.
12595  *
12596  * Fork - LGPL
12597  * <script type="text/javascript">
12598  */
12599
12600 /**
12601  * @class Roo.data.DataProxy
12602  * @extends Roo.data.Observable
12603  * This class is an abstract base class for implementations which provide retrieval of
12604  * unformatted data objects.<br>
12605  * <p>
12606  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12607  * (of the appropriate type which knows how to parse the data object) to provide a block of
12608  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12609  * <p>
12610  * Custom implementations must implement the load method as described in
12611  * {@link Roo.data.HttpProxy#load}.
12612  */
12613 Roo.data.DataProxy = function(){
12614     this.addEvents({
12615         /**
12616          * @event beforeload
12617          * Fires before a network request is made to retrieve a data object.
12618          * @param {Object} This DataProxy object.
12619          * @param {Object} params The params parameter to the load function.
12620          */
12621         beforeload : true,
12622         /**
12623          * @event load
12624          * Fires before the load method's callback is called.
12625          * @param {Object} This DataProxy object.
12626          * @param {Object} o The data object.
12627          * @param {Object} arg The callback argument object passed to the load function.
12628          */
12629         load : true,
12630         /**
12631          * @event loadexception
12632          * Fires if an Exception occurs during data retrieval.
12633          * @param {Object} This DataProxy object.
12634          * @param {Object} o The data object.
12635          * @param {Object} arg The callback argument object passed to the load function.
12636          * @param {Object} e The Exception.
12637          */
12638         loadexception : true
12639     });
12640     Roo.data.DataProxy.superclass.constructor.call(this);
12641 };
12642
12643 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12644
12645     /**
12646      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12647      */
12648 /*
12649  * Based on:
12650  * Ext JS Library 1.1.1
12651  * Copyright(c) 2006-2007, Ext JS, LLC.
12652  *
12653  * Originally Released Under LGPL - original licence link has changed is not relivant.
12654  *
12655  * Fork - LGPL
12656  * <script type="text/javascript">
12657  */
12658 /**
12659  * @class Roo.data.MemoryProxy
12660  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12661  * to the Reader when its load method is called.
12662  * @constructor
12663  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12664  */
12665 Roo.data.MemoryProxy = function(data){
12666     if (data.data) {
12667         data = data.data;
12668     }
12669     Roo.data.MemoryProxy.superclass.constructor.call(this);
12670     this.data = data;
12671 };
12672
12673 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12674     
12675     /**
12676      * Load data from the requested source (in this case an in-memory
12677      * data object passed to the constructor), read the data object into
12678      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12679      * process that block using the passed callback.
12680      * @param {Object} params This parameter is not used by the MemoryProxy class.
12681      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12682      * object into a block of Roo.data.Records.
12683      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12684      * The function must be passed <ul>
12685      * <li>The Record block object</li>
12686      * <li>The "arg" argument from the load function</li>
12687      * <li>A boolean success indicator</li>
12688      * </ul>
12689      * @param {Object} scope The scope in which to call the callback
12690      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12691      */
12692     load : function(params, reader, callback, scope, arg){
12693         params = params || {};
12694         var result;
12695         try {
12696             result = reader.readRecords(params.data ? params.data :this.data);
12697         }catch(e){
12698             this.fireEvent("loadexception", this, arg, null, e);
12699             callback.call(scope, null, arg, false);
12700             return;
12701         }
12702         callback.call(scope, result, arg, true);
12703     },
12704     
12705     // private
12706     update : function(params, records){
12707         
12708     }
12709 });/*
12710  * Based on:
12711  * Ext JS Library 1.1.1
12712  * Copyright(c) 2006-2007, Ext JS, LLC.
12713  *
12714  * Originally Released Under LGPL - original licence link has changed is not relivant.
12715  *
12716  * Fork - LGPL
12717  * <script type="text/javascript">
12718  */
12719 /**
12720  * @class Roo.data.HttpProxy
12721  * @extends Roo.data.DataProxy
12722  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12723  * configured to reference a certain URL.<br><br>
12724  * <p>
12725  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12726  * from which the running page was served.<br><br>
12727  * <p>
12728  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12729  * <p>
12730  * Be aware that to enable the browser to parse an XML document, the server must set
12731  * the Content-Type header in the HTTP response to "text/xml".
12732  * @constructor
12733  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12734  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12735  * will be used to make the request.
12736  */
12737 Roo.data.HttpProxy = function(conn){
12738     Roo.data.HttpProxy.superclass.constructor.call(this);
12739     // is conn a conn config or a real conn?
12740     this.conn = conn;
12741     this.useAjax = !conn || !conn.events;
12742   
12743 };
12744
12745 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12746     // thse are take from connection...
12747     
12748     /**
12749      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12750      */
12751     /**
12752      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12753      * extra parameters to each request made by this object. (defaults to undefined)
12754      */
12755     /**
12756      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12757      *  to each request made by this object. (defaults to undefined)
12758      */
12759     /**
12760      * @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)
12761      */
12762     /**
12763      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12764      */
12765      /**
12766      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12767      * @type Boolean
12768      */
12769   
12770
12771     /**
12772      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12773      * @type Boolean
12774      */
12775     /**
12776      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12777      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12778      * a finer-grained basis than the DataProxy events.
12779      */
12780     getConnection : function(){
12781         return this.useAjax ? Roo.Ajax : this.conn;
12782     },
12783
12784     /**
12785      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12786      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12787      * process that block using the passed callback.
12788      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12789      * for the request to the remote server.
12790      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12791      * object into a block of Roo.data.Records.
12792      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12793      * The function must be passed <ul>
12794      * <li>The Record block object</li>
12795      * <li>The "arg" argument from the load function</li>
12796      * <li>A boolean success indicator</li>
12797      * </ul>
12798      * @param {Object} scope The scope in which to call the callback
12799      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12800      */
12801     load : function(params, reader, callback, scope, arg){
12802         if(this.fireEvent("beforeload", this, params) !== false){
12803             var  o = {
12804                 params : params || {},
12805                 request: {
12806                     callback : callback,
12807                     scope : scope,
12808                     arg : arg
12809                 },
12810                 reader: reader,
12811                 callback : this.loadResponse,
12812                 scope: this
12813             };
12814             if(this.useAjax){
12815                 Roo.applyIf(o, this.conn);
12816                 if(this.activeRequest){
12817                     Roo.Ajax.abort(this.activeRequest);
12818                 }
12819                 this.activeRequest = Roo.Ajax.request(o);
12820             }else{
12821                 this.conn.request(o);
12822             }
12823         }else{
12824             callback.call(scope||this, null, arg, false);
12825         }
12826     },
12827
12828     // private
12829     loadResponse : function(o, success, response){
12830         delete this.activeRequest;
12831         if(!success){
12832             this.fireEvent("loadexception", this, o, response);
12833             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12834             return;
12835         }
12836         var result;
12837         try {
12838             result = o.reader.read(response);
12839         }catch(e){
12840             this.fireEvent("loadexception", this, o, response, e);
12841             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12842             return;
12843         }
12844         
12845         this.fireEvent("load", this, o, o.request.arg);
12846         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12847     },
12848
12849     // private
12850     update : function(dataSet){
12851
12852     },
12853
12854     // private
12855     updateResponse : function(dataSet){
12856
12857     }
12858 });/*
12859  * Based on:
12860  * Ext JS Library 1.1.1
12861  * Copyright(c) 2006-2007, Ext JS, LLC.
12862  *
12863  * Originally Released Under LGPL - original licence link has changed is not relivant.
12864  *
12865  * Fork - LGPL
12866  * <script type="text/javascript">
12867  */
12868
12869 /**
12870  * @class Roo.data.ScriptTagProxy
12871  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12872  * other than the originating domain of the running page.<br><br>
12873  * <p>
12874  * <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
12875  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12876  * <p>
12877  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12878  * source code that is used as the source inside a &lt;script> tag.<br><br>
12879  * <p>
12880  * In order for the browser to process the returned data, the server must wrap the data object
12881  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12882  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12883  * depending on whether the callback name was passed:
12884  * <p>
12885  * <pre><code>
12886 boolean scriptTag = false;
12887 String cb = request.getParameter("callback");
12888 if (cb != null) {
12889     scriptTag = true;
12890     response.setContentType("text/javascript");
12891 } else {
12892     response.setContentType("application/x-json");
12893 }
12894 Writer out = response.getWriter();
12895 if (scriptTag) {
12896     out.write(cb + "(");
12897 }
12898 out.print(dataBlock.toJsonString());
12899 if (scriptTag) {
12900     out.write(");");
12901 }
12902 </pre></code>
12903  *
12904  * @constructor
12905  * @param {Object} config A configuration object.
12906  */
12907 Roo.data.ScriptTagProxy = function(config){
12908     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12909     Roo.apply(this, config);
12910     this.head = document.getElementsByTagName("head")[0];
12911 };
12912
12913 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12914
12915 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12916     /**
12917      * @cfg {String} url The URL from which to request the data object.
12918      */
12919     /**
12920      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12921      */
12922     timeout : 30000,
12923     /**
12924      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12925      * the server the name of the callback function set up by the load call to process the returned data object.
12926      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12927      * javascript output which calls this named function passing the data object as its only parameter.
12928      */
12929     callbackParam : "callback",
12930     /**
12931      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12932      * name to the request.
12933      */
12934     nocache : true,
12935
12936     /**
12937      * Load data from the configured URL, read the data object into
12938      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12939      * process that block using the passed callback.
12940      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12941      * for the request to the remote server.
12942      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12943      * object into a block of Roo.data.Records.
12944      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12945      * The function must be passed <ul>
12946      * <li>The Record block object</li>
12947      * <li>The "arg" argument from the load function</li>
12948      * <li>A boolean success indicator</li>
12949      * </ul>
12950      * @param {Object} scope The scope in which to call the callback
12951      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12952      */
12953     load : function(params, reader, callback, scope, arg){
12954         if(this.fireEvent("beforeload", this, params) !== false){
12955
12956             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12957
12958             var url = this.url;
12959             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12960             if(this.nocache){
12961                 url += "&_dc=" + (new Date().getTime());
12962             }
12963             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12964             var trans = {
12965                 id : transId,
12966                 cb : "stcCallback"+transId,
12967                 scriptId : "stcScript"+transId,
12968                 params : params,
12969                 arg : arg,
12970                 url : url,
12971                 callback : callback,
12972                 scope : scope,
12973                 reader : reader
12974             };
12975             var conn = this;
12976
12977             window[trans.cb] = function(o){
12978                 conn.handleResponse(o, trans);
12979             };
12980
12981             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12982
12983             if(this.autoAbort !== false){
12984                 this.abort();
12985             }
12986
12987             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12988
12989             var script = document.createElement("script");
12990             script.setAttribute("src", url);
12991             script.setAttribute("type", "text/javascript");
12992             script.setAttribute("id", trans.scriptId);
12993             this.head.appendChild(script);
12994
12995             this.trans = trans;
12996         }else{
12997             callback.call(scope||this, null, arg, false);
12998         }
12999     },
13000
13001     // private
13002     isLoading : function(){
13003         return this.trans ? true : false;
13004     },
13005
13006     /**
13007      * Abort the current server request.
13008      */
13009     abort : function(){
13010         if(this.isLoading()){
13011             this.destroyTrans(this.trans);
13012         }
13013     },
13014
13015     // private
13016     destroyTrans : function(trans, isLoaded){
13017         this.head.removeChild(document.getElementById(trans.scriptId));
13018         clearTimeout(trans.timeoutId);
13019         if(isLoaded){
13020             window[trans.cb] = undefined;
13021             try{
13022                 delete window[trans.cb];
13023             }catch(e){}
13024         }else{
13025             // if hasn't been loaded, wait for load to remove it to prevent script error
13026             window[trans.cb] = function(){
13027                 window[trans.cb] = undefined;
13028                 try{
13029                     delete window[trans.cb];
13030                 }catch(e){}
13031             };
13032         }
13033     },
13034
13035     // private
13036     handleResponse : function(o, trans){
13037         this.trans = false;
13038         this.destroyTrans(trans, true);
13039         var result;
13040         try {
13041             result = trans.reader.readRecords(o);
13042         }catch(e){
13043             this.fireEvent("loadexception", this, o, trans.arg, e);
13044             trans.callback.call(trans.scope||window, null, trans.arg, false);
13045             return;
13046         }
13047         this.fireEvent("load", this, o, trans.arg);
13048         trans.callback.call(trans.scope||window, result, trans.arg, true);
13049     },
13050
13051     // private
13052     handleFailure : function(trans){
13053         this.trans = false;
13054         this.destroyTrans(trans, false);
13055         this.fireEvent("loadexception", this, null, trans.arg);
13056         trans.callback.call(trans.scope||window, null, trans.arg, false);
13057     }
13058 });/*
13059  * Based on:
13060  * Ext JS Library 1.1.1
13061  * Copyright(c) 2006-2007, Ext JS, LLC.
13062  *
13063  * Originally Released Under LGPL - original licence link has changed is not relivant.
13064  *
13065  * Fork - LGPL
13066  * <script type="text/javascript">
13067  */
13068
13069 /**
13070  * @class Roo.data.JsonReader
13071  * @extends Roo.data.DataReader
13072  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
13073  * based on mappings in a provided Roo.data.Record constructor.
13074  * 
13075  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
13076  * in the reply previously. 
13077  * 
13078  * <p>
13079  * Example code:
13080  * <pre><code>
13081 var RecordDef = Roo.data.Record.create([
13082     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
13083     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
13084 ]);
13085 var myReader = new Roo.data.JsonReader({
13086     totalProperty: "results",    // The property which contains the total dataset size (optional)
13087     root: "rows",                // The property which contains an Array of row objects
13088     id: "id"                     // The property within each row object that provides an ID for the record (optional)
13089 }, RecordDef);
13090 </code></pre>
13091  * <p>
13092  * This would consume a JSON file like this:
13093  * <pre><code>
13094 { 'results': 2, 'rows': [
13095     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
13096     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
13097 }
13098 </code></pre>
13099  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
13100  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
13101  * paged from the remote server.
13102  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
13103  * @cfg {String} root name of the property which contains the Array of row objects.
13104  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13105  * @cfg {Array} fields Array of field definition objects
13106  * @constructor
13107  * Create a new JsonReader
13108  * @param {Object} meta Metadata configuration options
13109  * @param {Object} recordType Either an Array of field definition objects,
13110  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
13111  */
13112 Roo.data.JsonReader = function(meta, recordType){
13113     
13114     meta = meta || {};
13115     // set some defaults:
13116     Roo.applyIf(meta, {
13117         totalProperty: 'total',
13118         successProperty : 'success',
13119         root : 'data',
13120         id : 'id'
13121     });
13122     
13123     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13124 };
13125 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
13126     
13127     readerType : 'Json',
13128     
13129     /**
13130      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
13131      * Used by Store query builder to append _requestMeta to params.
13132      * 
13133      */
13134     metaFromRemote : false,
13135     /**
13136      * This method is only used by a DataProxy which has retrieved data from a remote server.
13137      * @param {Object} response The XHR object which contains the JSON data in its responseText.
13138      * @return {Object} data A data block which is used by an Roo.data.Store object as
13139      * a cache of Roo.data.Records.
13140      */
13141     read : function(response){
13142         var json = response.responseText;
13143        
13144         var o = /* eval:var:o */ eval("("+json+")");
13145         if(!o) {
13146             throw {message: "JsonReader.read: Json object not found"};
13147         }
13148         
13149         if(o.metaData){
13150             
13151             delete this.ef;
13152             this.metaFromRemote = true;
13153             this.meta = o.metaData;
13154             this.recordType = Roo.data.Record.create(o.metaData.fields);
13155             this.onMetaChange(this.meta, this.recordType, o);
13156         }
13157         return this.readRecords(o);
13158     },
13159
13160     // private function a store will implement
13161     onMetaChange : function(meta, recordType, o){
13162
13163     },
13164
13165     /**
13166          * @ignore
13167          */
13168     simpleAccess: function(obj, subsc) {
13169         return obj[subsc];
13170     },
13171
13172         /**
13173          * @ignore
13174          */
13175     getJsonAccessor: function(){
13176         var re = /[\[\.]/;
13177         return function(expr) {
13178             try {
13179                 return(re.test(expr))
13180                     ? new Function("obj", "return obj." + expr)
13181                     : function(obj){
13182                         return obj[expr];
13183                     };
13184             } catch(e){}
13185             return Roo.emptyFn;
13186         };
13187     }(),
13188
13189     /**
13190      * Create a data block containing Roo.data.Records from an XML document.
13191      * @param {Object} o An object which contains an Array of row objects in the property specified
13192      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
13193      * which contains the total size of the dataset.
13194      * @return {Object} data A data block which is used by an Roo.data.Store object as
13195      * a cache of Roo.data.Records.
13196      */
13197     readRecords : function(o){
13198         /**
13199          * After any data loads, the raw JSON data is available for further custom processing.
13200          * @type Object
13201          */
13202         this.o = o;
13203         var s = this.meta, Record = this.recordType,
13204             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
13205
13206 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
13207         if (!this.ef) {
13208             if(s.totalProperty) {
13209                     this.getTotal = this.getJsonAccessor(s.totalProperty);
13210                 }
13211                 if(s.successProperty) {
13212                     this.getSuccess = this.getJsonAccessor(s.successProperty);
13213                 }
13214                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
13215                 if (s.id) {
13216                         var g = this.getJsonAccessor(s.id);
13217                         this.getId = function(rec) {
13218                                 var r = g(rec);  
13219                                 return (r === undefined || r === "") ? null : r;
13220                         };
13221                 } else {
13222                         this.getId = function(){return null;};
13223                 }
13224             this.ef = [];
13225             for(var jj = 0; jj < fl; jj++){
13226                 f = fi[jj];
13227                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
13228                 this.ef[jj] = this.getJsonAccessor(map);
13229             }
13230         }
13231
13232         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
13233         if(s.totalProperty){
13234             var vt = parseInt(this.getTotal(o), 10);
13235             if(!isNaN(vt)){
13236                 totalRecords = vt;
13237             }
13238         }
13239         if(s.successProperty){
13240             var vs = this.getSuccess(o);
13241             if(vs === false || vs === 'false'){
13242                 success = false;
13243             }
13244         }
13245         var records = [];
13246         for(var i = 0; i < c; i++){
13247                 var n = root[i];
13248             var values = {};
13249             var id = this.getId(n);
13250             for(var j = 0; j < fl; j++){
13251                 f = fi[j];
13252             var v = this.ef[j](n);
13253             if (!f.convert) {
13254                 Roo.log('missing convert for ' + f.name);
13255                 Roo.log(f);
13256                 continue;
13257             }
13258             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
13259             }
13260             var record = new Record(values, id);
13261             record.json = n;
13262             records[i] = record;
13263         }
13264         return {
13265             raw : o,
13266             success : success,
13267             records : records,
13268             totalRecords : totalRecords
13269         };
13270     },
13271     // used when loading children.. @see loadDataFromChildren
13272     toLoadData: function(rec)
13273     {
13274         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13275         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13276         return { data : data, total : data.length };
13277         
13278     }
13279 });/*
13280  * Based on:
13281  * Ext JS Library 1.1.1
13282  * Copyright(c) 2006-2007, Ext JS, LLC.
13283  *
13284  * Originally Released Under LGPL - original licence link has changed is not relivant.
13285  *
13286  * Fork - LGPL
13287  * <script type="text/javascript">
13288  */
13289
13290 /**
13291  * @class Roo.data.ArrayReader
13292  * @extends Roo.data.DataReader
13293  * Data reader class to create an Array of Roo.data.Record objects from an Array.
13294  * Each element of that Array represents a row of data fields. The
13295  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13296  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13297  * <p>
13298  * Example code:.
13299  * <pre><code>
13300 var RecordDef = Roo.data.Record.create([
13301     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
13302     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
13303 ]);
13304 var myReader = new Roo.data.ArrayReader({
13305     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
13306 }, RecordDef);
13307 </code></pre>
13308  * <p>
13309  * This would consume an Array like this:
13310  * <pre><code>
13311 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13312   </code></pre>
13313  
13314  * @constructor
13315  * Create a new JsonReader
13316  * @param {Object} meta Metadata configuration options.
13317  * @param {Object|Array} recordType Either an Array of field definition objects
13318  * 
13319  * @cfg {Array} fields Array of field definition objects
13320  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13321  * as specified to {@link Roo.data.Record#create},
13322  * or an {@link Roo.data.Record} object
13323  *
13324  * 
13325  * created using {@link Roo.data.Record#create}.
13326  */
13327 Roo.data.ArrayReader = function(meta, recordType)
13328 {    
13329     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13330 };
13331
13332 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13333     
13334       /**
13335      * Create a data block containing Roo.data.Records from an XML document.
13336      * @param {Object} o An Array of row objects which represents the dataset.
13337      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13338      * a cache of Roo.data.Records.
13339      */
13340     readRecords : function(o)
13341     {
13342         var sid = this.meta ? this.meta.id : null;
13343         var recordType = this.recordType, fields = recordType.prototype.fields;
13344         var records = [];
13345         var root = o;
13346         for(var i = 0; i < root.length; i++){
13347                 var n = root[i];
13348             var values = {};
13349             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13350             for(var j = 0, jlen = fields.length; j < jlen; j++){
13351                 var f = fields.items[j];
13352                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13353                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13354                 v = f.convert(v);
13355                 values[f.name] = v;
13356             }
13357             var record = new recordType(values, id);
13358             record.json = n;
13359             records[records.length] = record;
13360         }
13361         return {
13362             records : records,
13363             totalRecords : records.length
13364         };
13365     },
13366     // used when loading children.. @see loadDataFromChildren
13367     toLoadData: function(rec)
13368     {
13369         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13370         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13371         
13372     }
13373     
13374     
13375 });/*
13376  * - LGPL
13377  * * 
13378  */
13379
13380 /**
13381  * @class Roo.bootstrap.ComboBox
13382  * @extends Roo.bootstrap.TriggerField
13383  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13384  * @cfg {Boolean} append (true|false) default false
13385  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13386  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13387  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13388  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13389  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13390  * @cfg {Boolean} animate default true
13391  * @cfg {Boolean} emptyResultText only for touch device
13392  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13393  * @cfg {String} emptyTitle default ''
13394  * @constructor
13395  * Create a new ComboBox.
13396  * @param {Object} config Configuration options
13397  */
13398 Roo.bootstrap.ComboBox = function(config){
13399     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13400     this.addEvents({
13401         /**
13402          * @event expand
13403          * Fires when the dropdown list is expanded
13404         * @param {Roo.bootstrap.ComboBox} combo This combo box
13405         */
13406         'expand' : true,
13407         /**
13408          * @event collapse
13409          * Fires when the dropdown list is collapsed
13410         * @param {Roo.bootstrap.ComboBox} combo This combo box
13411         */
13412         'collapse' : true,
13413         /**
13414          * @event beforeselect
13415          * Fires before a list item is selected. Return false to cancel the selection.
13416         * @param {Roo.bootstrap.ComboBox} combo This combo box
13417         * @param {Roo.data.Record} record The data record returned from the underlying store
13418         * @param {Number} index The index of the selected item in the dropdown list
13419         */
13420         'beforeselect' : true,
13421         /**
13422          * @event select
13423          * Fires when a list item is selected
13424         * @param {Roo.bootstrap.ComboBox} combo This combo box
13425         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13426         * @param {Number} index The index of the selected item in the dropdown list
13427         */
13428         'select' : true,
13429         /**
13430          * @event beforequery
13431          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13432          * The event object passed has these properties:
13433         * @param {Roo.bootstrap.ComboBox} combo This combo box
13434         * @param {String} query The query
13435         * @param {Boolean} forceAll true to force "all" query
13436         * @param {Boolean} cancel true to cancel the query
13437         * @param {Object} e The query event object
13438         */
13439         'beforequery': true,
13440          /**
13441          * @event add
13442          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13443         * @param {Roo.bootstrap.ComboBox} combo This combo box
13444         */
13445         'add' : true,
13446         /**
13447          * @event edit
13448          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13449         * @param {Roo.bootstrap.ComboBox} combo This combo box
13450         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13451         */
13452         'edit' : true,
13453         /**
13454          * @event remove
13455          * Fires when the remove value from the combobox array
13456         * @param {Roo.bootstrap.ComboBox} combo This combo box
13457         */
13458         'remove' : true,
13459         /**
13460          * @event afterremove
13461          * Fires when the remove value from the combobox array
13462         * @param {Roo.bootstrap.ComboBox} combo This combo box
13463         */
13464         'afterremove' : true,
13465         /**
13466          * @event specialfilter
13467          * Fires when specialfilter
13468             * @param {Roo.bootstrap.ComboBox} combo This combo box
13469             */
13470         'specialfilter' : true,
13471         /**
13472          * @event tick
13473          * Fires when tick the element
13474             * @param {Roo.bootstrap.ComboBox} combo This combo box
13475             */
13476         'tick' : true,
13477         /**
13478          * @event touchviewdisplay
13479          * Fires when touch view require special display (default is using displayField)
13480             * @param {Roo.bootstrap.ComboBox} combo This combo box
13481             * @param {Object} cfg set html .
13482             */
13483         'touchviewdisplay' : true
13484         
13485     });
13486     
13487     this.item = [];
13488     this.tickItems = [];
13489     
13490     this.selectedIndex = -1;
13491     if(this.mode == 'local'){
13492         if(config.queryDelay === undefined){
13493             this.queryDelay = 10;
13494         }
13495         if(config.minChars === undefined){
13496             this.minChars = 0;
13497         }
13498     }
13499 };
13500
13501 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13502      
13503     /**
13504      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13505      * rendering into an Roo.Editor, defaults to false)
13506      */
13507     /**
13508      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13509      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13510      */
13511     /**
13512      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13513      */
13514     /**
13515      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13516      * the dropdown list (defaults to undefined, with no header element)
13517      */
13518
13519      /**
13520      * @cfg {String/Roo.Template} tpl The template to use to render the output
13521      */
13522      
13523      /**
13524      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13525      */
13526     listWidth: undefined,
13527     /**
13528      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13529      * mode = 'remote' or 'text' if mode = 'local')
13530      */
13531     displayField: undefined,
13532     
13533     /**
13534      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13535      * mode = 'remote' or 'value' if mode = 'local'). 
13536      * Note: use of a valueField requires the user make a selection
13537      * in order for a value to be mapped.
13538      */
13539     valueField: undefined,
13540     /**
13541      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13542      */
13543     modalTitle : '',
13544     
13545     /**
13546      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13547      * field's data value (defaults to the underlying DOM element's name)
13548      */
13549     hiddenName: undefined,
13550     /**
13551      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13552      */
13553     listClass: '',
13554     /**
13555      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13556      */
13557     selectedClass: 'active',
13558     
13559     /**
13560      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13561      */
13562     shadow:'sides',
13563     /**
13564      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13565      * anchor positions (defaults to 'tl-bl')
13566      */
13567     listAlign: 'tl-bl?',
13568     /**
13569      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13570      */
13571     maxHeight: 300,
13572     /**
13573      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13574      * query specified by the allQuery config option (defaults to 'query')
13575      */
13576     triggerAction: 'query',
13577     /**
13578      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13579      * (defaults to 4, does not apply if editable = false)
13580      */
13581     minChars : 4,
13582     /**
13583      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13584      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13585      */
13586     typeAhead: false,
13587     /**
13588      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13589      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13590      */
13591     queryDelay: 500,
13592     /**
13593      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13594      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13595      */
13596     pageSize: 0,
13597     /**
13598      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13599      * when editable = true (defaults to false)
13600      */
13601     selectOnFocus:false,
13602     /**
13603      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13604      */
13605     queryParam: 'query',
13606     /**
13607      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13608      * when mode = 'remote' (defaults to 'Loading...')
13609      */
13610     loadingText: 'Loading...',
13611     /**
13612      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13613      */
13614     resizable: false,
13615     /**
13616      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13617      */
13618     handleHeight : 8,
13619     /**
13620      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13621      * traditional select (defaults to true)
13622      */
13623     editable: true,
13624     /**
13625      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13626      */
13627     allQuery: '',
13628     /**
13629      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13630      */
13631     mode: 'remote',
13632     /**
13633      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13634      * listWidth has a higher value)
13635      */
13636     minListWidth : 70,
13637     /**
13638      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13639      * allow the user to set arbitrary text into the field (defaults to false)
13640      */
13641     forceSelection:false,
13642     /**
13643      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13644      * if typeAhead = true (defaults to 250)
13645      */
13646     typeAheadDelay : 250,
13647     /**
13648      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13649      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13650      */
13651     valueNotFoundText : undefined,
13652     /**
13653      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13654      */
13655     blockFocus : false,
13656     
13657     /**
13658      * @cfg {Boolean} disableClear Disable showing of clear button.
13659      */
13660     disableClear : false,
13661     /**
13662      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13663      */
13664     alwaysQuery : false,
13665     
13666     /**
13667      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13668      */
13669     multiple : false,
13670     
13671     /**
13672      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13673      */
13674     invalidClass : "has-warning",
13675     
13676     /**
13677      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13678      */
13679     validClass : "has-success",
13680     
13681     /**
13682      * @cfg {Boolean} specialFilter (true|false) special filter default false
13683      */
13684     specialFilter : false,
13685     
13686     /**
13687      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13688      */
13689     mobileTouchView : true,
13690     
13691     /**
13692      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13693      */
13694     useNativeIOS : false,
13695     
13696     /**
13697      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13698      */
13699     mobile_restrict_height : false,
13700     
13701     ios_options : false,
13702     
13703     //private
13704     addicon : false,
13705     editicon: false,
13706     
13707     page: 0,
13708     hasQuery: false,
13709     append: false,
13710     loadNext: false,
13711     autoFocus : true,
13712     tickable : false,
13713     btnPosition : 'right',
13714     triggerList : true,
13715     showToggleBtn : true,
13716     animate : true,
13717     emptyResultText: 'Empty',
13718     triggerText : 'Select',
13719     emptyTitle : '',
13720     
13721     // element that contains real text value.. (when hidden is used..)
13722     
13723     getAutoCreate : function()
13724     {   
13725         var cfg = false;
13726         //render
13727         /*
13728          * Render classic select for iso
13729          */
13730         
13731         if(Roo.isIOS && this.useNativeIOS){
13732             cfg = this.getAutoCreateNativeIOS();
13733             return cfg;
13734         }
13735         
13736         /*
13737          * Touch Devices
13738          */
13739         
13740         if(Roo.isTouch && this.mobileTouchView){
13741             cfg = this.getAutoCreateTouchView();
13742             return cfg;;
13743         }
13744         
13745         /*
13746          *  Normal ComboBox
13747          */
13748         if(!this.tickable){
13749             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13750             return cfg;
13751         }
13752         
13753         /*
13754          *  ComboBox with tickable selections
13755          */
13756              
13757         var align = this.labelAlign || this.parentLabelAlign();
13758         
13759         cfg = {
13760             cls : 'form-group roo-combobox-tickable' //input-group
13761         };
13762         
13763         var btn_text_select = '';
13764         var btn_text_done = '';
13765         var btn_text_cancel = '';
13766         
13767         if (this.btn_text_show) {
13768             btn_text_select = 'Select';
13769             btn_text_done = 'Done';
13770             btn_text_cancel = 'Cancel'; 
13771         }
13772         
13773         var buttons = {
13774             tag : 'div',
13775             cls : 'tickable-buttons',
13776             cn : [
13777                 {
13778                     tag : 'button',
13779                     type : 'button',
13780                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13781                     //html : this.triggerText
13782                     html: btn_text_select
13783                 },
13784                 {
13785                     tag : 'button',
13786                     type : 'button',
13787                     name : 'ok',
13788                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13789                     //html : 'Done'
13790                     html: btn_text_done
13791                 },
13792                 {
13793                     tag : 'button',
13794                     type : 'button',
13795                     name : 'cancel',
13796                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13797                     //html : 'Cancel'
13798                     html: btn_text_cancel
13799                 }
13800             ]
13801         };
13802         
13803         if(this.editable){
13804             buttons.cn.unshift({
13805                 tag: 'input',
13806                 cls: 'roo-select2-search-field-input'
13807             });
13808         }
13809         
13810         var _this = this;
13811         
13812         Roo.each(buttons.cn, function(c){
13813             if (_this.size) {
13814                 c.cls += ' btn-' + _this.size;
13815             }
13816
13817             if (_this.disabled) {
13818                 c.disabled = true;
13819             }
13820         });
13821         
13822         var box = {
13823             tag: 'div',
13824             style : 'display: contents',
13825             cn: [
13826                 {
13827                     tag: 'input',
13828                     type : 'hidden',
13829                     cls: 'form-hidden-field'
13830                 },
13831                 {
13832                     tag: 'ul',
13833                     cls: 'roo-select2-choices',
13834                     cn:[
13835                         {
13836                             tag: 'li',
13837                             cls: 'roo-select2-search-field',
13838                             cn: [
13839                                 buttons
13840                             ]
13841                         }
13842                     ]
13843                 }
13844             ]
13845         };
13846         
13847         var combobox = {
13848             cls: 'roo-select2-container input-group roo-select2-container-multi',
13849             cn: [
13850                 
13851                 box
13852 //                {
13853 //                    tag: 'ul',
13854 //                    cls: 'typeahead typeahead-long dropdown-menu',
13855 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13856 //                }
13857             ]
13858         };
13859         
13860         if(this.hasFeedback && !this.allowBlank){
13861             
13862             var feedback = {
13863                 tag: 'span',
13864                 cls: 'glyphicon form-control-feedback'
13865             };
13866
13867             combobox.cn.push(feedback);
13868         }
13869         
13870         var indicator = {
13871             tag : 'i',
13872             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13873             tooltip : 'This field is required'
13874         };
13875         if (Roo.bootstrap.version == 4) {
13876             indicator = {
13877                 tag : 'i',
13878                 style : 'display:none'
13879             };
13880         }
13881         if (align ==='left' && this.fieldLabel.length) {
13882             
13883             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13884             
13885             cfg.cn = [
13886                 indicator,
13887                 {
13888                     tag: 'label',
13889                     'for' :  id,
13890                     cls : 'control-label col-form-label',
13891                     html : this.fieldLabel
13892
13893                 },
13894                 {
13895                     cls : "", 
13896                     cn: [
13897                         combobox
13898                     ]
13899                 }
13900
13901             ];
13902             
13903             var labelCfg = cfg.cn[1];
13904             var contentCfg = cfg.cn[2];
13905             
13906
13907             if(this.indicatorpos == 'right'){
13908                 
13909                 cfg.cn = [
13910                     {
13911                         tag: 'label',
13912                         'for' :  id,
13913                         cls : 'control-label col-form-label',
13914                         cn : [
13915                             {
13916                                 tag : 'span',
13917                                 html : this.fieldLabel
13918                             },
13919                             indicator
13920                         ]
13921                     },
13922                     {
13923                         cls : "",
13924                         cn: [
13925                             combobox
13926                         ]
13927                     }
13928
13929                 ];
13930                 
13931                 
13932                 
13933                 labelCfg = cfg.cn[0];
13934                 contentCfg = cfg.cn[1];
13935             
13936             }
13937             
13938             if(this.labelWidth > 12){
13939                 labelCfg.style = "width: " + this.labelWidth + 'px';
13940             }
13941             
13942             if(this.labelWidth < 13 && this.labelmd == 0){
13943                 this.labelmd = this.labelWidth;
13944             }
13945             
13946             if(this.labellg > 0){
13947                 labelCfg.cls += ' col-lg-' + this.labellg;
13948                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13949             }
13950             
13951             if(this.labelmd > 0){
13952                 labelCfg.cls += ' col-md-' + this.labelmd;
13953                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13954             }
13955             
13956             if(this.labelsm > 0){
13957                 labelCfg.cls += ' col-sm-' + this.labelsm;
13958                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13959             }
13960             
13961             if(this.labelxs > 0){
13962                 labelCfg.cls += ' col-xs-' + this.labelxs;
13963                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13964             }
13965                 
13966                 
13967         } else if ( this.fieldLabel.length) {
13968 //                Roo.log(" label");
13969                  cfg.cn = [
13970                    indicator,
13971                     {
13972                         tag: 'label',
13973                         //cls : 'input-group-addon',
13974                         html : this.fieldLabel
13975                     },
13976                     combobox
13977                 ];
13978                 
13979                 if(this.indicatorpos == 'right'){
13980                     cfg.cn = [
13981                         {
13982                             tag: 'label',
13983                             //cls : 'input-group-addon',
13984                             html : this.fieldLabel
13985                         },
13986                         indicator,
13987                         combobox
13988                     ];
13989                     
13990                 }
13991
13992         } else {
13993             
13994 //                Roo.log(" no label && no align");
13995                 cfg = combobox
13996                      
13997                 
13998         }
13999          
14000         var settings=this;
14001         ['xs','sm','md','lg'].map(function(size){
14002             if (settings[size]) {
14003                 cfg.cls += ' col-' + size + '-' + settings[size];
14004             }
14005         });
14006         
14007         return cfg;
14008         
14009     },
14010     
14011     _initEventsCalled : false,
14012     
14013     // private
14014     initEvents: function()
14015     {   
14016         if (this._initEventsCalled) { // as we call render... prevent looping...
14017             return;
14018         }
14019         this._initEventsCalled = true;
14020         
14021         if (!this.store) {
14022             throw "can not find store for combo";
14023         }
14024         
14025         this.indicator = this.indicatorEl();
14026         
14027         this.store = Roo.factory(this.store, Roo.data);
14028         this.store.parent = this;
14029         
14030         // if we are building from html. then this element is so complex, that we can not really
14031         // use the rendered HTML.
14032         // so we have to trash and replace the previous code.
14033         if (Roo.XComponent.build_from_html) {
14034             // remove this element....
14035             var e = this.el.dom, k=0;
14036             while (e ) { e = e.previousSibling;  ++k;}
14037
14038             this.el.remove();
14039             
14040             this.el=false;
14041             this.rendered = false;
14042             
14043             this.render(this.parent().getChildContainer(true), k);
14044         }
14045         
14046         if(Roo.isIOS && this.useNativeIOS){
14047             this.initIOSView();
14048             return;
14049         }
14050         
14051         /*
14052          * Touch Devices
14053          */
14054         
14055         if(Roo.isTouch && this.mobileTouchView){
14056             this.initTouchView();
14057             return;
14058         }
14059         
14060         if(this.tickable){
14061             this.initTickableEvents();
14062             return;
14063         }
14064         
14065         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
14066         
14067         if(this.hiddenName){
14068             
14069             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14070             
14071             this.hiddenField.dom.value =
14072                 this.hiddenValue !== undefined ? this.hiddenValue :
14073                 this.value !== undefined ? this.value : '';
14074
14075             // prevent input submission
14076             this.el.dom.removeAttribute('name');
14077             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14078              
14079              
14080         }
14081         //if(Roo.isGecko){
14082         //    this.el.dom.setAttribute('autocomplete', 'off');
14083         //}
14084         
14085         var cls = 'x-combo-list';
14086         
14087         //this.list = new Roo.Layer({
14088         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
14089         //});
14090         
14091         var _this = this;
14092         
14093         (function(){
14094             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14095             _this.list.setWidth(lw);
14096         }).defer(100);
14097         
14098         this.list.on('mouseover', this.onViewOver, this);
14099         this.list.on('mousemove', this.onViewMove, this);
14100         this.list.on('scroll', this.onViewScroll, this);
14101         
14102         /*
14103         this.list.swallowEvent('mousewheel');
14104         this.assetHeight = 0;
14105
14106         if(this.title){
14107             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
14108             this.assetHeight += this.header.getHeight();
14109         }
14110
14111         this.innerList = this.list.createChild({cls:cls+'-inner'});
14112         this.innerList.on('mouseover', this.onViewOver, this);
14113         this.innerList.on('mousemove', this.onViewMove, this);
14114         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14115         
14116         if(this.allowBlank && !this.pageSize && !this.disableClear){
14117             this.footer = this.list.createChild({cls:cls+'-ft'});
14118             this.pageTb = new Roo.Toolbar(this.footer);
14119            
14120         }
14121         if(this.pageSize){
14122             this.footer = this.list.createChild({cls:cls+'-ft'});
14123             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
14124                     {pageSize: this.pageSize});
14125             
14126         }
14127         
14128         if (this.pageTb && this.allowBlank && !this.disableClear) {
14129             var _this = this;
14130             this.pageTb.add(new Roo.Toolbar.Fill(), {
14131                 cls: 'x-btn-icon x-btn-clear',
14132                 text: '&#160;',
14133                 handler: function()
14134                 {
14135                     _this.collapse();
14136                     _this.clearValue();
14137                     _this.onSelect(false, -1);
14138                 }
14139             });
14140         }
14141         if (this.footer) {
14142             this.assetHeight += this.footer.getHeight();
14143         }
14144         */
14145             
14146         if(!this.tpl){
14147             this.tpl = Roo.bootstrap.version == 4 ?
14148                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
14149                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
14150         }
14151
14152         this.view = new Roo.View(this.list, this.tpl, {
14153             singleSelect:true, store: this.store, selectedClass: this.selectedClass
14154         });
14155         //this.view.wrapEl.setDisplayed(false);
14156         this.view.on('click', this.onViewClick, this);
14157         
14158         
14159         this.store.on('beforeload', this.onBeforeLoad, this);
14160         this.store.on('load', this.onLoad, this);
14161         this.store.on('loadexception', this.onLoadException, this);
14162         /*
14163         if(this.resizable){
14164             this.resizer = new Roo.Resizable(this.list,  {
14165                pinned:true, handles:'se'
14166             });
14167             this.resizer.on('resize', function(r, w, h){
14168                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
14169                 this.listWidth = w;
14170                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
14171                 this.restrictHeight();
14172             }, this);
14173             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
14174         }
14175         */
14176         if(!this.editable){
14177             this.editable = true;
14178             this.setEditable(false);
14179         }
14180         
14181         /*
14182         
14183         if (typeof(this.events.add.listeners) != 'undefined') {
14184             
14185             this.addicon = this.wrap.createChild(
14186                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
14187        
14188             this.addicon.on('click', function(e) {
14189                 this.fireEvent('add', this);
14190             }, this);
14191         }
14192         if (typeof(this.events.edit.listeners) != 'undefined') {
14193             
14194             this.editicon = this.wrap.createChild(
14195                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
14196             if (this.addicon) {
14197                 this.editicon.setStyle('margin-left', '40px');
14198             }
14199             this.editicon.on('click', function(e) {
14200                 
14201                 // we fire even  if inothing is selected..
14202                 this.fireEvent('edit', this, this.lastData );
14203                 
14204             }, this);
14205         }
14206         */
14207         
14208         this.keyNav = new Roo.KeyNav(this.inputEl(), {
14209             "up" : function(e){
14210                 this.inKeyMode = true;
14211                 this.selectPrev();
14212             },
14213
14214             "down" : function(e){
14215                 if(!this.isExpanded()){
14216                     this.onTriggerClick();
14217                 }else{
14218                     this.inKeyMode = true;
14219                     this.selectNext();
14220                 }
14221             },
14222
14223             "enter" : function(e){
14224 //                this.onViewClick();
14225                 //return true;
14226                 this.collapse();
14227                 
14228                 if(this.fireEvent("specialkey", this, e)){
14229                     this.onViewClick(false);
14230                 }
14231                 
14232                 return true;
14233             },
14234
14235             "esc" : function(e){
14236                 this.collapse();
14237             },
14238
14239             "tab" : function(e){
14240                 this.collapse();
14241                 
14242                 if(this.fireEvent("specialkey", this, e)){
14243                     this.onViewClick(false);
14244                 }
14245                 
14246                 return true;
14247             },
14248
14249             scope : this,
14250
14251             doRelay : function(foo, bar, hname){
14252                 if(hname == 'down' || this.scope.isExpanded()){
14253                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14254                 }
14255                 return true;
14256             },
14257
14258             forceKeyDown: true
14259         });
14260         
14261         
14262         this.queryDelay = Math.max(this.queryDelay || 10,
14263                 this.mode == 'local' ? 10 : 250);
14264         
14265         
14266         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14267         
14268         if(this.typeAhead){
14269             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14270         }
14271         if(this.editable !== false){
14272             this.inputEl().on("keyup", this.onKeyUp, this);
14273         }
14274         if(this.forceSelection){
14275             this.inputEl().on('blur', this.doForce, this);
14276         }
14277         
14278         if(this.multiple){
14279             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14280             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14281         }
14282     },
14283     
14284     initTickableEvents: function()
14285     {   
14286         this.createList();
14287         
14288         if(this.hiddenName){
14289             
14290             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14291             
14292             this.hiddenField.dom.value =
14293                 this.hiddenValue !== undefined ? this.hiddenValue :
14294                 this.value !== undefined ? this.value : '';
14295
14296             // prevent input submission
14297             this.el.dom.removeAttribute('name');
14298             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14299              
14300              
14301         }
14302         
14303 //        this.list = this.el.select('ul.dropdown-menu',true).first();
14304         
14305         this.choices = this.el.select('ul.roo-select2-choices', true).first();
14306         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14307         if(this.triggerList){
14308             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14309         }
14310          
14311         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14312         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14313         
14314         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14315         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14316         
14317         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14318         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14319         
14320         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14321         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14322         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14323         
14324         this.okBtn.hide();
14325         this.cancelBtn.hide();
14326         
14327         var _this = this;
14328         
14329         (function(){
14330             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14331             _this.list.setWidth(lw);
14332         }).defer(100);
14333         
14334         this.list.on('mouseover', this.onViewOver, this);
14335         this.list.on('mousemove', this.onViewMove, this);
14336         
14337         this.list.on('scroll', this.onViewScroll, this);
14338         
14339         if(!this.tpl){
14340             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
14341                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14342         }
14343
14344         this.view = new Roo.View(this.list, this.tpl, {
14345             singleSelect:true,
14346             tickable:true,
14347             parent:this,
14348             store: this.store,
14349             selectedClass: this.selectedClass
14350         });
14351         
14352         //this.view.wrapEl.setDisplayed(false);
14353         this.view.on('click', this.onViewClick, this);
14354         
14355         
14356         
14357         this.store.on('beforeload', this.onBeforeLoad, this);
14358         this.store.on('load', this.onLoad, this);
14359         this.store.on('loadexception', this.onLoadException, this);
14360         
14361         if(this.editable){
14362             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14363                 "up" : function(e){
14364                     this.inKeyMode = true;
14365                     this.selectPrev();
14366                 },
14367
14368                 "down" : function(e){
14369                     this.inKeyMode = true;
14370                     this.selectNext();
14371                 },
14372
14373                 "enter" : function(e){
14374                     if(this.fireEvent("specialkey", this, e)){
14375                         this.onViewClick(false);
14376                     }
14377                     
14378                     return true;
14379                 },
14380
14381                 "esc" : function(e){
14382                     this.onTickableFooterButtonClick(e, false, false);
14383                 },
14384
14385                 "tab" : function(e){
14386                     this.fireEvent("specialkey", this, e);
14387                     
14388                     this.onTickableFooterButtonClick(e, false, false);
14389                     
14390                     return true;
14391                 },
14392
14393                 scope : this,
14394
14395                 doRelay : function(e, fn, key){
14396                     if(this.scope.isExpanded()){
14397                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14398                     }
14399                     return true;
14400                 },
14401
14402                 forceKeyDown: true
14403             });
14404         }
14405         
14406         this.queryDelay = Math.max(this.queryDelay || 10,
14407                 this.mode == 'local' ? 10 : 250);
14408         
14409         
14410         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14411         
14412         if(this.typeAhead){
14413             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14414         }
14415         
14416         if(this.editable !== false){
14417             this.tickableInputEl().on("keyup", this.onKeyUp, this);
14418         }
14419         
14420         this.indicator = this.indicatorEl();
14421         
14422         if(this.indicator){
14423             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14424             this.indicator.hide();
14425         }
14426         
14427     },
14428
14429     onDestroy : function(){
14430         if(this.view){
14431             this.view.setStore(null);
14432             this.view.el.removeAllListeners();
14433             this.view.el.remove();
14434             this.view.purgeListeners();
14435         }
14436         if(this.list){
14437             this.list.dom.innerHTML  = '';
14438         }
14439         
14440         if(this.store){
14441             this.store.un('beforeload', this.onBeforeLoad, this);
14442             this.store.un('load', this.onLoad, this);
14443             this.store.un('loadexception', this.onLoadException, this);
14444         }
14445         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14446     },
14447
14448     // private
14449     fireKey : function(e){
14450         if(e.isNavKeyPress() && !this.list.isVisible()){
14451             this.fireEvent("specialkey", this, e);
14452         }
14453     },
14454
14455     // private
14456     onResize: function(w, h){
14457 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14458 //        
14459 //        if(typeof w != 'number'){
14460 //            // we do not handle it!?!?
14461 //            return;
14462 //        }
14463 //        var tw = this.trigger.getWidth();
14464 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14465 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14466 //        var x = w - tw;
14467 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14468 //            
14469 //        //this.trigger.setStyle('left', x+'px');
14470 //        
14471 //        if(this.list && this.listWidth === undefined){
14472 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14473 //            this.list.setWidth(lw);
14474 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14475 //        }
14476         
14477     
14478         
14479     },
14480
14481     /**
14482      * Allow or prevent the user from directly editing the field text.  If false is passed,
14483      * the user will only be able to select from the items defined in the dropdown list.  This method
14484      * is the runtime equivalent of setting the 'editable' config option at config time.
14485      * @param {Boolean} value True to allow the user to directly edit the field text
14486      */
14487     setEditable : function(value){
14488         if(value == this.editable){
14489             return;
14490         }
14491         this.editable = value;
14492         if(!value){
14493             this.inputEl().dom.setAttribute('readOnly', true);
14494             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14495             this.inputEl().addClass('x-combo-noedit');
14496         }else{
14497             this.inputEl().dom.setAttribute('readOnly', false);
14498             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14499             this.inputEl().removeClass('x-combo-noedit');
14500         }
14501     },
14502
14503     // private
14504     
14505     onBeforeLoad : function(combo,opts){
14506         if(!this.hasFocus){
14507             return;
14508         }
14509          if (!opts.add) {
14510             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14511          }
14512         this.restrictHeight();
14513         this.selectedIndex = -1;
14514     },
14515
14516     // private
14517     onLoad : function(){
14518         
14519         this.hasQuery = false;
14520         
14521         if(!this.hasFocus){
14522             return;
14523         }
14524         
14525         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14526             this.loading.hide();
14527         }
14528         
14529         if(this.store.getCount() > 0){
14530             
14531             this.expand();
14532             this.restrictHeight();
14533             if(this.lastQuery == this.allQuery){
14534                 if(this.editable && !this.tickable){
14535                     this.inputEl().dom.select();
14536                 }
14537                 
14538                 if(
14539                     !this.selectByValue(this.value, true) &&
14540                     this.autoFocus && 
14541                     (
14542                         !this.store.lastOptions ||
14543                         typeof(this.store.lastOptions.add) == 'undefined' || 
14544                         this.store.lastOptions.add != true
14545                     )
14546                 ){
14547                     this.select(0, true);
14548                 }
14549             }else{
14550                 if(this.autoFocus){
14551                     this.selectNext();
14552                 }
14553                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14554                     this.taTask.delay(this.typeAheadDelay);
14555                 }
14556             }
14557         }else{
14558             this.onEmptyResults();
14559         }
14560         
14561         //this.el.focus();
14562     },
14563     // private
14564     onLoadException : function()
14565     {
14566         this.hasQuery = false;
14567         
14568         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14569             this.loading.hide();
14570         }
14571         
14572         if(this.tickable && this.editable){
14573             return;
14574         }
14575         
14576         this.collapse();
14577         // only causes errors at present
14578         //Roo.log(this.store.reader.jsonData);
14579         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14580             // fixme
14581             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14582         //}
14583         
14584         
14585     },
14586     // private
14587     onTypeAhead : function(){
14588         if(this.store.getCount() > 0){
14589             var r = this.store.getAt(0);
14590             var newValue = r.data[this.displayField];
14591             var len = newValue.length;
14592             var selStart = this.getRawValue().length;
14593             
14594             if(selStart != len){
14595                 this.setRawValue(newValue);
14596                 this.selectText(selStart, newValue.length);
14597             }
14598         }
14599     },
14600
14601     // private
14602     onSelect : function(record, index){
14603         
14604         if(this.fireEvent('beforeselect', this, record, index) !== false){
14605         
14606             this.setFromData(index > -1 ? record.data : false);
14607             
14608             this.collapse();
14609             this.fireEvent('select', this, record, index);
14610         }
14611     },
14612
14613     /**
14614      * Returns the currently selected field value or empty string if no value is set.
14615      * @return {String} value The selected value
14616      */
14617     getValue : function()
14618     {
14619         if(Roo.isIOS && this.useNativeIOS){
14620             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14621         }
14622         
14623         if(this.multiple){
14624             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14625         }
14626         
14627         if(this.valueField){
14628             return typeof this.value != 'undefined' ? this.value : '';
14629         }else{
14630             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14631         }
14632     },
14633     
14634     getRawValue : function()
14635     {
14636         if(Roo.isIOS && this.useNativeIOS){
14637             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14638         }
14639         
14640         var v = this.inputEl().getValue();
14641         
14642         return v;
14643     },
14644
14645     /**
14646      * Clears any text/value currently set in the field
14647      */
14648     clearValue : function(){
14649         
14650         if(this.hiddenField){
14651             this.hiddenField.dom.value = '';
14652         }
14653         this.value = '';
14654         this.setRawValue('');
14655         this.lastSelectionText = '';
14656         this.lastData = false;
14657         
14658         var close = this.closeTriggerEl();
14659         
14660         if(close){
14661             close.hide();
14662         }
14663         
14664         this.validate();
14665         
14666     },
14667
14668     /**
14669      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14670      * will be displayed in the field.  If the value does not match the data value of an existing item,
14671      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14672      * Otherwise the field will be blank (although the value will still be set).
14673      * @param {String} value The value to match
14674      */
14675     setValue : function(v)
14676     {
14677         if(Roo.isIOS && this.useNativeIOS){
14678             this.setIOSValue(v);
14679             return;
14680         }
14681         
14682         if(this.multiple){
14683             this.syncValue();
14684             return;
14685         }
14686         
14687         var text = v;
14688         if(this.valueField){
14689             var r = this.findRecord(this.valueField, v);
14690             if(r){
14691                 text = r.data[this.displayField];
14692             }else if(this.valueNotFoundText !== undefined){
14693                 text = this.valueNotFoundText;
14694             }
14695         }
14696         this.lastSelectionText = text;
14697         if(this.hiddenField){
14698             this.hiddenField.dom.value = v;
14699         }
14700         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14701         this.value = v;
14702         
14703         var close = this.closeTriggerEl();
14704         
14705         if(close){
14706             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14707         }
14708         
14709         this.validate();
14710     },
14711     /**
14712      * @property {Object} the last set data for the element
14713      */
14714     
14715     lastData : false,
14716     /**
14717      * Sets the value of the field based on a object which is related to the record format for the store.
14718      * @param {Object} value the value to set as. or false on reset?
14719      */
14720     setFromData : function(o){
14721         
14722         if(this.multiple){
14723             this.addItem(o);
14724             return;
14725         }
14726             
14727         var dv = ''; // display value
14728         var vv = ''; // value value..
14729         this.lastData = o;
14730         if (this.displayField) {
14731             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14732         } else {
14733             // this is an error condition!!!
14734             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14735         }
14736         
14737         if(this.valueField){
14738             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14739         }
14740         
14741         var close = this.closeTriggerEl();
14742         
14743         if(close){
14744             if(dv.length || vv * 1 > 0){
14745                 close.show() ;
14746                 this.blockFocus=true;
14747             } else {
14748                 close.hide();
14749             }             
14750         }
14751         
14752         if(this.hiddenField){
14753             this.hiddenField.dom.value = vv;
14754             
14755             this.lastSelectionText = dv;
14756             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14757             this.value = vv;
14758             return;
14759         }
14760         // no hidden field.. - we store the value in 'value', but still display
14761         // display field!!!!
14762         this.lastSelectionText = dv;
14763         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14764         this.value = vv;
14765         
14766         
14767         
14768     },
14769     // private
14770     reset : function(){
14771         // overridden so that last data is reset..
14772         
14773         if(this.multiple){
14774             this.clearItem();
14775             return;
14776         }
14777         
14778         this.setValue(this.originalValue);
14779         //this.clearInvalid();
14780         this.lastData = false;
14781         if (this.view) {
14782             this.view.clearSelections();
14783         }
14784         
14785         this.validate();
14786     },
14787     // private
14788     findRecord : function(prop, value){
14789         var record;
14790         if(this.store.getCount() > 0){
14791             this.store.each(function(r){
14792                 if(r.data[prop] == value){
14793                     record = r;
14794                     return false;
14795                 }
14796                 return true;
14797             });
14798         }
14799         return record;
14800     },
14801     
14802     getName: function()
14803     {
14804         // returns hidden if it's set..
14805         if (!this.rendered) {return ''};
14806         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14807         
14808     },
14809     // private
14810     onViewMove : function(e, t){
14811         this.inKeyMode = false;
14812     },
14813
14814     // private
14815     onViewOver : function(e, t){
14816         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14817             return;
14818         }
14819         var item = this.view.findItemFromChild(t);
14820         
14821         if(item){
14822             var index = this.view.indexOf(item);
14823             this.select(index, false);
14824         }
14825     },
14826
14827     // private
14828     onViewClick : function(view, doFocus, el, e)
14829     {
14830         var index = this.view.getSelectedIndexes()[0];
14831         
14832         var r = this.store.getAt(index);
14833         
14834         if(this.tickable){
14835             
14836             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14837                 return;
14838             }
14839             
14840             var rm = false;
14841             var _this = this;
14842             
14843             Roo.each(this.tickItems, function(v,k){
14844                 
14845                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14846                     Roo.log(v);
14847                     _this.tickItems.splice(k, 1);
14848                     
14849                     if(typeof(e) == 'undefined' && view == false){
14850                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14851                     }
14852                     
14853                     rm = true;
14854                     return;
14855                 }
14856             });
14857             
14858             if(rm){
14859                 return;
14860             }
14861             
14862             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14863                 this.tickItems.push(r.data);
14864             }
14865             
14866             if(typeof(e) == 'undefined' && view == false){
14867                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14868             }
14869                     
14870             return;
14871         }
14872         
14873         if(r){
14874             this.onSelect(r, index);
14875         }
14876         if(doFocus !== false && !this.blockFocus){
14877             this.inputEl().focus();
14878         }
14879     },
14880
14881     // private
14882     restrictHeight : function(){
14883         //this.innerList.dom.style.height = '';
14884         //var inner = this.innerList.dom;
14885         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14886         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14887         //this.list.beginUpdate();
14888         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14889         this.list.alignTo(this.inputEl(), this.listAlign);
14890         this.list.alignTo(this.inputEl(), this.listAlign);
14891         //this.list.endUpdate();
14892     },
14893
14894     // private
14895     onEmptyResults : function(){
14896         
14897         if(this.tickable && this.editable){
14898             this.hasFocus = false;
14899             this.restrictHeight();
14900             return;
14901         }
14902         
14903         this.collapse();
14904     },
14905
14906     /**
14907      * Returns true if the dropdown list is expanded, else false.
14908      */
14909     isExpanded : function(){
14910         return this.list.isVisible();
14911     },
14912
14913     /**
14914      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14915      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14916      * @param {String} value The data value of the item to select
14917      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14918      * selected item if it is not currently in view (defaults to true)
14919      * @return {Boolean} True if the value matched an item in the list, else false
14920      */
14921     selectByValue : function(v, scrollIntoView){
14922         if(v !== undefined && v !== null){
14923             var r = this.findRecord(this.valueField || this.displayField, v);
14924             if(r){
14925                 this.select(this.store.indexOf(r), scrollIntoView);
14926                 return true;
14927             }
14928         }
14929         return false;
14930     },
14931
14932     /**
14933      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14934      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14935      * @param {Number} index The zero-based index of the list item to select
14936      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14937      * selected item if it is not currently in view (defaults to true)
14938      */
14939     select : function(index, scrollIntoView){
14940         this.selectedIndex = index;
14941         this.view.select(index);
14942         if(scrollIntoView !== false){
14943             var el = this.view.getNode(index);
14944             /*
14945              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14946              */
14947             if(el){
14948                 this.list.scrollChildIntoView(el, false);
14949             }
14950         }
14951     },
14952
14953     // private
14954     selectNext : function(){
14955         var ct = this.store.getCount();
14956         if(ct > 0){
14957             if(this.selectedIndex == -1){
14958                 this.select(0);
14959             }else if(this.selectedIndex < ct-1){
14960                 this.select(this.selectedIndex+1);
14961             }
14962         }
14963     },
14964
14965     // private
14966     selectPrev : function(){
14967         var ct = this.store.getCount();
14968         if(ct > 0){
14969             if(this.selectedIndex == -1){
14970                 this.select(0);
14971             }else if(this.selectedIndex != 0){
14972                 this.select(this.selectedIndex-1);
14973             }
14974         }
14975     },
14976
14977     // private
14978     onKeyUp : function(e){
14979         if(this.editable !== false && !e.isSpecialKey()){
14980             this.lastKey = e.getKey();
14981             this.dqTask.delay(this.queryDelay);
14982         }
14983     },
14984
14985     // private
14986     validateBlur : function(){
14987         return !this.list || !this.list.isVisible();   
14988     },
14989
14990     // private
14991     initQuery : function(){
14992         
14993         var v = this.getRawValue();
14994         
14995         if(this.tickable && this.editable){
14996             v = this.tickableInputEl().getValue();
14997         }
14998         
14999         this.doQuery(v);
15000     },
15001
15002     // private
15003     doForce : function(){
15004         if(this.inputEl().dom.value.length > 0){
15005             this.inputEl().dom.value =
15006                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
15007              
15008         }
15009     },
15010
15011     /**
15012      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
15013      * query allowing the query action to be canceled if needed.
15014      * @param {String} query The SQL query to execute
15015      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
15016      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
15017      * saved in the current store (defaults to false)
15018      */
15019     doQuery : function(q, forceAll){
15020         
15021         if(q === undefined || q === null){
15022             q = '';
15023         }
15024         var qe = {
15025             query: q,
15026             forceAll: forceAll,
15027             combo: this,
15028             cancel:false
15029         };
15030         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
15031             return false;
15032         }
15033         q = qe.query;
15034         
15035         forceAll = qe.forceAll;
15036         if(forceAll === true || (q.length >= this.minChars)){
15037             
15038             this.hasQuery = true;
15039             
15040             if(this.lastQuery != q || this.alwaysQuery){
15041                 this.lastQuery = q;
15042                 if(this.mode == 'local'){
15043                     this.selectedIndex = -1;
15044                     if(forceAll){
15045                         this.store.clearFilter();
15046                     }else{
15047                         
15048                         if(this.specialFilter){
15049                             this.fireEvent('specialfilter', this);
15050                             this.onLoad();
15051                             return;
15052                         }
15053                         
15054                         this.store.filter(this.displayField, q);
15055                     }
15056                     
15057                     this.store.fireEvent("datachanged", this.store);
15058                     
15059                     this.onLoad();
15060                     
15061                     
15062                 }else{
15063                     
15064                     this.store.baseParams[this.queryParam] = q;
15065                     
15066                     var options = {params : this.getParams(q)};
15067                     
15068                     if(this.loadNext){
15069                         options.add = true;
15070                         options.params.start = this.page * this.pageSize;
15071                     }
15072                     
15073                     this.store.load(options);
15074                     
15075                     /*
15076                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
15077                      *  we should expand the list on onLoad
15078                      *  so command out it
15079                      */
15080 //                    this.expand();
15081                 }
15082             }else{
15083                 this.selectedIndex = -1;
15084                 this.onLoad();   
15085             }
15086         }
15087         
15088         this.loadNext = false;
15089     },
15090     
15091     // private
15092     getParams : function(q){
15093         var p = {};
15094         //p[this.queryParam] = q;
15095         
15096         if(this.pageSize){
15097             p.start = 0;
15098             p.limit = this.pageSize;
15099         }
15100         return p;
15101     },
15102
15103     /**
15104      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
15105      */
15106     collapse : function(){
15107         if(!this.isExpanded()){
15108             return;
15109         }
15110         
15111         this.list.hide();
15112         
15113         this.hasFocus = false;
15114         
15115         if(this.tickable){
15116             this.okBtn.hide();
15117             this.cancelBtn.hide();
15118             this.trigger.show();
15119             
15120             if(this.editable){
15121                 this.tickableInputEl().dom.value = '';
15122                 this.tickableInputEl().blur();
15123             }
15124             
15125         }
15126         
15127         Roo.get(document).un('mousedown', this.collapseIf, this);
15128         Roo.get(document).un('mousewheel', this.collapseIf, this);
15129         if (!this.editable) {
15130             Roo.get(document).un('keydown', this.listKeyPress, this);
15131         }
15132         this.fireEvent('collapse', this);
15133         
15134         this.validate();
15135     },
15136
15137     // private
15138     collapseIf : function(e){
15139         var in_combo  = e.within(this.el);
15140         var in_list =  e.within(this.list);
15141         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
15142         
15143         if (in_combo || in_list || is_list) {
15144             //e.stopPropagation();
15145             return;
15146         }
15147         
15148         if(this.tickable){
15149             this.onTickableFooterButtonClick(e, false, false);
15150         }
15151
15152         this.collapse();
15153         
15154     },
15155
15156     /**
15157      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
15158      */
15159     expand : function(){
15160        
15161         if(this.isExpanded() || !this.hasFocus){
15162             return;
15163         }
15164         
15165         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
15166         this.list.setWidth(lw);
15167         
15168         Roo.log('expand');
15169         
15170         this.list.show();
15171         
15172         this.restrictHeight();
15173         
15174         if(this.tickable){
15175             
15176             this.tickItems = Roo.apply([], this.item);
15177             
15178             this.okBtn.show();
15179             this.cancelBtn.show();
15180             this.trigger.hide();
15181             
15182             if(this.editable){
15183                 this.tickableInputEl().focus();
15184             }
15185             
15186         }
15187         
15188         Roo.get(document).on('mousedown', this.collapseIf, this);
15189         Roo.get(document).on('mousewheel', this.collapseIf, this);
15190         if (!this.editable) {
15191             Roo.get(document).on('keydown', this.listKeyPress, this);
15192         }
15193         
15194         this.fireEvent('expand', this);
15195     },
15196
15197     // private
15198     // Implements the default empty TriggerField.onTriggerClick function
15199     onTriggerClick : function(e)
15200     {
15201         Roo.log('trigger click');
15202         
15203         if(this.disabled || !this.triggerList){
15204             return;
15205         }
15206         
15207         this.page = 0;
15208         this.loadNext = false;
15209         
15210         if(this.isExpanded()){
15211             this.collapse();
15212             if (!this.blockFocus) {
15213                 this.inputEl().focus();
15214             }
15215             
15216         }else {
15217             this.hasFocus = true;
15218             if(this.triggerAction == 'all') {
15219                 this.doQuery(this.allQuery, true);
15220             } else {
15221                 this.doQuery(this.getRawValue());
15222             }
15223             if (!this.blockFocus) {
15224                 this.inputEl().focus();
15225             }
15226         }
15227     },
15228     
15229     onTickableTriggerClick : function(e)
15230     {
15231         if(this.disabled){
15232             return;
15233         }
15234         
15235         this.page = 0;
15236         this.loadNext = false;
15237         this.hasFocus = true;
15238         
15239         if(this.triggerAction == 'all') {
15240             this.doQuery(this.allQuery, true);
15241         } else {
15242             this.doQuery(this.getRawValue());
15243         }
15244     },
15245     
15246     onSearchFieldClick : function(e)
15247     {
15248         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
15249             this.onTickableFooterButtonClick(e, false, false);
15250             return;
15251         }
15252         
15253         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
15254             return;
15255         }
15256         
15257         this.page = 0;
15258         this.loadNext = false;
15259         this.hasFocus = true;
15260         
15261         if(this.triggerAction == 'all') {
15262             this.doQuery(this.allQuery, true);
15263         } else {
15264             this.doQuery(this.getRawValue());
15265         }
15266     },
15267     
15268     listKeyPress : function(e)
15269     {
15270         //Roo.log('listkeypress');
15271         // scroll to first matching element based on key pres..
15272         if (e.isSpecialKey()) {
15273             return false;
15274         }
15275         var k = String.fromCharCode(e.getKey()).toUpperCase();
15276         //Roo.log(k);
15277         var match  = false;
15278         var csel = this.view.getSelectedNodes();
15279         var cselitem = false;
15280         if (csel.length) {
15281             var ix = this.view.indexOf(csel[0]);
15282             cselitem  = this.store.getAt(ix);
15283             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15284                 cselitem = false;
15285             }
15286             
15287         }
15288         
15289         this.store.each(function(v) { 
15290             if (cselitem) {
15291                 // start at existing selection.
15292                 if (cselitem.id == v.id) {
15293                     cselitem = false;
15294                 }
15295                 return true;
15296             }
15297                 
15298             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15299                 match = this.store.indexOf(v);
15300                 return false;
15301             }
15302             return true;
15303         }, this);
15304         
15305         if (match === false) {
15306             return true; // no more action?
15307         }
15308         // scroll to?
15309         this.view.select(match);
15310         var sn = Roo.get(this.view.getSelectedNodes()[0]);
15311         sn.scrollIntoView(sn.dom.parentNode, false);
15312     },
15313     
15314     onViewScroll : function(e, t){
15315         
15316         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){
15317             return;
15318         }
15319         
15320         this.hasQuery = true;
15321         
15322         this.loading = this.list.select('.loading', true).first();
15323         
15324         if(this.loading === null){
15325             this.list.createChild({
15326                 tag: 'div',
15327                 cls: 'loading roo-select2-more-results roo-select2-active',
15328                 html: 'Loading more results...'
15329             });
15330             
15331             this.loading = this.list.select('.loading', true).first();
15332             
15333             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15334             
15335             this.loading.hide();
15336         }
15337         
15338         this.loading.show();
15339         
15340         var _combo = this;
15341         
15342         this.page++;
15343         this.loadNext = true;
15344         
15345         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15346         
15347         return;
15348     },
15349     
15350     addItem : function(o)
15351     {   
15352         var dv = ''; // display value
15353         
15354         if (this.displayField) {
15355             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15356         } else {
15357             // this is an error condition!!!
15358             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
15359         }
15360         
15361         if(!dv.length){
15362             return;
15363         }
15364         
15365         var choice = this.choices.createChild({
15366             tag: 'li',
15367             cls: 'roo-select2-search-choice',
15368             cn: [
15369                 {
15370                     tag: 'div',
15371                     html: dv
15372                 },
15373                 {
15374                     tag: 'a',
15375                     href: '#',
15376                     cls: 'roo-select2-search-choice-close fa fa-times',
15377                     tabindex: '-1'
15378                 }
15379             ]
15380             
15381         }, this.searchField);
15382         
15383         var close = choice.select('a.roo-select2-search-choice-close', true).first();
15384         
15385         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15386         
15387         this.item.push(o);
15388         
15389         this.lastData = o;
15390         
15391         this.syncValue();
15392         
15393         this.inputEl().dom.value = '';
15394         
15395         this.validate();
15396     },
15397     
15398     onRemoveItem : function(e, _self, o)
15399     {
15400         e.preventDefault();
15401         
15402         this.lastItem = Roo.apply([], this.item);
15403         
15404         var index = this.item.indexOf(o.data) * 1;
15405         
15406         if( index < 0){
15407             Roo.log('not this item?!');
15408             return;
15409         }
15410         
15411         this.item.splice(index, 1);
15412         o.item.remove();
15413         
15414         this.syncValue();
15415         
15416         this.fireEvent('remove', this, e);
15417         
15418         this.validate();
15419         
15420     },
15421     
15422     syncValue : function()
15423     {
15424         if(!this.item.length){
15425             this.clearValue();
15426             return;
15427         }
15428             
15429         var value = [];
15430         var _this = this;
15431         Roo.each(this.item, function(i){
15432             if(_this.valueField){
15433                 value.push(i[_this.valueField]);
15434                 return;
15435             }
15436
15437             value.push(i);
15438         });
15439
15440         this.value = value.join(',');
15441
15442         if(this.hiddenField){
15443             this.hiddenField.dom.value = this.value;
15444         }
15445         
15446         this.store.fireEvent("datachanged", this.store);
15447         
15448         this.validate();
15449     },
15450     
15451     clearItem : function()
15452     {
15453         if(!this.multiple){
15454             return;
15455         }
15456         
15457         this.item = [];
15458         
15459         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15460            c.remove();
15461         });
15462         
15463         this.syncValue();
15464         
15465         this.validate();
15466         
15467         if(this.tickable && !Roo.isTouch){
15468             this.view.refresh();
15469         }
15470     },
15471     
15472     inputEl: function ()
15473     {
15474         if(Roo.isIOS && this.useNativeIOS){
15475             return this.el.select('select.roo-ios-select', true).first();
15476         }
15477         
15478         if(Roo.isTouch && this.mobileTouchView){
15479             return this.el.select('input.form-control',true).first();
15480         }
15481         
15482         if(this.tickable){
15483             return this.searchField;
15484         }
15485         
15486         return this.el.select('input.form-control',true).first();
15487     },
15488     
15489     onTickableFooterButtonClick : function(e, btn, el)
15490     {
15491         e.preventDefault();
15492         
15493         this.lastItem = Roo.apply([], this.item);
15494         
15495         if(btn && btn.name == 'cancel'){
15496             this.tickItems = Roo.apply([], this.item);
15497             this.collapse();
15498             return;
15499         }
15500         
15501         this.clearItem();
15502         
15503         var _this = this;
15504         
15505         Roo.each(this.tickItems, function(o){
15506             _this.addItem(o);
15507         });
15508         
15509         this.collapse();
15510         
15511     },
15512     
15513     validate : function()
15514     {
15515         if(this.getVisibilityEl().hasClass('hidden')){
15516             return true;
15517         }
15518         
15519         var v = this.getRawValue();
15520         
15521         if(this.multiple){
15522             v = this.getValue();
15523         }
15524         
15525         if(this.disabled || this.allowBlank || v.length){
15526             this.markValid();
15527             return true;
15528         }
15529         
15530         this.markInvalid();
15531         return false;
15532     },
15533     
15534     tickableInputEl : function()
15535     {
15536         if(!this.tickable || !this.editable){
15537             return this.inputEl();
15538         }
15539         
15540         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15541     },
15542     
15543     
15544     getAutoCreateTouchView : function()
15545     {
15546         var id = Roo.id();
15547         
15548         var cfg = {
15549             cls: 'form-group' //input-group
15550         };
15551         
15552         var input =  {
15553             tag: 'input',
15554             id : id,
15555             type : this.inputType,
15556             cls : 'form-control x-combo-noedit',
15557             autocomplete: 'new-password',
15558             placeholder : this.placeholder || '',
15559             readonly : true
15560         };
15561         
15562         if (this.name) {
15563             input.name = this.name;
15564         }
15565         
15566         if (this.size) {
15567             input.cls += ' input-' + this.size;
15568         }
15569         
15570         if (this.disabled) {
15571             input.disabled = true;
15572         }
15573         
15574         var inputblock = {
15575             cls : '',
15576             cn : [
15577                 input
15578             ]
15579         };
15580         
15581         if(this.before){
15582             inputblock.cls += ' input-group';
15583             
15584             inputblock.cn.unshift({
15585                 tag :'span',
15586                 cls : 'input-group-addon input-group-prepend input-group-text',
15587                 html : this.before
15588             });
15589         }
15590         
15591         if(this.removable && !this.multiple){
15592             inputblock.cls += ' roo-removable';
15593             
15594             inputblock.cn.push({
15595                 tag: 'button',
15596                 html : 'x',
15597                 cls : 'roo-combo-removable-btn close'
15598             });
15599         }
15600
15601         if(this.hasFeedback && !this.allowBlank){
15602             
15603             inputblock.cls += ' has-feedback';
15604             
15605             inputblock.cn.push({
15606                 tag: 'span',
15607                 cls: 'glyphicon form-control-feedback'
15608             });
15609             
15610         }
15611         
15612         if (this.after) {
15613             
15614             inputblock.cls += (this.before) ? '' : ' input-group';
15615             
15616             inputblock.cn.push({
15617                 tag :'span',
15618                 cls : 'input-group-addon input-group-append input-group-text',
15619                 html : this.after
15620             });
15621         }
15622
15623         
15624         var ibwrap = inputblock;
15625         
15626         if(this.multiple){
15627             ibwrap = {
15628                 tag: 'ul',
15629                 cls: 'roo-select2-choices',
15630                 cn:[
15631                     {
15632                         tag: 'li',
15633                         cls: 'roo-select2-search-field',
15634                         cn: [
15635
15636                             inputblock
15637                         ]
15638                     }
15639                 ]
15640             };
15641         
15642             
15643         }
15644         
15645         var combobox = {
15646             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15647             cn: [
15648                 {
15649                     tag: 'input',
15650                     type : 'hidden',
15651                     cls: 'form-hidden-field'
15652                 },
15653                 ibwrap
15654             ]
15655         };
15656         
15657         if(!this.multiple && this.showToggleBtn){
15658             
15659             var caret = {
15660                 cls: 'caret'
15661             };
15662             
15663             if (this.caret != false) {
15664                 caret = {
15665                      tag: 'i',
15666                      cls: 'fa fa-' + this.caret
15667                 };
15668                 
15669             }
15670             
15671             combobox.cn.push({
15672                 tag :'span',
15673                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15674                 cn : [
15675                     Roo.bootstrap.version == 3 ? caret : '',
15676                     {
15677                         tag: 'span',
15678                         cls: 'combobox-clear',
15679                         cn  : [
15680                             {
15681                                 tag : 'i',
15682                                 cls: 'icon-remove'
15683                             }
15684                         ]
15685                     }
15686                 ]
15687
15688             })
15689         }
15690         
15691         if(this.multiple){
15692             combobox.cls += ' roo-select2-container-multi';
15693         }
15694         
15695         var align = this.labelAlign || this.parentLabelAlign();
15696         
15697         if (align ==='left' && this.fieldLabel.length) {
15698
15699             cfg.cn = [
15700                 {
15701                    tag : 'i',
15702                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15703                    tooltip : 'This field is required'
15704                 },
15705                 {
15706                     tag: 'label',
15707                     cls : 'control-label col-form-label',
15708                     html : this.fieldLabel
15709
15710                 },
15711                 {
15712                     cls : '', 
15713                     cn: [
15714                         combobox
15715                     ]
15716                 }
15717             ];
15718             
15719             var labelCfg = cfg.cn[1];
15720             var contentCfg = cfg.cn[2];
15721             
15722
15723             if(this.indicatorpos == 'right'){
15724                 cfg.cn = [
15725                     {
15726                         tag: 'label',
15727                         'for' :  id,
15728                         cls : 'control-label col-form-label',
15729                         cn : [
15730                             {
15731                                 tag : 'span',
15732                                 html : this.fieldLabel
15733                             },
15734                             {
15735                                 tag : 'i',
15736                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15737                                 tooltip : 'This field is required'
15738                             }
15739                         ]
15740                     },
15741                     {
15742                         cls : "",
15743                         cn: [
15744                             combobox
15745                         ]
15746                     }
15747
15748                 ];
15749                 
15750                 labelCfg = cfg.cn[0];
15751                 contentCfg = cfg.cn[1];
15752             }
15753             
15754            
15755             
15756             if(this.labelWidth > 12){
15757                 labelCfg.style = "width: " + this.labelWidth + 'px';
15758             }
15759             
15760             if(this.labelWidth < 13 && this.labelmd == 0){
15761                 this.labelmd = this.labelWidth;
15762             }
15763             
15764             if(this.labellg > 0){
15765                 labelCfg.cls += ' col-lg-' + this.labellg;
15766                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15767             }
15768             
15769             if(this.labelmd > 0){
15770                 labelCfg.cls += ' col-md-' + this.labelmd;
15771                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15772             }
15773             
15774             if(this.labelsm > 0){
15775                 labelCfg.cls += ' col-sm-' + this.labelsm;
15776                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15777             }
15778             
15779             if(this.labelxs > 0){
15780                 labelCfg.cls += ' col-xs-' + this.labelxs;
15781                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15782             }
15783                 
15784                 
15785         } else if ( this.fieldLabel.length) {
15786             cfg.cn = [
15787                 {
15788                    tag : 'i',
15789                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15790                    tooltip : 'This field is required'
15791                 },
15792                 {
15793                     tag: 'label',
15794                     cls : 'control-label',
15795                     html : this.fieldLabel
15796
15797                 },
15798                 {
15799                     cls : '', 
15800                     cn: [
15801                         combobox
15802                     ]
15803                 }
15804             ];
15805             
15806             if(this.indicatorpos == 'right'){
15807                 cfg.cn = [
15808                     {
15809                         tag: 'label',
15810                         cls : 'control-label',
15811                         html : this.fieldLabel,
15812                         cn : [
15813                             {
15814                                tag : 'i',
15815                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15816                                tooltip : 'This field is required'
15817                             }
15818                         ]
15819                     },
15820                     {
15821                         cls : '', 
15822                         cn: [
15823                             combobox
15824                         ]
15825                     }
15826                 ];
15827             }
15828         } else {
15829             cfg.cn = combobox;    
15830         }
15831         
15832         
15833         var settings = this;
15834         
15835         ['xs','sm','md','lg'].map(function(size){
15836             if (settings[size]) {
15837                 cfg.cls += ' col-' + size + '-' + settings[size];
15838             }
15839         });
15840         
15841         return cfg;
15842     },
15843     
15844     initTouchView : function()
15845     {
15846         this.renderTouchView();
15847         
15848         this.touchViewEl.on('scroll', function(){
15849             this.el.dom.scrollTop = 0;
15850         }, this);
15851         
15852         this.originalValue = this.getValue();
15853         
15854         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15855         
15856         this.inputEl().on("click", this.showTouchView, this);
15857         if (this.triggerEl) {
15858             this.triggerEl.on("click", this.showTouchView, this);
15859         }
15860         
15861         
15862         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15863         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15864         
15865         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15866         
15867         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15868         this.store.on('load', this.onTouchViewLoad, this);
15869         this.store.on('loadexception', this.onTouchViewLoadException, this);
15870         
15871         if(this.hiddenName){
15872             
15873             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15874             
15875             this.hiddenField.dom.value =
15876                 this.hiddenValue !== undefined ? this.hiddenValue :
15877                 this.value !== undefined ? this.value : '';
15878         
15879             this.el.dom.removeAttribute('name');
15880             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15881         }
15882         
15883         if(this.multiple){
15884             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15885             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15886         }
15887         
15888         if(this.removable && !this.multiple){
15889             var close = this.closeTriggerEl();
15890             if(close){
15891                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15892                 close.on('click', this.removeBtnClick, this, close);
15893             }
15894         }
15895         /*
15896          * fix the bug in Safari iOS8
15897          */
15898         this.inputEl().on("focus", function(e){
15899             document.activeElement.blur();
15900         }, this);
15901         
15902         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15903         
15904         return;
15905         
15906         
15907     },
15908     
15909     renderTouchView : function()
15910     {
15911         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15912         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15913         
15914         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15915         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15916         
15917         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15918         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15919         this.touchViewBodyEl.setStyle('overflow', 'auto');
15920         
15921         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15922         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15923         
15924         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15925         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15926         
15927     },
15928     
15929     showTouchView : function()
15930     {
15931         if(this.disabled){
15932             return;
15933         }
15934         
15935         this.touchViewHeaderEl.hide();
15936
15937         if(this.modalTitle.length){
15938             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15939             this.touchViewHeaderEl.show();
15940         }
15941
15942         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15943         this.touchViewEl.show();
15944
15945         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15946         
15947         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15948         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15949
15950         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15951
15952         if(this.modalTitle.length){
15953             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15954         }
15955         
15956         this.touchViewBodyEl.setHeight(bodyHeight);
15957
15958         if(this.animate){
15959             var _this = this;
15960             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15961         }else{
15962             this.touchViewEl.addClass('in');
15963         }
15964         
15965         if(this._touchViewMask){
15966             Roo.get(document.body).addClass("x-body-masked");
15967             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15968             this._touchViewMask.setStyle('z-index', 10000);
15969             this._touchViewMask.addClass('show');
15970         }
15971         
15972         this.doTouchViewQuery();
15973         
15974     },
15975     
15976     hideTouchView : function()
15977     {
15978         this.touchViewEl.removeClass('in');
15979
15980         if(this.animate){
15981             var _this = this;
15982             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15983         }else{
15984             this.touchViewEl.setStyle('display', 'none');
15985         }
15986         
15987         if(this._touchViewMask){
15988             this._touchViewMask.removeClass('show');
15989             Roo.get(document.body).removeClass("x-body-masked");
15990         }
15991     },
15992     
15993     setTouchViewValue : function()
15994     {
15995         if(this.multiple){
15996             this.clearItem();
15997         
15998             var _this = this;
15999
16000             Roo.each(this.tickItems, function(o){
16001                 this.addItem(o);
16002             }, this);
16003         }
16004         
16005         this.hideTouchView();
16006     },
16007     
16008     doTouchViewQuery : function()
16009     {
16010         var qe = {
16011             query: '',
16012             forceAll: true,
16013             combo: this,
16014             cancel:false
16015         };
16016         
16017         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
16018             return false;
16019         }
16020         
16021         if(!this.alwaysQuery || this.mode == 'local'){
16022             this.onTouchViewLoad();
16023             return;
16024         }
16025         
16026         this.store.load();
16027     },
16028     
16029     onTouchViewBeforeLoad : function(combo,opts)
16030     {
16031         return;
16032     },
16033
16034     // private
16035     onTouchViewLoad : function()
16036     {
16037         if(this.store.getCount() < 1){
16038             this.onTouchViewEmptyResults();
16039             return;
16040         }
16041         
16042         this.clearTouchView();
16043         
16044         var rawValue = this.getRawValue();
16045         
16046         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
16047         
16048         this.tickItems = [];
16049         
16050         this.store.data.each(function(d, rowIndex){
16051             var row = this.touchViewListGroup.createChild(template);
16052             
16053             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
16054                 row.addClass(d.data.cls);
16055             }
16056             
16057             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16058                 var cfg = {
16059                     data : d.data,
16060                     html : d.data[this.displayField]
16061                 };
16062                 
16063                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
16064                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
16065                 }
16066             }
16067             row.removeClass('selected');
16068             if(!this.multiple && this.valueField &&
16069                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
16070             {
16071                 // radio buttons..
16072                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16073                 row.addClass('selected');
16074             }
16075             
16076             if(this.multiple && this.valueField &&
16077                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
16078             {
16079                 
16080                 // checkboxes...
16081                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16082                 this.tickItems.push(d.data);
16083             }
16084             
16085             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
16086             
16087         }, this);
16088         
16089         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
16090         
16091         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16092
16093         if(this.modalTitle.length){
16094             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16095         }
16096
16097         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
16098         
16099         if(this.mobile_restrict_height && listHeight < bodyHeight){
16100             this.touchViewBodyEl.setHeight(listHeight);
16101         }
16102         
16103         var _this = this;
16104         
16105         if(firstChecked && listHeight > bodyHeight){
16106             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
16107         }
16108         
16109     },
16110     
16111     onTouchViewLoadException : function()
16112     {
16113         this.hideTouchView();
16114     },
16115     
16116     onTouchViewEmptyResults : function()
16117     {
16118         this.clearTouchView();
16119         
16120         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
16121         
16122         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
16123         
16124     },
16125     
16126     clearTouchView : function()
16127     {
16128         this.touchViewListGroup.dom.innerHTML = '';
16129     },
16130     
16131     onTouchViewClick : function(e, el, o)
16132     {
16133         e.preventDefault();
16134         
16135         var row = o.row;
16136         var rowIndex = o.rowIndex;
16137         
16138         var r = this.store.getAt(rowIndex);
16139         
16140         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
16141             
16142             if(!this.multiple){
16143                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
16144                     c.dom.removeAttribute('checked');
16145                 }, this);
16146
16147                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16148
16149                 this.setFromData(r.data);
16150
16151                 var close = this.closeTriggerEl();
16152
16153                 if(close){
16154                     close.show();
16155                 }
16156
16157                 this.hideTouchView();
16158
16159                 this.fireEvent('select', this, r, rowIndex);
16160
16161                 return;
16162             }
16163
16164             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
16165                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
16166                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
16167                 return;
16168             }
16169
16170             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16171             this.addItem(r.data);
16172             this.tickItems.push(r.data);
16173         }
16174     },
16175     
16176     getAutoCreateNativeIOS : function()
16177     {
16178         var cfg = {
16179             cls: 'form-group' //input-group,
16180         };
16181         
16182         var combobox =  {
16183             tag: 'select',
16184             cls : 'roo-ios-select'
16185         };
16186         
16187         if (this.name) {
16188             combobox.name = this.name;
16189         }
16190         
16191         if (this.disabled) {
16192             combobox.disabled = true;
16193         }
16194         
16195         var settings = this;
16196         
16197         ['xs','sm','md','lg'].map(function(size){
16198             if (settings[size]) {
16199                 cfg.cls += ' col-' + size + '-' + settings[size];
16200             }
16201         });
16202         
16203         cfg.cn = combobox;
16204         
16205         return cfg;
16206         
16207     },
16208     
16209     initIOSView : function()
16210     {
16211         this.store.on('load', this.onIOSViewLoad, this);
16212         
16213         return;
16214     },
16215     
16216     onIOSViewLoad : function()
16217     {
16218         if(this.store.getCount() < 1){
16219             return;
16220         }
16221         
16222         this.clearIOSView();
16223         
16224         if(this.allowBlank) {
16225             
16226             var default_text = '-- SELECT --';
16227             
16228             if(this.placeholder.length){
16229                 default_text = this.placeholder;
16230             }
16231             
16232             if(this.emptyTitle.length){
16233                 default_text += ' - ' + this.emptyTitle + ' -';
16234             }
16235             
16236             var opt = this.inputEl().createChild({
16237                 tag: 'option',
16238                 value : 0,
16239                 html : default_text
16240             });
16241             
16242             var o = {};
16243             o[this.valueField] = 0;
16244             o[this.displayField] = default_text;
16245             
16246             this.ios_options.push({
16247                 data : o,
16248                 el : opt
16249             });
16250             
16251         }
16252         
16253         this.store.data.each(function(d, rowIndex){
16254             
16255             var html = '';
16256             
16257             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16258                 html = d.data[this.displayField];
16259             }
16260             
16261             var value = '';
16262             
16263             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
16264                 value = d.data[this.valueField];
16265             }
16266             
16267             var option = {
16268                 tag: 'option',
16269                 value : value,
16270                 html : html
16271             };
16272             
16273             if(this.value == d.data[this.valueField]){
16274                 option['selected'] = true;
16275             }
16276             
16277             var opt = this.inputEl().createChild(option);
16278             
16279             this.ios_options.push({
16280                 data : d.data,
16281                 el : opt
16282             });
16283             
16284         }, this);
16285         
16286         this.inputEl().on('change', function(){
16287            this.fireEvent('select', this);
16288         }, this);
16289         
16290     },
16291     
16292     clearIOSView: function()
16293     {
16294         this.inputEl().dom.innerHTML = '';
16295         
16296         this.ios_options = [];
16297     },
16298     
16299     setIOSValue: function(v)
16300     {
16301         this.value = v;
16302         
16303         if(!this.ios_options){
16304             return;
16305         }
16306         
16307         Roo.each(this.ios_options, function(opts){
16308            
16309            opts.el.dom.removeAttribute('selected');
16310            
16311            if(opts.data[this.valueField] != v){
16312                return;
16313            }
16314            
16315            opts.el.dom.setAttribute('selected', true);
16316            
16317         }, this);
16318     }
16319
16320     /** 
16321     * @cfg {Boolean} grow 
16322     * @hide 
16323     */
16324     /** 
16325     * @cfg {Number} growMin 
16326     * @hide 
16327     */
16328     /** 
16329     * @cfg {Number} growMax 
16330     * @hide 
16331     */
16332     /**
16333      * @hide
16334      * @method autoSize
16335      */
16336 });
16337
16338 Roo.apply(Roo.bootstrap.ComboBox,  {
16339     
16340     header : {
16341         tag: 'div',
16342         cls: 'modal-header',
16343         cn: [
16344             {
16345                 tag: 'h4',
16346                 cls: 'modal-title'
16347             }
16348         ]
16349     },
16350     
16351     body : {
16352         tag: 'div',
16353         cls: 'modal-body',
16354         cn: [
16355             {
16356                 tag: 'ul',
16357                 cls: 'list-group'
16358             }
16359         ]
16360     },
16361     
16362     listItemRadio : {
16363         tag: 'li',
16364         cls: 'list-group-item',
16365         cn: [
16366             {
16367                 tag: 'span',
16368                 cls: 'roo-combobox-list-group-item-value'
16369             },
16370             {
16371                 tag: 'div',
16372                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16373                 cn: [
16374                     {
16375                         tag: 'input',
16376                         type: 'radio'
16377                     },
16378                     {
16379                         tag: 'label'
16380                     }
16381                 ]
16382             }
16383         ]
16384     },
16385     
16386     listItemCheckbox : {
16387         tag: 'li',
16388         cls: 'list-group-item',
16389         cn: [
16390             {
16391                 tag: 'span',
16392                 cls: 'roo-combobox-list-group-item-value'
16393             },
16394             {
16395                 tag: 'div',
16396                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16397                 cn: [
16398                     {
16399                         tag: 'input',
16400                         type: 'checkbox'
16401                     },
16402                     {
16403                         tag: 'label'
16404                     }
16405                 ]
16406             }
16407         ]
16408     },
16409     
16410     emptyResult : {
16411         tag: 'div',
16412         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16413     },
16414     
16415     footer : {
16416         tag: 'div',
16417         cls: 'modal-footer',
16418         cn: [
16419             {
16420                 tag: 'div',
16421                 cls: 'row',
16422                 cn: [
16423                     {
16424                         tag: 'div',
16425                         cls: 'col-xs-6 text-left',
16426                         cn: {
16427                             tag: 'button',
16428                             cls: 'btn btn-danger roo-touch-view-cancel',
16429                             html: 'Cancel'
16430                         }
16431                     },
16432                     {
16433                         tag: 'div',
16434                         cls: 'col-xs-6 text-right',
16435                         cn: {
16436                             tag: 'button',
16437                             cls: 'btn btn-success roo-touch-view-ok',
16438                             html: 'OK'
16439                         }
16440                     }
16441                 ]
16442             }
16443         ]
16444         
16445     }
16446 });
16447
16448 Roo.apply(Roo.bootstrap.ComboBox,  {
16449     
16450     touchViewTemplate : {
16451         tag: 'div',
16452         cls: 'modal fade roo-combobox-touch-view',
16453         cn: [
16454             {
16455                 tag: 'div',
16456                 cls: 'modal-dialog',
16457                 style : 'position:fixed', // we have to fix position....
16458                 cn: [
16459                     {
16460                         tag: 'div',
16461                         cls: 'modal-content',
16462                         cn: [
16463                             Roo.bootstrap.ComboBox.header,
16464                             Roo.bootstrap.ComboBox.body,
16465                             Roo.bootstrap.ComboBox.footer
16466                         ]
16467                     }
16468                 ]
16469             }
16470         ]
16471     }
16472 });/*
16473  * Based on:
16474  * Ext JS Library 1.1.1
16475  * Copyright(c) 2006-2007, Ext JS, LLC.
16476  *
16477  * Originally Released Under LGPL - original licence link has changed is not relivant.
16478  *
16479  * Fork - LGPL
16480  * <script type="text/javascript">
16481  */
16482
16483 /**
16484  * @class Roo.View
16485  * @extends Roo.util.Observable
16486  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16487  * This class also supports single and multi selection modes. <br>
16488  * Create a data model bound view:
16489  <pre><code>
16490  var store = new Roo.data.Store(...);
16491
16492  var view = new Roo.View({
16493     el : "my-element",
16494     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16495  
16496     singleSelect: true,
16497     selectedClass: "ydataview-selected",
16498     store: store
16499  });
16500
16501  // listen for node click?
16502  view.on("click", function(vw, index, node, e){
16503  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16504  });
16505
16506  // load XML data
16507  dataModel.load("foobar.xml");
16508  </code></pre>
16509  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16510  * <br><br>
16511  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16512  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16513  * 
16514  * Note: old style constructor is still suported (container, template, config)
16515  * 
16516  * @constructor
16517  * Create a new View
16518  * @param {Object} config The config object
16519  * 
16520  */
16521 Roo.View = function(config, depreciated_tpl, depreciated_config){
16522     
16523     this.parent = false;
16524     
16525     if (typeof(depreciated_tpl) == 'undefined') {
16526         // new way.. - universal constructor.
16527         Roo.apply(this, config);
16528         this.el  = Roo.get(this.el);
16529     } else {
16530         // old format..
16531         this.el  = Roo.get(config);
16532         this.tpl = depreciated_tpl;
16533         Roo.apply(this, depreciated_config);
16534     }
16535     this.wrapEl  = this.el.wrap().wrap();
16536     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16537     
16538     
16539     if(typeof(this.tpl) == "string"){
16540         this.tpl = new Roo.Template(this.tpl);
16541     } else {
16542         // support xtype ctors..
16543         this.tpl = new Roo.factory(this.tpl, Roo);
16544     }
16545     
16546     
16547     this.tpl.compile();
16548     
16549     /** @private */
16550     this.addEvents({
16551         /**
16552          * @event beforeclick
16553          * Fires before a click is processed. Returns false to cancel the default action.
16554          * @param {Roo.View} this
16555          * @param {Number} index The index of the target node
16556          * @param {HTMLElement} node The target node
16557          * @param {Roo.EventObject} e The raw event object
16558          */
16559             "beforeclick" : true,
16560         /**
16561          * @event click
16562          * Fires when a template node is clicked.
16563          * @param {Roo.View} this
16564          * @param {Number} index The index of the target node
16565          * @param {HTMLElement} node The target node
16566          * @param {Roo.EventObject} e The raw event object
16567          */
16568             "click" : true,
16569         /**
16570          * @event dblclick
16571          * Fires when a template node is double clicked.
16572          * @param {Roo.View} this
16573          * @param {Number} index The index of the target node
16574          * @param {HTMLElement} node The target node
16575          * @param {Roo.EventObject} e The raw event object
16576          */
16577             "dblclick" : true,
16578         /**
16579          * @event contextmenu
16580          * Fires when a template node is right clicked.
16581          * @param {Roo.View} this
16582          * @param {Number} index The index of the target node
16583          * @param {HTMLElement} node The target node
16584          * @param {Roo.EventObject} e The raw event object
16585          */
16586             "contextmenu" : true,
16587         /**
16588          * @event selectionchange
16589          * Fires when the selected nodes change.
16590          * @param {Roo.View} this
16591          * @param {Array} selections Array of the selected nodes
16592          */
16593             "selectionchange" : true,
16594     
16595         /**
16596          * @event beforeselect
16597          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16598          * @param {Roo.View} this
16599          * @param {HTMLElement} node The node to be selected
16600          * @param {Array} selections Array of currently selected nodes
16601          */
16602             "beforeselect" : true,
16603         /**
16604          * @event preparedata
16605          * Fires on every row to render, to allow you to change the data.
16606          * @param {Roo.View} this
16607          * @param {Object} data to be rendered (change this)
16608          */
16609           "preparedata" : true
16610           
16611           
16612         });
16613
16614
16615
16616     this.el.on({
16617         "click": this.onClick,
16618         "dblclick": this.onDblClick,
16619         "contextmenu": this.onContextMenu,
16620         scope:this
16621     });
16622
16623     this.selections = [];
16624     this.nodes = [];
16625     this.cmp = new Roo.CompositeElementLite([]);
16626     if(this.store){
16627         this.store = Roo.factory(this.store, Roo.data);
16628         this.setStore(this.store, true);
16629     }
16630     
16631     if ( this.footer && this.footer.xtype) {
16632            
16633          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16634         
16635         this.footer.dataSource = this.store;
16636         this.footer.container = fctr;
16637         this.footer = Roo.factory(this.footer, Roo);
16638         fctr.insertFirst(this.el);
16639         
16640         // this is a bit insane - as the paging toolbar seems to detach the el..
16641 //        dom.parentNode.parentNode.parentNode
16642          // they get detached?
16643     }
16644     
16645     
16646     Roo.View.superclass.constructor.call(this);
16647     
16648     
16649 };
16650
16651 Roo.extend(Roo.View, Roo.util.Observable, {
16652     
16653      /**
16654      * @cfg {Roo.data.Store} store Data store to load data from.
16655      */
16656     store : false,
16657     
16658     /**
16659      * @cfg {String|Roo.Element} el The container element.
16660      */
16661     el : '',
16662     
16663     /**
16664      * @cfg {String|Roo.Template} tpl The template used by this View 
16665      */
16666     tpl : false,
16667     /**
16668      * @cfg {String} dataName the named area of the template to use as the data area
16669      *                          Works with domtemplates roo-name="name"
16670      */
16671     dataName: false,
16672     /**
16673      * @cfg {String} selectedClass The css class to add to selected nodes
16674      */
16675     selectedClass : "x-view-selected",
16676      /**
16677      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16678      */
16679     emptyText : "",
16680     
16681     /**
16682      * @cfg {String} text to display on mask (default Loading)
16683      */
16684     mask : false,
16685     /**
16686      * @cfg {Boolean} multiSelect Allow multiple selection
16687      */
16688     multiSelect : false,
16689     /**
16690      * @cfg {Boolean} singleSelect Allow single selection
16691      */
16692     singleSelect:  false,
16693     
16694     /**
16695      * @cfg {Boolean} toggleSelect - selecting 
16696      */
16697     toggleSelect : false,
16698     
16699     /**
16700      * @cfg {Boolean} tickable - selecting 
16701      */
16702     tickable : false,
16703     
16704     /**
16705      * Returns the element this view is bound to.
16706      * @return {Roo.Element}
16707      */
16708     getEl : function(){
16709         return this.wrapEl;
16710     },
16711     
16712     
16713
16714     /**
16715      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16716      */
16717     refresh : function(){
16718         //Roo.log('refresh');
16719         var t = this.tpl;
16720         
16721         // if we are using something like 'domtemplate', then
16722         // the what gets used is:
16723         // t.applySubtemplate(NAME, data, wrapping data..)
16724         // the outer template then get' applied with
16725         //     the store 'extra data'
16726         // and the body get's added to the
16727         //      roo-name="data" node?
16728         //      <span class='roo-tpl-{name}'></span> ?????
16729         
16730         
16731         
16732         this.clearSelections();
16733         this.el.update("");
16734         var html = [];
16735         var records = this.store.getRange();
16736         if(records.length < 1) {
16737             
16738             // is this valid??  = should it render a template??
16739             
16740             this.el.update(this.emptyText);
16741             return;
16742         }
16743         var el = this.el;
16744         if (this.dataName) {
16745             this.el.update(t.apply(this.store.meta)); //????
16746             el = this.el.child('.roo-tpl-' + this.dataName);
16747         }
16748         
16749         for(var i = 0, len = records.length; i < len; i++){
16750             var data = this.prepareData(records[i].data, i, records[i]);
16751             this.fireEvent("preparedata", this, data, i, records[i]);
16752             
16753             var d = Roo.apply({}, data);
16754             
16755             if(this.tickable){
16756                 Roo.apply(d, {'roo-id' : Roo.id()});
16757                 
16758                 var _this = this;
16759             
16760                 Roo.each(this.parent.item, function(item){
16761                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16762                         return;
16763                     }
16764                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16765                 });
16766             }
16767             
16768             html[html.length] = Roo.util.Format.trim(
16769                 this.dataName ?
16770                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16771                     t.apply(d)
16772             );
16773         }
16774         
16775         
16776         
16777         el.update(html.join(""));
16778         this.nodes = el.dom.childNodes;
16779         this.updateIndexes(0);
16780     },
16781     
16782
16783     /**
16784      * Function to override to reformat the data that is sent to
16785      * the template for each node.
16786      * DEPRICATED - use the preparedata event handler.
16787      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16788      * a JSON object for an UpdateManager bound view).
16789      */
16790     prepareData : function(data, index, record)
16791     {
16792         this.fireEvent("preparedata", this, data, index, record);
16793         return data;
16794     },
16795
16796     onUpdate : function(ds, record){
16797         // Roo.log('on update');   
16798         this.clearSelections();
16799         var index = this.store.indexOf(record);
16800         var n = this.nodes[index];
16801         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16802         n.parentNode.removeChild(n);
16803         this.updateIndexes(index, index);
16804     },
16805
16806     
16807     
16808 // --------- FIXME     
16809     onAdd : function(ds, records, index)
16810     {
16811         //Roo.log(['on Add', ds, records, index] );        
16812         this.clearSelections();
16813         if(this.nodes.length == 0){
16814             this.refresh();
16815             return;
16816         }
16817         var n = this.nodes[index];
16818         for(var i = 0, len = records.length; i < len; i++){
16819             var d = this.prepareData(records[i].data, i, records[i]);
16820             if(n){
16821                 this.tpl.insertBefore(n, d);
16822             }else{
16823                 
16824                 this.tpl.append(this.el, d);
16825             }
16826         }
16827         this.updateIndexes(index);
16828     },
16829
16830     onRemove : function(ds, record, index){
16831        // Roo.log('onRemove');
16832         this.clearSelections();
16833         var el = this.dataName  ?
16834             this.el.child('.roo-tpl-' + this.dataName) :
16835             this.el; 
16836         
16837         el.dom.removeChild(this.nodes[index]);
16838         this.updateIndexes(index);
16839     },
16840
16841     /**
16842      * Refresh an individual node.
16843      * @param {Number} index
16844      */
16845     refreshNode : function(index){
16846         this.onUpdate(this.store, this.store.getAt(index));
16847     },
16848
16849     updateIndexes : function(startIndex, endIndex){
16850         var ns = this.nodes;
16851         startIndex = startIndex || 0;
16852         endIndex = endIndex || ns.length - 1;
16853         for(var i = startIndex; i <= endIndex; i++){
16854             ns[i].nodeIndex = i;
16855         }
16856     },
16857
16858     /**
16859      * Changes the data store this view uses and refresh the view.
16860      * @param {Store} store
16861      */
16862     setStore : function(store, initial){
16863         if(!initial && this.store){
16864             this.store.un("datachanged", this.refresh);
16865             this.store.un("add", this.onAdd);
16866             this.store.un("remove", this.onRemove);
16867             this.store.un("update", this.onUpdate);
16868             this.store.un("clear", this.refresh);
16869             this.store.un("beforeload", this.onBeforeLoad);
16870             this.store.un("load", this.onLoad);
16871             this.store.un("loadexception", this.onLoad);
16872         }
16873         if(store){
16874           
16875             store.on("datachanged", this.refresh, this);
16876             store.on("add", this.onAdd, this);
16877             store.on("remove", this.onRemove, this);
16878             store.on("update", this.onUpdate, this);
16879             store.on("clear", this.refresh, this);
16880             store.on("beforeload", this.onBeforeLoad, this);
16881             store.on("load", this.onLoad, this);
16882             store.on("loadexception", this.onLoad, this);
16883         }
16884         
16885         if(store){
16886             this.refresh();
16887         }
16888     },
16889     /**
16890      * onbeforeLoad - masks the loading area.
16891      *
16892      */
16893     onBeforeLoad : function(store,opts)
16894     {
16895          //Roo.log('onBeforeLoad');   
16896         if (!opts.add) {
16897             this.el.update("");
16898         }
16899         this.el.mask(this.mask ? this.mask : "Loading" ); 
16900     },
16901     onLoad : function ()
16902     {
16903         this.el.unmask();
16904     },
16905     
16906
16907     /**
16908      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16909      * @param {HTMLElement} node
16910      * @return {HTMLElement} The template node
16911      */
16912     findItemFromChild : function(node){
16913         var el = this.dataName  ?
16914             this.el.child('.roo-tpl-' + this.dataName,true) :
16915             this.el.dom; 
16916         
16917         if(!node || node.parentNode == el){
16918                     return node;
16919             }
16920             var p = node.parentNode;
16921             while(p && p != el){
16922             if(p.parentNode == el){
16923                 return p;
16924             }
16925             p = p.parentNode;
16926         }
16927             return null;
16928     },
16929
16930     /** @ignore */
16931     onClick : function(e){
16932         var item = this.findItemFromChild(e.getTarget());
16933         if(item){
16934             var index = this.indexOf(item);
16935             if(this.onItemClick(item, index, e) !== false){
16936                 this.fireEvent("click", this, index, item, e);
16937             }
16938         }else{
16939             this.clearSelections();
16940         }
16941     },
16942
16943     /** @ignore */
16944     onContextMenu : function(e){
16945         var item = this.findItemFromChild(e.getTarget());
16946         if(item){
16947             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16948         }
16949     },
16950
16951     /** @ignore */
16952     onDblClick : function(e){
16953         var item = this.findItemFromChild(e.getTarget());
16954         if(item){
16955             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16956         }
16957     },
16958
16959     onItemClick : function(item, index, e)
16960     {
16961         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16962             return false;
16963         }
16964         if (this.toggleSelect) {
16965             var m = this.isSelected(item) ? 'unselect' : 'select';
16966             //Roo.log(m);
16967             var _t = this;
16968             _t[m](item, true, false);
16969             return true;
16970         }
16971         if(this.multiSelect || this.singleSelect){
16972             if(this.multiSelect && e.shiftKey && this.lastSelection){
16973                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16974             }else{
16975                 this.select(item, this.multiSelect && e.ctrlKey);
16976                 this.lastSelection = item;
16977             }
16978             
16979             if(!this.tickable){
16980                 e.preventDefault();
16981             }
16982             
16983         }
16984         return true;
16985     },
16986
16987     /**
16988      * Get the number of selected nodes.
16989      * @return {Number}
16990      */
16991     getSelectionCount : function(){
16992         return this.selections.length;
16993     },
16994
16995     /**
16996      * Get the currently selected nodes.
16997      * @return {Array} An array of HTMLElements
16998      */
16999     getSelectedNodes : function(){
17000         return this.selections;
17001     },
17002
17003     /**
17004      * Get the indexes of the selected nodes.
17005      * @return {Array}
17006      */
17007     getSelectedIndexes : function(){
17008         var indexes = [], s = this.selections;
17009         for(var i = 0, len = s.length; i < len; i++){
17010             indexes.push(s[i].nodeIndex);
17011         }
17012         return indexes;
17013     },
17014
17015     /**
17016      * Clear all selections
17017      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
17018      */
17019     clearSelections : function(suppressEvent){
17020         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
17021             this.cmp.elements = this.selections;
17022             this.cmp.removeClass(this.selectedClass);
17023             this.selections = [];
17024             if(!suppressEvent){
17025                 this.fireEvent("selectionchange", this, this.selections);
17026             }
17027         }
17028     },
17029
17030     /**
17031      * Returns true if the passed node is selected
17032      * @param {HTMLElement/Number} node The node or node index
17033      * @return {Boolean}
17034      */
17035     isSelected : function(node){
17036         var s = this.selections;
17037         if(s.length < 1){
17038             return false;
17039         }
17040         node = this.getNode(node);
17041         return s.indexOf(node) !== -1;
17042     },
17043
17044     /**
17045      * Selects nodes.
17046      * @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
17047      * @param {Boolean} keepExisting (optional) true to keep existing selections
17048      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17049      */
17050     select : function(nodeInfo, keepExisting, suppressEvent){
17051         if(nodeInfo instanceof Array){
17052             if(!keepExisting){
17053                 this.clearSelections(true);
17054             }
17055             for(var i = 0, len = nodeInfo.length; i < len; i++){
17056                 this.select(nodeInfo[i], true, true);
17057             }
17058             return;
17059         } 
17060         var node = this.getNode(nodeInfo);
17061         if(!node || this.isSelected(node)){
17062             return; // already selected.
17063         }
17064         if(!keepExisting){
17065             this.clearSelections(true);
17066         }
17067         
17068         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
17069             Roo.fly(node).addClass(this.selectedClass);
17070             this.selections.push(node);
17071             if(!suppressEvent){
17072                 this.fireEvent("selectionchange", this, this.selections);
17073             }
17074         }
17075         
17076         
17077     },
17078       /**
17079      * Unselects nodes.
17080      * @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
17081      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
17082      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17083      */
17084     unselect : function(nodeInfo, keepExisting, suppressEvent)
17085     {
17086         if(nodeInfo instanceof Array){
17087             Roo.each(this.selections, function(s) {
17088                 this.unselect(s, nodeInfo);
17089             }, this);
17090             return;
17091         }
17092         var node = this.getNode(nodeInfo);
17093         if(!node || !this.isSelected(node)){
17094             //Roo.log("not selected");
17095             return; // not selected.
17096         }
17097         // fireevent???
17098         var ns = [];
17099         Roo.each(this.selections, function(s) {
17100             if (s == node ) {
17101                 Roo.fly(node).removeClass(this.selectedClass);
17102
17103                 return;
17104             }
17105             ns.push(s);
17106         },this);
17107         
17108         this.selections= ns;
17109         this.fireEvent("selectionchange", this, this.selections);
17110     },
17111
17112     /**
17113      * Gets a template node.
17114      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17115      * @return {HTMLElement} The node or null if it wasn't found
17116      */
17117     getNode : function(nodeInfo){
17118         if(typeof nodeInfo == "string"){
17119             return document.getElementById(nodeInfo);
17120         }else if(typeof nodeInfo == "number"){
17121             return this.nodes[nodeInfo];
17122         }
17123         return nodeInfo;
17124     },
17125
17126     /**
17127      * Gets a range template nodes.
17128      * @param {Number} startIndex
17129      * @param {Number} endIndex
17130      * @return {Array} An array of nodes
17131      */
17132     getNodes : function(start, end){
17133         var ns = this.nodes;
17134         start = start || 0;
17135         end = typeof end == "undefined" ? ns.length - 1 : end;
17136         var nodes = [];
17137         if(start <= end){
17138             for(var i = start; i <= end; i++){
17139                 nodes.push(ns[i]);
17140             }
17141         } else{
17142             for(var i = start; i >= end; i--){
17143                 nodes.push(ns[i]);
17144             }
17145         }
17146         return nodes;
17147     },
17148
17149     /**
17150      * Finds the index of the passed node
17151      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17152      * @return {Number} The index of the node or -1
17153      */
17154     indexOf : function(node){
17155         node = this.getNode(node);
17156         if(typeof node.nodeIndex == "number"){
17157             return node.nodeIndex;
17158         }
17159         var ns = this.nodes;
17160         for(var i = 0, len = ns.length; i < len; i++){
17161             if(ns[i] == node){
17162                 return i;
17163             }
17164         }
17165         return -1;
17166     }
17167 });
17168 /*
17169  * - LGPL
17170  *
17171  * based on jquery fullcalendar
17172  * 
17173  */
17174
17175 Roo.bootstrap = Roo.bootstrap || {};
17176 /**
17177  * @class Roo.bootstrap.Calendar
17178  * @extends Roo.bootstrap.Component
17179  * Bootstrap Calendar class
17180  * @cfg {Boolean} loadMask (true|false) default false
17181  * @cfg {Object} header generate the user specific header of the calendar, default false
17182
17183  * @constructor
17184  * Create a new Container
17185  * @param {Object} config The config object
17186  */
17187
17188
17189
17190 Roo.bootstrap.Calendar = function(config){
17191     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
17192      this.addEvents({
17193         /**
17194              * @event select
17195              * Fires when a date is selected
17196              * @param {DatePicker} this
17197              * @param {Date} date The selected date
17198              */
17199         'select': true,
17200         /**
17201              * @event monthchange
17202              * Fires when the displayed month changes 
17203              * @param {DatePicker} this
17204              * @param {Date} date The selected month
17205              */
17206         'monthchange': true,
17207         /**
17208              * @event evententer
17209              * Fires when mouse over an event
17210              * @param {Calendar} this
17211              * @param {event} Event
17212              */
17213         'evententer': true,
17214         /**
17215              * @event eventleave
17216              * Fires when the mouse leaves an
17217              * @param {Calendar} this
17218              * @param {event}
17219              */
17220         'eventleave': true,
17221         /**
17222              * @event eventclick
17223              * Fires when the mouse click an
17224              * @param {Calendar} this
17225              * @param {event}
17226              */
17227         'eventclick': true
17228         
17229     });
17230
17231 };
17232
17233 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
17234     
17235      /**
17236      * @cfg {Number} startDay
17237      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
17238      */
17239     startDay : 0,
17240     
17241     loadMask : false,
17242     
17243     header : false,
17244       
17245     getAutoCreate : function(){
17246         
17247         
17248         var fc_button = function(name, corner, style, content ) {
17249             return Roo.apply({},{
17250                 tag : 'span',
17251                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
17252                          (corner.length ?
17253                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
17254                             ''
17255                         ),
17256                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
17257                 unselectable: 'on'
17258             });
17259         };
17260         
17261         var header = {};
17262         
17263         if(!this.header){
17264             header = {
17265                 tag : 'table',
17266                 cls : 'fc-header',
17267                 style : 'width:100%',
17268                 cn : [
17269                     {
17270                         tag: 'tr',
17271                         cn : [
17272                             {
17273                                 tag : 'td',
17274                                 cls : 'fc-header-left',
17275                                 cn : [
17276                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
17277                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
17278                                     { tag: 'span', cls: 'fc-header-space' },
17279                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
17280
17281
17282                                 ]
17283                             },
17284
17285                             {
17286                                 tag : 'td',
17287                                 cls : 'fc-header-center',
17288                                 cn : [
17289                                     {
17290                                         tag: 'span',
17291                                         cls: 'fc-header-title',
17292                                         cn : {
17293                                             tag: 'H2',
17294                                             html : 'month / year'
17295                                         }
17296                                     }
17297
17298                                 ]
17299                             },
17300                             {
17301                                 tag : 'td',
17302                                 cls : 'fc-header-right',
17303                                 cn : [
17304                               /*      fc_button('month', 'left', '', 'month' ),
17305                                     fc_button('week', '', '', 'week' ),
17306                                     fc_button('day', 'right', '', 'day' )
17307                                 */    
17308
17309                                 ]
17310                             }
17311
17312                         ]
17313                     }
17314                 ]
17315             };
17316         }
17317         
17318         header = this.header;
17319         
17320        
17321         var cal_heads = function() {
17322             var ret = [];
17323             // fixme - handle this.
17324             
17325             for (var i =0; i < Date.dayNames.length; i++) {
17326                 var d = Date.dayNames[i];
17327                 ret.push({
17328                     tag: 'th',
17329                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17330                     html : d.substring(0,3)
17331                 });
17332                 
17333             }
17334             ret[0].cls += ' fc-first';
17335             ret[6].cls += ' fc-last';
17336             return ret;
17337         };
17338         var cal_cell = function(n) {
17339             return  {
17340                 tag: 'td',
17341                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17342                 cn : [
17343                     {
17344                         cn : [
17345                             {
17346                                 cls: 'fc-day-number',
17347                                 html: 'D'
17348                             },
17349                             {
17350                                 cls: 'fc-day-content',
17351                              
17352                                 cn : [
17353                                      {
17354                                         style: 'position: relative;' // height: 17px;
17355                                     }
17356                                 ]
17357                             }
17358                             
17359                             
17360                         ]
17361                     }
17362                 ]
17363                 
17364             }
17365         };
17366         var cal_rows = function() {
17367             
17368             var ret = [];
17369             for (var r = 0; r < 6; r++) {
17370                 var row= {
17371                     tag : 'tr',
17372                     cls : 'fc-week',
17373                     cn : []
17374                 };
17375                 
17376                 for (var i =0; i < Date.dayNames.length; i++) {
17377                     var d = Date.dayNames[i];
17378                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17379
17380                 }
17381                 row.cn[0].cls+=' fc-first';
17382                 row.cn[0].cn[0].style = 'min-height:90px';
17383                 row.cn[6].cls+=' fc-last';
17384                 ret.push(row);
17385                 
17386             }
17387             ret[0].cls += ' fc-first';
17388             ret[4].cls += ' fc-prev-last';
17389             ret[5].cls += ' fc-last';
17390             return ret;
17391             
17392         };
17393         
17394         var cal_table = {
17395             tag: 'table',
17396             cls: 'fc-border-separate',
17397             style : 'width:100%',
17398             cellspacing  : 0,
17399             cn : [
17400                 { 
17401                     tag: 'thead',
17402                     cn : [
17403                         { 
17404                             tag: 'tr',
17405                             cls : 'fc-first fc-last',
17406                             cn : cal_heads()
17407                         }
17408                     ]
17409                 },
17410                 { 
17411                     tag: 'tbody',
17412                     cn : cal_rows()
17413                 }
17414                   
17415             ]
17416         };
17417          
17418          var cfg = {
17419             cls : 'fc fc-ltr',
17420             cn : [
17421                 header,
17422                 {
17423                     cls : 'fc-content',
17424                     style : "position: relative;",
17425                     cn : [
17426                         {
17427                             cls : 'fc-view fc-view-month fc-grid',
17428                             style : 'position: relative',
17429                             unselectable : 'on',
17430                             cn : [
17431                                 {
17432                                     cls : 'fc-event-container',
17433                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17434                                 },
17435                                 cal_table
17436                             ]
17437                         }
17438                     ]
17439     
17440                 }
17441            ] 
17442             
17443         };
17444         
17445          
17446         
17447         return cfg;
17448     },
17449     
17450     
17451     initEvents : function()
17452     {
17453         if(!this.store){
17454             throw "can not find store for calendar";
17455         }
17456         
17457         var mark = {
17458             tag: "div",
17459             cls:"x-dlg-mask",
17460             style: "text-align:center",
17461             cn: [
17462                 {
17463                     tag: "div",
17464                     style: "background-color:white;width:50%;margin:250 auto",
17465                     cn: [
17466                         {
17467                             tag: "img",
17468                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17469                         },
17470                         {
17471                             tag: "span",
17472                             html: "Loading"
17473                         }
17474                         
17475                     ]
17476                 }
17477             ]
17478         };
17479         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17480         
17481         var size = this.el.select('.fc-content', true).first().getSize();
17482         this.maskEl.setSize(size.width, size.height);
17483         this.maskEl.enableDisplayMode("block");
17484         if(!this.loadMask){
17485             this.maskEl.hide();
17486         }
17487         
17488         this.store = Roo.factory(this.store, Roo.data);
17489         this.store.on('load', this.onLoad, this);
17490         this.store.on('beforeload', this.onBeforeLoad, this);
17491         
17492         this.resize();
17493         
17494         this.cells = this.el.select('.fc-day',true);
17495         //Roo.log(this.cells);
17496         this.textNodes = this.el.query('.fc-day-number');
17497         this.cells.addClassOnOver('fc-state-hover');
17498         
17499         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17500         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17501         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17502         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17503         
17504         this.on('monthchange', this.onMonthChange, this);
17505         
17506         this.update(new Date().clearTime());
17507     },
17508     
17509     resize : function() {
17510         var sz  = this.el.getSize();
17511         
17512         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17513         this.el.select('.fc-day-content div',true).setHeight(34);
17514     },
17515     
17516     
17517     // private
17518     showPrevMonth : function(e){
17519         this.update(this.activeDate.add("mo", -1));
17520     },
17521     showToday : function(e){
17522         this.update(new Date().clearTime());
17523     },
17524     // private
17525     showNextMonth : function(e){
17526         this.update(this.activeDate.add("mo", 1));
17527     },
17528
17529     // private
17530     showPrevYear : function(){
17531         this.update(this.activeDate.add("y", -1));
17532     },
17533
17534     // private
17535     showNextYear : function(){
17536         this.update(this.activeDate.add("y", 1));
17537     },
17538
17539     
17540    // private
17541     update : function(date)
17542     {
17543         var vd = this.activeDate;
17544         this.activeDate = date;
17545 //        if(vd && this.el){
17546 //            var t = date.getTime();
17547 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17548 //                Roo.log('using add remove');
17549 //                
17550 //                this.fireEvent('monthchange', this, date);
17551 //                
17552 //                this.cells.removeClass("fc-state-highlight");
17553 //                this.cells.each(function(c){
17554 //                   if(c.dateValue == t){
17555 //                       c.addClass("fc-state-highlight");
17556 //                       setTimeout(function(){
17557 //                            try{c.dom.firstChild.focus();}catch(e){}
17558 //                       }, 50);
17559 //                       return false;
17560 //                   }
17561 //                   return true;
17562 //                });
17563 //                return;
17564 //            }
17565 //        }
17566         
17567         var days = date.getDaysInMonth();
17568         
17569         var firstOfMonth = date.getFirstDateOfMonth();
17570         var startingPos = firstOfMonth.getDay()-this.startDay;
17571         
17572         if(startingPos < this.startDay){
17573             startingPos += 7;
17574         }
17575         
17576         var pm = date.add(Date.MONTH, -1);
17577         var prevStart = pm.getDaysInMonth()-startingPos;
17578 //        
17579         this.cells = this.el.select('.fc-day',true);
17580         this.textNodes = this.el.query('.fc-day-number');
17581         this.cells.addClassOnOver('fc-state-hover');
17582         
17583         var cells = this.cells.elements;
17584         var textEls = this.textNodes;
17585         
17586         Roo.each(cells, function(cell){
17587             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17588         });
17589         
17590         days += startingPos;
17591
17592         // convert everything to numbers so it's fast
17593         var day = 86400000;
17594         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17595         //Roo.log(d);
17596         //Roo.log(pm);
17597         //Roo.log(prevStart);
17598         
17599         var today = new Date().clearTime().getTime();
17600         var sel = date.clearTime().getTime();
17601         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17602         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17603         var ddMatch = this.disabledDatesRE;
17604         var ddText = this.disabledDatesText;
17605         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17606         var ddaysText = this.disabledDaysText;
17607         var format = this.format;
17608         
17609         var setCellClass = function(cal, cell){
17610             cell.row = 0;
17611             cell.events = [];
17612             cell.more = [];
17613             //Roo.log('set Cell Class');
17614             cell.title = "";
17615             var t = d.getTime();
17616             
17617             //Roo.log(d);
17618             
17619             cell.dateValue = t;
17620             if(t == today){
17621                 cell.className += " fc-today";
17622                 cell.className += " fc-state-highlight";
17623                 cell.title = cal.todayText;
17624             }
17625             if(t == sel){
17626                 // disable highlight in other month..
17627                 //cell.className += " fc-state-highlight";
17628                 
17629             }
17630             // disabling
17631             if(t < min) {
17632                 cell.className = " fc-state-disabled";
17633                 cell.title = cal.minText;
17634                 return;
17635             }
17636             if(t > max) {
17637                 cell.className = " fc-state-disabled";
17638                 cell.title = cal.maxText;
17639                 return;
17640             }
17641             if(ddays){
17642                 if(ddays.indexOf(d.getDay()) != -1){
17643                     cell.title = ddaysText;
17644                     cell.className = " fc-state-disabled";
17645                 }
17646             }
17647             if(ddMatch && format){
17648                 var fvalue = d.dateFormat(format);
17649                 if(ddMatch.test(fvalue)){
17650                     cell.title = ddText.replace("%0", fvalue);
17651                     cell.className = " fc-state-disabled";
17652                 }
17653             }
17654             
17655             if (!cell.initialClassName) {
17656                 cell.initialClassName = cell.dom.className;
17657             }
17658             
17659             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17660         };
17661
17662         var i = 0;
17663         
17664         for(; i < startingPos; i++) {
17665             textEls[i].innerHTML = (++prevStart);
17666             d.setDate(d.getDate()+1);
17667             
17668             cells[i].className = "fc-past fc-other-month";
17669             setCellClass(this, cells[i]);
17670         }
17671         
17672         var intDay = 0;
17673         
17674         for(; i < days; i++){
17675             intDay = i - startingPos + 1;
17676             textEls[i].innerHTML = (intDay);
17677             d.setDate(d.getDate()+1);
17678             
17679             cells[i].className = ''; // "x-date-active";
17680             setCellClass(this, cells[i]);
17681         }
17682         var extraDays = 0;
17683         
17684         for(; i < 42; i++) {
17685             textEls[i].innerHTML = (++extraDays);
17686             d.setDate(d.getDate()+1);
17687             
17688             cells[i].className = "fc-future fc-other-month";
17689             setCellClass(this, cells[i]);
17690         }
17691         
17692         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17693         
17694         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17695         
17696         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17697         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17698         
17699         if(totalRows != 6){
17700             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17701             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17702         }
17703         
17704         this.fireEvent('monthchange', this, date);
17705         
17706         
17707         /*
17708         if(!this.internalRender){
17709             var main = this.el.dom.firstChild;
17710             var w = main.offsetWidth;
17711             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17712             Roo.fly(main).setWidth(w);
17713             this.internalRender = true;
17714             // opera does not respect the auto grow header center column
17715             // then, after it gets a width opera refuses to recalculate
17716             // without a second pass
17717             if(Roo.isOpera && !this.secondPass){
17718                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17719                 this.secondPass = true;
17720                 this.update.defer(10, this, [date]);
17721             }
17722         }
17723         */
17724         
17725     },
17726     
17727     findCell : function(dt) {
17728         dt = dt.clearTime().getTime();
17729         var ret = false;
17730         this.cells.each(function(c){
17731             //Roo.log("check " +c.dateValue + '?=' + dt);
17732             if(c.dateValue == dt){
17733                 ret = c;
17734                 return false;
17735             }
17736             return true;
17737         });
17738         
17739         return ret;
17740     },
17741     
17742     findCells : function(ev) {
17743         var s = ev.start.clone().clearTime().getTime();
17744        // Roo.log(s);
17745         var e= ev.end.clone().clearTime().getTime();
17746        // Roo.log(e);
17747         var ret = [];
17748         this.cells.each(function(c){
17749              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17750             
17751             if(c.dateValue > e){
17752                 return ;
17753             }
17754             if(c.dateValue < s){
17755                 return ;
17756             }
17757             ret.push(c);
17758         });
17759         
17760         return ret;    
17761     },
17762     
17763 //    findBestRow: function(cells)
17764 //    {
17765 //        var ret = 0;
17766 //        
17767 //        for (var i =0 ; i < cells.length;i++) {
17768 //            ret  = Math.max(cells[i].rows || 0,ret);
17769 //        }
17770 //        return ret;
17771 //        
17772 //    },
17773     
17774     
17775     addItem : function(ev)
17776     {
17777         // look for vertical location slot in
17778         var cells = this.findCells(ev);
17779         
17780 //        ev.row = this.findBestRow(cells);
17781         
17782         // work out the location.
17783         
17784         var crow = false;
17785         var rows = [];
17786         for(var i =0; i < cells.length; i++) {
17787             
17788             cells[i].row = cells[0].row;
17789             
17790             if(i == 0){
17791                 cells[i].row = cells[i].row + 1;
17792             }
17793             
17794             if (!crow) {
17795                 crow = {
17796                     start : cells[i],
17797                     end :  cells[i]
17798                 };
17799                 continue;
17800             }
17801             if (crow.start.getY() == cells[i].getY()) {
17802                 // on same row.
17803                 crow.end = cells[i];
17804                 continue;
17805             }
17806             // different row.
17807             rows.push(crow);
17808             crow = {
17809                 start: cells[i],
17810                 end : cells[i]
17811             };
17812             
17813         }
17814         
17815         rows.push(crow);
17816         ev.els = [];
17817         ev.rows = rows;
17818         ev.cells = cells;
17819         
17820         cells[0].events.push(ev);
17821         
17822         this.calevents.push(ev);
17823     },
17824     
17825     clearEvents: function() {
17826         
17827         if(!this.calevents){
17828             return;
17829         }
17830         
17831         Roo.each(this.cells.elements, function(c){
17832             c.row = 0;
17833             c.events = [];
17834             c.more = [];
17835         });
17836         
17837         Roo.each(this.calevents, function(e) {
17838             Roo.each(e.els, function(el) {
17839                 el.un('mouseenter' ,this.onEventEnter, this);
17840                 el.un('mouseleave' ,this.onEventLeave, this);
17841                 el.remove();
17842             },this);
17843         },this);
17844         
17845         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17846             e.remove();
17847         });
17848         
17849     },
17850     
17851     renderEvents: function()
17852     {   
17853         var _this = this;
17854         
17855         this.cells.each(function(c) {
17856             
17857             if(c.row < 5){
17858                 return;
17859             }
17860             
17861             var ev = c.events;
17862             
17863             var r = 4;
17864             if(c.row != c.events.length){
17865                 r = 4 - (4 - (c.row - c.events.length));
17866             }
17867             
17868             c.events = ev.slice(0, r);
17869             c.more = ev.slice(r);
17870             
17871             if(c.more.length && c.more.length == 1){
17872                 c.events.push(c.more.pop());
17873             }
17874             
17875             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17876             
17877         });
17878             
17879         this.cells.each(function(c) {
17880             
17881             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17882             
17883             
17884             for (var e = 0; e < c.events.length; e++){
17885                 var ev = c.events[e];
17886                 var rows = ev.rows;
17887                 
17888                 for(var i = 0; i < rows.length; i++) {
17889                 
17890                     // how many rows should it span..
17891
17892                     var  cfg = {
17893                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17894                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17895
17896                         unselectable : "on",
17897                         cn : [
17898                             {
17899                                 cls: 'fc-event-inner',
17900                                 cn : [
17901     //                                {
17902     //                                  tag:'span',
17903     //                                  cls: 'fc-event-time',
17904     //                                  html : cells.length > 1 ? '' : ev.time
17905     //                                },
17906                                     {
17907                                       tag:'span',
17908                                       cls: 'fc-event-title',
17909                                       html : String.format('{0}', ev.title)
17910                                     }
17911
17912
17913                                 ]
17914                             },
17915                             {
17916                                 cls: 'ui-resizable-handle ui-resizable-e',
17917                                 html : '&nbsp;&nbsp;&nbsp'
17918                             }
17919
17920                         ]
17921                     };
17922
17923                     if (i == 0) {
17924                         cfg.cls += ' fc-event-start';
17925                     }
17926                     if ((i+1) == rows.length) {
17927                         cfg.cls += ' fc-event-end';
17928                     }
17929
17930                     var ctr = _this.el.select('.fc-event-container',true).first();
17931                     var cg = ctr.createChild(cfg);
17932
17933                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17934                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17935
17936                     var r = (c.more.length) ? 1 : 0;
17937                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17938                     cg.setWidth(ebox.right - sbox.x -2);
17939
17940                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17941                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17942                     cg.on('click', _this.onEventClick, _this, ev);
17943
17944                     ev.els.push(cg);
17945                     
17946                 }
17947                 
17948             }
17949             
17950             
17951             if(c.more.length){
17952                 var  cfg = {
17953                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17954                     style : 'position: absolute',
17955                     unselectable : "on",
17956                     cn : [
17957                         {
17958                             cls: 'fc-event-inner',
17959                             cn : [
17960                                 {
17961                                   tag:'span',
17962                                   cls: 'fc-event-title',
17963                                   html : 'More'
17964                                 }
17965
17966
17967                             ]
17968                         },
17969                         {
17970                             cls: 'ui-resizable-handle ui-resizable-e',
17971                             html : '&nbsp;&nbsp;&nbsp'
17972                         }
17973
17974                     ]
17975                 };
17976
17977                 var ctr = _this.el.select('.fc-event-container',true).first();
17978                 var cg = ctr.createChild(cfg);
17979
17980                 var sbox = c.select('.fc-day-content',true).first().getBox();
17981                 var ebox = c.select('.fc-day-content',true).first().getBox();
17982                 //Roo.log(cg);
17983                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17984                 cg.setWidth(ebox.right - sbox.x -2);
17985
17986                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17987                 
17988             }
17989             
17990         });
17991         
17992         
17993         
17994     },
17995     
17996     onEventEnter: function (e, el,event,d) {
17997         this.fireEvent('evententer', this, el, event);
17998     },
17999     
18000     onEventLeave: function (e, el,event,d) {
18001         this.fireEvent('eventleave', this, el, event);
18002     },
18003     
18004     onEventClick: function (e, el,event,d) {
18005         this.fireEvent('eventclick', this, el, event);
18006     },
18007     
18008     onMonthChange: function () {
18009         this.store.load();
18010     },
18011     
18012     onMoreEventClick: function(e, el, more)
18013     {
18014         var _this = this;
18015         
18016         this.calpopover.placement = 'right';
18017         this.calpopover.setTitle('More');
18018         
18019         this.calpopover.setContent('');
18020         
18021         var ctr = this.calpopover.el.select('.popover-content', true).first();
18022         
18023         Roo.each(more, function(m){
18024             var cfg = {
18025                 cls : 'fc-event-hori fc-event-draggable',
18026                 html : m.title
18027             };
18028             var cg = ctr.createChild(cfg);
18029             
18030             cg.on('click', _this.onEventClick, _this, m);
18031         });
18032         
18033         this.calpopover.show(el);
18034         
18035         
18036     },
18037     
18038     onLoad: function () 
18039     {   
18040         this.calevents = [];
18041         var cal = this;
18042         
18043         if(this.store.getCount() > 0){
18044             this.store.data.each(function(d){
18045                cal.addItem({
18046                     id : d.data.id,
18047                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
18048                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
18049                     time : d.data.start_time,
18050                     title : d.data.title,
18051                     description : d.data.description,
18052                     venue : d.data.venue
18053                 });
18054             });
18055         }
18056         
18057         this.renderEvents();
18058         
18059         if(this.calevents.length && this.loadMask){
18060             this.maskEl.hide();
18061         }
18062     },
18063     
18064     onBeforeLoad: function()
18065     {
18066         this.clearEvents();
18067         if(this.loadMask){
18068             this.maskEl.show();
18069         }
18070     }
18071 });
18072
18073  
18074  /*
18075  * - LGPL
18076  *
18077  * element
18078  * 
18079  */
18080
18081 /**
18082  * @class Roo.bootstrap.Popover
18083  * @extends Roo.bootstrap.Component
18084  * Bootstrap Popover class
18085  * @cfg {String} html contents of the popover   (or false to use children..)
18086  * @cfg {String} title of popover (or false to hide)
18087  * @cfg {String} placement how it is placed
18088  * @cfg {String} trigger click || hover (or false to trigger manually)
18089  * @cfg {String} over what (parent or false to trigger manually.)
18090  * @cfg {Number} delay - delay before showing
18091  
18092  * @constructor
18093  * Create a new Popover
18094  * @param {Object} config The config object
18095  */
18096
18097 Roo.bootstrap.Popover = function(config){
18098     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
18099     
18100     this.addEvents({
18101         // raw events
18102          /**
18103          * @event show
18104          * After the popover show
18105          * 
18106          * @param {Roo.bootstrap.Popover} this
18107          */
18108         "show" : true,
18109         /**
18110          * @event hide
18111          * After the popover hide
18112          * 
18113          * @param {Roo.bootstrap.Popover} this
18114          */
18115         "hide" : true
18116     });
18117 };
18118
18119 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
18120     
18121     title: 'Fill in a title',
18122     html: false,
18123     
18124     placement : 'right',
18125     trigger : 'hover', // hover
18126     
18127     delay : 0,
18128     
18129     over: 'parent',
18130     
18131     can_build_overlaid : false,
18132     
18133     getChildContainer : function()
18134     {
18135         return this.el.select('.popover-content',true).first();
18136     },
18137     
18138     getAutoCreate : function(){
18139          
18140         var cfg = {
18141            cls : 'popover roo-dynamic',
18142            style: 'display:block',
18143            cn : [
18144                 {
18145                     cls : 'arrow'
18146                 },
18147                 {
18148                     cls : 'popover-inner',
18149                     cn : [
18150                         {
18151                             tag: 'h3',
18152                             cls: 'popover-title popover-header',
18153                             html : this.title
18154                         },
18155                         {
18156                             cls : 'popover-content popover-body',
18157                             html : this.html
18158                         }
18159                     ]
18160                     
18161                 }
18162            ]
18163         };
18164         
18165         return cfg;
18166     },
18167     setTitle: function(str)
18168     {
18169         this.title = str;
18170         this.el.select('.popover-title',true).first().dom.innerHTML = str;
18171     },
18172     setContent: function(str)
18173     {
18174         this.html = str;
18175         this.el.select('.popover-content',true).first().dom.innerHTML = str;
18176     },
18177     // as it get's added to the bottom of the page.
18178     onRender : function(ct, position)
18179     {
18180         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
18181         if(!this.el){
18182             var cfg = Roo.apply({},  this.getAutoCreate());
18183             cfg.id = Roo.id();
18184             
18185             if (this.cls) {
18186                 cfg.cls += ' ' + this.cls;
18187             }
18188             if (this.style) {
18189                 cfg.style = this.style;
18190             }
18191             //Roo.log("adding to ");
18192             this.el = Roo.get(document.body).createChild(cfg, position);
18193 //            Roo.log(this.el);
18194         }
18195         this.initEvents();
18196     },
18197     
18198     initEvents : function()
18199     {
18200         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
18201         this.el.enableDisplayMode('block');
18202         this.el.hide();
18203         if (this.over === false) {
18204             return; 
18205         }
18206         if (this.triggers === false) {
18207             return;
18208         }
18209         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18210         var triggers = this.trigger ? this.trigger.split(' ') : [];
18211         Roo.each(triggers, function(trigger) {
18212         
18213             if (trigger == 'click') {
18214                 on_el.on('click', this.toggle, this);
18215             } else if (trigger != 'manual') {
18216                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
18217                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
18218       
18219                 on_el.on(eventIn  ,this.enter, this);
18220                 on_el.on(eventOut, this.leave, this);
18221             }
18222         }, this);
18223         
18224     },
18225     
18226     
18227     // private
18228     timeout : null,
18229     hoverState : null,
18230     
18231     toggle : function () {
18232         this.hoverState == 'in' ? this.leave() : this.enter();
18233     },
18234     
18235     enter : function () {
18236         
18237         clearTimeout(this.timeout);
18238     
18239         this.hoverState = 'in';
18240     
18241         if (!this.delay || !this.delay.show) {
18242             this.show();
18243             return;
18244         }
18245         var _t = this;
18246         this.timeout = setTimeout(function () {
18247             if (_t.hoverState == 'in') {
18248                 _t.show();
18249             }
18250         }, this.delay.show)
18251     },
18252     
18253     leave : function() {
18254         clearTimeout(this.timeout);
18255     
18256         this.hoverState = 'out';
18257     
18258         if (!this.delay || !this.delay.hide) {
18259             this.hide();
18260             return;
18261         }
18262         var _t = this;
18263         this.timeout = setTimeout(function () {
18264             if (_t.hoverState == 'out') {
18265                 _t.hide();
18266             }
18267         }, this.delay.hide)
18268     },
18269     
18270     show : function (on_el)
18271     {
18272         if (!on_el) {
18273             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18274         }
18275         
18276         // set content.
18277         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
18278         if (this.html !== false) {
18279             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18280         }
18281         this.el.removeClass([
18282             'fade','top','bottom', 'left', 'right','in',
18283             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18284         ]);
18285         if (!this.title.length) {
18286             this.el.select('.popover-title',true).hide();
18287         }
18288         
18289         var placement = typeof this.placement == 'function' ?
18290             this.placement.call(this, this.el, on_el) :
18291             this.placement;
18292             
18293         var autoToken = /\s?auto?\s?/i;
18294         var autoPlace = autoToken.test(placement);
18295         if (autoPlace) {
18296             placement = placement.replace(autoToken, '') || 'top';
18297         }
18298         
18299         //this.el.detach()
18300         //this.el.setXY([0,0]);
18301         this.el.show();
18302         this.el.dom.style.display='block';
18303         this.el.addClass(placement);
18304         
18305         //this.el.appendTo(on_el);
18306         
18307         var p = this.getPosition();
18308         var box = this.el.getBox();
18309         
18310         if (autoPlace) {
18311             // fixme..
18312         }
18313         var align = Roo.bootstrap.Popover.alignment[placement];
18314         
18315 //        Roo.log(align);
18316         this.el.alignTo(on_el, align[0],align[1]);
18317         //var arrow = this.el.select('.arrow',true).first();
18318         //arrow.set(align[2], 
18319         
18320         this.el.addClass('in');
18321         
18322         
18323         if (this.el.hasClass('fade')) {
18324             // fade it?
18325         }
18326         
18327         this.hoverState = 'in';
18328         
18329         this.fireEvent('show', this);
18330         
18331     },
18332     hide : function()
18333     {
18334         this.el.setXY([0,0]);
18335         this.el.removeClass('in');
18336         this.el.hide();
18337         this.hoverState = null;
18338         
18339         this.fireEvent('hide', this);
18340     }
18341     
18342 });
18343
18344 Roo.bootstrap.Popover.alignment = {
18345     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18346     'right' : ['l-r', [10,0], 'left bs-popover-left'],
18347     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18348     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18349 };
18350
18351  /*
18352  * - LGPL
18353  *
18354  * Progress
18355  * 
18356  */
18357
18358 /**
18359  * @class Roo.bootstrap.Progress
18360  * @extends Roo.bootstrap.Component
18361  * Bootstrap Progress class
18362  * @cfg {Boolean} striped striped of the progress bar
18363  * @cfg {Boolean} active animated of the progress bar
18364  * 
18365  * 
18366  * @constructor
18367  * Create a new Progress
18368  * @param {Object} config The config object
18369  */
18370
18371 Roo.bootstrap.Progress = function(config){
18372     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18373 };
18374
18375 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
18376     
18377     striped : false,
18378     active: false,
18379     
18380     getAutoCreate : function(){
18381         var cfg = {
18382             tag: 'div',
18383             cls: 'progress'
18384         };
18385         
18386         
18387         if(this.striped){
18388             cfg.cls += ' progress-striped';
18389         }
18390       
18391         if(this.active){
18392             cfg.cls += ' active';
18393         }
18394         
18395         
18396         return cfg;
18397     }
18398    
18399 });
18400
18401  
18402
18403  /*
18404  * - LGPL
18405  *
18406  * ProgressBar
18407  * 
18408  */
18409
18410 /**
18411  * @class Roo.bootstrap.ProgressBar
18412  * @extends Roo.bootstrap.Component
18413  * Bootstrap ProgressBar class
18414  * @cfg {Number} aria_valuenow aria-value now
18415  * @cfg {Number} aria_valuemin aria-value min
18416  * @cfg {Number} aria_valuemax aria-value max
18417  * @cfg {String} label label for the progress bar
18418  * @cfg {String} panel (success | info | warning | danger )
18419  * @cfg {String} role role of the progress bar
18420  * @cfg {String} sr_only text
18421  * 
18422  * 
18423  * @constructor
18424  * Create a new ProgressBar
18425  * @param {Object} config The config object
18426  */
18427
18428 Roo.bootstrap.ProgressBar = function(config){
18429     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18430 };
18431
18432 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18433     
18434     aria_valuenow : 0,
18435     aria_valuemin : 0,
18436     aria_valuemax : 100,
18437     label : false,
18438     panel : false,
18439     role : false,
18440     sr_only: false,
18441     
18442     getAutoCreate : function()
18443     {
18444         
18445         var cfg = {
18446             tag: 'div',
18447             cls: 'progress-bar',
18448             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18449         };
18450         
18451         if(this.sr_only){
18452             cfg.cn = {
18453                 tag: 'span',
18454                 cls: 'sr-only',
18455                 html: this.sr_only
18456             }
18457         }
18458         
18459         if(this.role){
18460             cfg.role = this.role;
18461         }
18462         
18463         if(this.aria_valuenow){
18464             cfg['aria-valuenow'] = this.aria_valuenow;
18465         }
18466         
18467         if(this.aria_valuemin){
18468             cfg['aria-valuemin'] = this.aria_valuemin;
18469         }
18470         
18471         if(this.aria_valuemax){
18472             cfg['aria-valuemax'] = this.aria_valuemax;
18473         }
18474         
18475         if(this.label && !this.sr_only){
18476             cfg.html = this.label;
18477         }
18478         
18479         if(this.panel){
18480             cfg.cls += ' progress-bar-' + this.panel;
18481         }
18482         
18483         return cfg;
18484     },
18485     
18486     update : function(aria_valuenow)
18487     {
18488         this.aria_valuenow = aria_valuenow;
18489         
18490         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18491     }
18492    
18493 });
18494
18495  
18496
18497  /*
18498  * - LGPL
18499  *
18500  * column
18501  * 
18502  */
18503
18504 /**
18505  * @class Roo.bootstrap.TabGroup
18506  * @extends Roo.bootstrap.Column
18507  * Bootstrap Column class
18508  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18509  * @cfg {Boolean} carousel true to make the group behave like a carousel
18510  * @cfg {Boolean} bullets show bullets for the panels
18511  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18512  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18513  * @cfg {Boolean} showarrow (true|false) show arrow default true
18514  * 
18515  * @constructor
18516  * Create a new TabGroup
18517  * @param {Object} config The config object
18518  */
18519
18520 Roo.bootstrap.TabGroup = function(config){
18521     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18522     if (!this.navId) {
18523         this.navId = Roo.id();
18524     }
18525     this.tabs = [];
18526     Roo.bootstrap.TabGroup.register(this);
18527     
18528 };
18529
18530 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18531     
18532     carousel : false,
18533     transition : false,
18534     bullets : 0,
18535     timer : 0,
18536     autoslide : false,
18537     slideFn : false,
18538     slideOnTouch : false,
18539     showarrow : true,
18540     
18541     getAutoCreate : function()
18542     {
18543         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18544         
18545         cfg.cls += ' tab-content';
18546         
18547         if (this.carousel) {
18548             cfg.cls += ' carousel slide';
18549             
18550             cfg.cn = [{
18551                cls : 'carousel-inner',
18552                cn : []
18553             }];
18554         
18555             if(this.bullets  && !Roo.isTouch){
18556                 
18557                 var bullets = {
18558                     cls : 'carousel-bullets',
18559                     cn : []
18560                 };
18561                
18562                 if(this.bullets_cls){
18563                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18564                 }
18565                 
18566                 bullets.cn.push({
18567                     cls : 'clear'
18568                 });
18569                 
18570                 cfg.cn[0].cn.push(bullets);
18571             }
18572             
18573             if(this.showarrow){
18574                 cfg.cn[0].cn.push({
18575                     tag : 'div',
18576                     class : 'carousel-arrow',
18577                     cn : [
18578                         {
18579                             tag : 'div',
18580                             class : 'carousel-prev',
18581                             cn : [
18582                                 {
18583                                     tag : 'i',
18584                                     class : 'fa fa-chevron-left'
18585                                 }
18586                             ]
18587                         },
18588                         {
18589                             tag : 'div',
18590                             class : 'carousel-next',
18591                             cn : [
18592                                 {
18593                                     tag : 'i',
18594                                     class : 'fa fa-chevron-right'
18595                                 }
18596                             ]
18597                         }
18598                     ]
18599                 });
18600             }
18601             
18602         }
18603         
18604         return cfg;
18605     },
18606     
18607     initEvents:  function()
18608     {
18609 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18610 //            this.el.on("touchstart", this.onTouchStart, this);
18611 //        }
18612         
18613         if(this.autoslide){
18614             var _this = this;
18615             
18616             this.slideFn = window.setInterval(function() {
18617                 _this.showPanelNext();
18618             }, this.timer);
18619         }
18620         
18621         if(this.showarrow){
18622             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18623             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18624         }
18625         
18626         
18627     },
18628     
18629 //    onTouchStart : function(e, el, o)
18630 //    {
18631 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18632 //            return;
18633 //        }
18634 //        
18635 //        this.showPanelNext();
18636 //    },
18637     
18638     
18639     getChildContainer : function()
18640     {
18641         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18642     },
18643     
18644     /**
18645     * register a Navigation item
18646     * @param {Roo.bootstrap.NavItem} the navitem to add
18647     */
18648     register : function(item)
18649     {
18650         this.tabs.push( item);
18651         item.navId = this.navId; // not really needed..
18652         this.addBullet();
18653     
18654     },
18655     
18656     getActivePanel : function()
18657     {
18658         var r = false;
18659         Roo.each(this.tabs, function(t) {
18660             if (t.active) {
18661                 r = t;
18662                 return false;
18663             }
18664             return null;
18665         });
18666         return r;
18667         
18668     },
18669     getPanelByName : function(n)
18670     {
18671         var r = false;
18672         Roo.each(this.tabs, function(t) {
18673             if (t.tabId == n) {
18674                 r = t;
18675                 return false;
18676             }
18677             return null;
18678         });
18679         return r;
18680     },
18681     indexOfPanel : function(p)
18682     {
18683         var r = false;
18684         Roo.each(this.tabs, function(t,i) {
18685             if (t.tabId == p.tabId) {
18686                 r = i;
18687                 return false;
18688             }
18689             return null;
18690         });
18691         return r;
18692     },
18693     /**
18694      * show a specific panel
18695      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18696      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18697      */
18698     showPanel : function (pan)
18699     {
18700         if(this.transition || typeof(pan) == 'undefined'){
18701             Roo.log("waiting for the transitionend");
18702             return false;
18703         }
18704         
18705         if (typeof(pan) == 'number') {
18706             pan = this.tabs[pan];
18707         }
18708         
18709         if (typeof(pan) == 'string') {
18710             pan = this.getPanelByName(pan);
18711         }
18712         
18713         var cur = this.getActivePanel();
18714         
18715         if(!pan || !cur){
18716             Roo.log('pan or acitve pan is undefined');
18717             return false;
18718         }
18719         
18720         if (pan.tabId == this.getActivePanel().tabId) {
18721             return true;
18722         }
18723         
18724         if (false === cur.fireEvent('beforedeactivate')) {
18725             return false;
18726         }
18727         
18728         if(this.bullets > 0 && !Roo.isTouch){
18729             this.setActiveBullet(this.indexOfPanel(pan));
18730         }
18731         
18732         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18733             
18734             //class="carousel-item carousel-item-next carousel-item-left"
18735             
18736             this.transition = true;
18737             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18738             var lr = dir == 'next' ? 'left' : 'right';
18739             pan.el.addClass(dir); // or prev
18740             pan.el.addClass('carousel-item-' + dir); // or prev
18741             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18742             cur.el.addClass(lr); // or right
18743             pan.el.addClass(lr);
18744             cur.el.addClass('carousel-item-' +lr); // or right
18745             pan.el.addClass('carousel-item-' +lr);
18746             
18747             
18748             var _this = this;
18749             cur.el.on('transitionend', function() {
18750                 Roo.log("trans end?");
18751                 
18752                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18753                 pan.setActive(true);
18754                 
18755                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18756                 cur.setActive(false);
18757                 
18758                 _this.transition = false;
18759                 
18760             }, this, { single:  true } );
18761             
18762             return true;
18763         }
18764         
18765         cur.setActive(false);
18766         pan.setActive(true);
18767         
18768         return true;
18769         
18770     },
18771     showPanelNext : function()
18772     {
18773         var i = this.indexOfPanel(this.getActivePanel());
18774         
18775         if (i >= this.tabs.length - 1 && !this.autoslide) {
18776             return;
18777         }
18778         
18779         if (i >= this.tabs.length - 1 && this.autoslide) {
18780             i = -1;
18781         }
18782         
18783         this.showPanel(this.tabs[i+1]);
18784     },
18785     
18786     showPanelPrev : function()
18787     {
18788         var i = this.indexOfPanel(this.getActivePanel());
18789         
18790         if (i  < 1 && !this.autoslide) {
18791             return;
18792         }
18793         
18794         if (i < 1 && this.autoslide) {
18795             i = this.tabs.length;
18796         }
18797         
18798         this.showPanel(this.tabs[i-1]);
18799     },
18800     
18801     
18802     addBullet: function()
18803     {
18804         if(!this.bullets || Roo.isTouch){
18805             return;
18806         }
18807         var ctr = this.el.select('.carousel-bullets',true).first();
18808         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18809         var bullet = ctr.createChild({
18810             cls : 'bullet bullet-' + i
18811         },ctr.dom.lastChild);
18812         
18813         
18814         var _this = this;
18815         
18816         bullet.on('click', (function(e, el, o, ii, t){
18817
18818             e.preventDefault();
18819
18820             this.showPanel(ii);
18821
18822             if(this.autoslide && this.slideFn){
18823                 clearInterval(this.slideFn);
18824                 this.slideFn = window.setInterval(function() {
18825                     _this.showPanelNext();
18826                 }, this.timer);
18827             }
18828
18829         }).createDelegate(this, [i, bullet], true));
18830                 
18831         
18832     },
18833      
18834     setActiveBullet : function(i)
18835     {
18836         if(Roo.isTouch){
18837             return;
18838         }
18839         
18840         Roo.each(this.el.select('.bullet', true).elements, function(el){
18841             el.removeClass('selected');
18842         });
18843
18844         var bullet = this.el.select('.bullet-' + i, true).first();
18845         
18846         if(!bullet){
18847             return;
18848         }
18849         
18850         bullet.addClass('selected');
18851     }
18852     
18853     
18854   
18855 });
18856
18857  
18858
18859  
18860  
18861 Roo.apply(Roo.bootstrap.TabGroup, {
18862     
18863     groups: {},
18864      /**
18865     * register a Navigation Group
18866     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18867     */
18868     register : function(navgrp)
18869     {
18870         this.groups[navgrp.navId] = navgrp;
18871         
18872     },
18873     /**
18874     * fetch a Navigation Group based on the navigation ID
18875     * if one does not exist , it will get created.
18876     * @param {string} the navgroup to add
18877     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18878     */
18879     get: function(navId) {
18880         if (typeof(this.groups[navId]) == 'undefined') {
18881             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18882         }
18883         return this.groups[navId] ;
18884     }
18885     
18886     
18887     
18888 });
18889
18890  /*
18891  * - LGPL
18892  *
18893  * TabPanel
18894  * 
18895  */
18896
18897 /**
18898  * @class Roo.bootstrap.TabPanel
18899  * @extends Roo.bootstrap.Component
18900  * Bootstrap TabPanel class
18901  * @cfg {Boolean} active panel active
18902  * @cfg {String} html panel content
18903  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18904  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18905  * @cfg {String} href click to link..
18906  * 
18907  * 
18908  * @constructor
18909  * Create a new TabPanel
18910  * @param {Object} config The config object
18911  */
18912
18913 Roo.bootstrap.TabPanel = function(config){
18914     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18915     this.addEvents({
18916         /**
18917              * @event changed
18918              * Fires when the active status changes
18919              * @param {Roo.bootstrap.TabPanel} this
18920              * @param {Boolean} state the new state
18921             
18922          */
18923         'changed': true,
18924         /**
18925              * @event beforedeactivate
18926              * Fires before a tab is de-activated - can be used to do validation on a form.
18927              * @param {Roo.bootstrap.TabPanel} this
18928              * @return {Boolean} false if there is an error
18929             
18930          */
18931         'beforedeactivate': true
18932      });
18933     
18934     this.tabId = this.tabId || Roo.id();
18935   
18936 };
18937
18938 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18939     
18940     active: false,
18941     html: false,
18942     tabId: false,
18943     navId : false,
18944     href : '',
18945     
18946     getAutoCreate : function(){
18947         
18948         
18949         var cfg = {
18950             tag: 'div',
18951             // item is needed for carousel - not sure if it has any effect otherwise
18952             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18953             html: this.html || ''
18954         };
18955         
18956         if(this.active){
18957             cfg.cls += ' active';
18958         }
18959         
18960         if(this.tabId){
18961             cfg.tabId = this.tabId;
18962         }
18963         
18964         
18965         
18966         return cfg;
18967     },
18968     
18969     initEvents:  function()
18970     {
18971         var p = this.parent();
18972         
18973         this.navId = this.navId || p.navId;
18974         
18975         if (typeof(this.navId) != 'undefined') {
18976             // not really needed.. but just in case.. parent should be a NavGroup.
18977             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18978             
18979             tg.register(this);
18980             
18981             var i = tg.tabs.length - 1;
18982             
18983             if(this.active && tg.bullets > 0 && i < tg.bullets){
18984                 tg.setActiveBullet(i);
18985             }
18986         }
18987         
18988         this.el.on('click', this.onClick, this);
18989         
18990         if(Roo.isTouch){
18991             this.el.on("touchstart", this.onTouchStart, this);
18992             this.el.on("touchmove", this.onTouchMove, this);
18993             this.el.on("touchend", this.onTouchEnd, this);
18994         }
18995         
18996     },
18997     
18998     onRender : function(ct, position)
18999     {
19000         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
19001     },
19002     
19003     setActive : function(state)
19004     {
19005         Roo.log("panel - set active " + this.tabId + "=" + state);
19006         
19007         this.active = state;
19008         if (!state) {
19009             this.el.removeClass('active');
19010             
19011         } else  if (!this.el.hasClass('active')) {
19012             this.el.addClass('active');
19013         }
19014         
19015         this.fireEvent('changed', this, state);
19016     },
19017     
19018     onClick : function(e)
19019     {
19020         e.preventDefault();
19021         
19022         if(!this.href.length){
19023             return;
19024         }
19025         
19026         window.location.href = this.href;
19027     },
19028     
19029     startX : 0,
19030     startY : 0,
19031     endX : 0,
19032     endY : 0,
19033     swiping : false,
19034     
19035     onTouchStart : function(e)
19036     {
19037         this.swiping = false;
19038         
19039         this.startX = e.browserEvent.touches[0].clientX;
19040         this.startY = e.browserEvent.touches[0].clientY;
19041     },
19042     
19043     onTouchMove : function(e)
19044     {
19045         this.swiping = true;
19046         
19047         this.endX = e.browserEvent.touches[0].clientX;
19048         this.endY = e.browserEvent.touches[0].clientY;
19049     },
19050     
19051     onTouchEnd : function(e)
19052     {
19053         if(!this.swiping){
19054             this.onClick(e);
19055             return;
19056         }
19057         
19058         var tabGroup = this.parent();
19059         
19060         if(this.endX > this.startX){ // swiping right
19061             tabGroup.showPanelPrev();
19062             return;
19063         }
19064         
19065         if(this.startX > this.endX){ // swiping left
19066             tabGroup.showPanelNext();
19067             return;
19068         }
19069     }
19070     
19071     
19072 });
19073  
19074
19075  
19076
19077  /*
19078  * - LGPL
19079  *
19080  * DateField
19081  * 
19082  */
19083
19084 /**
19085  * @class Roo.bootstrap.DateField
19086  * @extends Roo.bootstrap.Input
19087  * Bootstrap DateField class
19088  * @cfg {Number} weekStart default 0
19089  * @cfg {String} viewMode default empty, (months|years)
19090  * @cfg {String} minViewMode default empty, (months|years)
19091  * @cfg {Number} startDate default -Infinity
19092  * @cfg {Number} endDate default Infinity
19093  * @cfg {Boolean} todayHighlight default false
19094  * @cfg {Boolean} todayBtn default false
19095  * @cfg {Boolean} calendarWeeks default false
19096  * @cfg {Object} daysOfWeekDisabled default empty
19097  * @cfg {Boolean} singleMode default false (true | false)
19098  * 
19099  * @cfg {Boolean} keyboardNavigation default true
19100  * @cfg {String} language default en
19101  * 
19102  * @constructor
19103  * Create a new DateField
19104  * @param {Object} config The config object
19105  */
19106
19107 Roo.bootstrap.DateField = function(config){
19108     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
19109      this.addEvents({
19110             /**
19111              * @event show
19112              * Fires when this field show.
19113              * @param {Roo.bootstrap.DateField} this
19114              * @param {Mixed} date The date value
19115              */
19116             show : true,
19117             /**
19118              * @event show
19119              * Fires when this field hide.
19120              * @param {Roo.bootstrap.DateField} this
19121              * @param {Mixed} date The date value
19122              */
19123             hide : true,
19124             /**
19125              * @event select
19126              * Fires when select a date.
19127              * @param {Roo.bootstrap.DateField} this
19128              * @param {Mixed} date The date value
19129              */
19130             select : true,
19131             /**
19132              * @event beforeselect
19133              * Fires when before select a date.
19134              * @param {Roo.bootstrap.DateField} this
19135              * @param {Mixed} date The date value
19136              */
19137             beforeselect : true
19138         });
19139 };
19140
19141 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
19142     
19143     /**
19144      * @cfg {String} format
19145      * The default date format string which can be overriden for localization support.  The format must be
19146      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
19147      */
19148     format : "m/d/y",
19149     /**
19150      * @cfg {String} altFormats
19151      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
19152      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
19153      */
19154     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
19155     
19156     weekStart : 0,
19157     
19158     viewMode : '',
19159     
19160     minViewMode : '',
19161     
19162     todayHighlight : false,
19163     
19164     todayBtn: false,
19165     
19166     language: 'en',
19167     
19168     keyboardNavigation: true,
19169     
19170     calendarWeeks: false,
19171     
19172     startDate: -Infinity,
19173     
19174     endDate: Infinity,
19175     
19176     daysOfWeekDisabled: [],
19177     
19178     _events: [],
19179     
19180     singleMode : false,
19181     
19182     UTCDate: function()
19183     {
19184         return new Date(Date.UTC.apply(Date, arguments));
19185     },
19186     
19187     UTCToday: function()
19188     {
19189         var today = new Date();
19190         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
19191     },
19192     
19193     getDate: function() {
19194             var d = this.getUTCDate();
19195             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
19196     },
19197     
19198     getUTCDate: function() {
19199             return this.date;
19200     },
19201     
19202     setDate: function(d) {
19203             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
19204     },
19205     
19206     setUTCDate: function(d) {
19207             this.date = d;
19208             this.setValue(this.formatDate(this.date));
19209     },
19210         
19211     onRender: function(ct, position)
19212     {
19213         
19214         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
19215         
19216         this.language = this.language || 'en';
19217         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
19218         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
19219         
19220         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
19221         this.format = this.format || 'm/d/y';
19222         this.isInline = false;
19223         this.isInput = true;
19224         this.component = this.el.select('.add-on', true).first() || false;
19225         this.component = (this.component && this.component.length === 0) ? false : this.component;
19226         this.hasInput = this.component && this.inputEl().length;
19227         
19228         if (typeof(this.minViewMode === 'string')) {
19229             switch (this.minViewMode) {
19230                 case 'months':
19231                     this.minViewMode = 1;
19232                     break;
19233                 case 'years':
19234                     this.minViewMode = 2;
19235                     break;
19236                 default:
19237                     this.minViewMode = 0;
19238                     break;
19239             }
19240         }
19241         
19242         if (typeof(this.viewMode === 'string')) {
19243             switch (this.viewMode) {
19244                 case 'months':
19245                     this.viewMode = 1;
19246                     break;
19247                 case 'years':
19248                     this.viewMode = 2;
19249                     break;
19250                 default:
19251                     this.viewMode = 0;
19252                     break;
19253             }
19254         }
19255                 
19256         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
19257         
19258 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
19259         
19260         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19261         
19262         this.picker().on('mousedown', this.onMousedown, this);
19263         this.picker().on('click', this.onClick, this);
19264         
19265         this.picker().addClass('datepicker-dropdown');
19266         
19267         this.startViewMode = this.viewMode;
19268         
19269         if(this.singleMode){
19270             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
19271                 v.setVisibilityMode(Roo.Element.DISPLAY);
19272                 v.hide();
19273             });
19274             
19275             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19276                 v.setStyle('width', '189px');
19277             });
19278         }
19279         
19280         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19281             if(!this.calendarWeeks){
19282                 v.remove();
19283                 return;
19284             }
19285             
19286             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19287             v.attr('colspan', function(i, val){
19288                 return parseInt(val) + 1;
19289             });
19290         });
19291                         
19292         
19293         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19294         
19295         this.setStartDate(this.startDate);
19296         this.setEndDate(this.endDate);
19297         
19298         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19299         
19300         this.fillDow();
19301         this.fillMonths();
19302         this.update();
19303         this.showMode();
19304         
19305         if(this.isInline) {
19306             this.showPopup();
19307         }
19308     },
19309     
19310     picker : function()
19311     {
19312         return this.pickerEl;
19313 //        return this.el.select('.datepicker', true).first();
19314     },
19315     
19316     fillDow: function()
19317     {
19318         var dowCnt = this.weekStart;
19319         
19320         var dow = {
19321             tag: 'tr',
19322             cn: [
19323                 
19324             ]
19325         };
19326         
19327         if(this.calendarWeeks){
19328             dow.cn.push({
19329                 tag: 'th',
19330                 cls: 'cw',
19331                 html: '&nbsp;'
19332             })
19333         }
19334         
19335         while (dowCnt < this.weekStart + 7) {
19336             dow.cn.push({
19337                 tag: 'th',
19338                 cls: 'dow',
19339                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19340             });
19341         }
19342         
19343         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19344     },
19345     
19346     fillMonths: function()
19347     {    
19348         var i = 0;
19349         var months = this.picker().select('>.datepicker-months td', true).first();
19350         
19351         months.dom.innerHTML = '';
19352         
19353         while (i < 12) {
19354             var month = {
19355                 tag: 'span',
19356                 cls: 'month',
19357                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19358             };
19359             
19360             months.createChild(month);
19361         }
19362         
19363     },
19364     
19365     update: function()
19366     {
19367         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;
19368         
19369         if (this.date < this.startDate) {
19370             this.viewDate = new Date(this.startDate);
19371         } else if (this.date > this.endDate) {
19372             this.viewDate = new Date(this.endDate);
19373         } else {
19374             this.viewDate = new Date(this.date);
19375         }
19376         
19377         this.fill();
19378     },
19379     
19380     fill: function() 
19381     {
19382         var d = new Date(this.viewDate),
19383                 year = d.getUTCFullYear(),
19384                 month = d.getUTCMonth(),
19385                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19386                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19387                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19388                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19389                 currentDate = this.date && this.date.valueOf(),
19390                 today = this.UTCToday();
19391         
19392         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19393         
19394 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19395         
19396 //        this.picker.select('>tfoot th.today').
19397 //                                              .text(dates[this.language].today)
19398 //                                              .toggle(this.todayBtn !== false);
19399     
19400         this.updateNavArrows();
19401         this.fillMonths();
19402                                                 
19403         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19404         
19405         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19406          
19407         prevMonth.setUTCDate(day);
19408         
19409         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19410         
19411         var nextMonth = new Date(prevMonth);
19412         
19413         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19414         
19415         nextMonth = nextMonth.valueOf();
19416         
19417         var fillMonths = false;
19418         
19419         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19420         
19421         while(prevMonth.valueOf() <= nextMonth) {
19422             var clsName = '';
19423             
19424             if (prevMonth.getUTCDay() === this.weekStart) {
19425                 if(fillMonths){
19426                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19427                 }
19428                     
19429                 fillMonths = {
19430                     tag: 'tr',
19431                     cn: []
19432                 };
19433                 
19434                 if(this.calendarWeeks){
19435                     // ISO 8601: First week contains first thursday.
19436                     // ISO also states week starts on Monday, but we can be more abstract here.
19437                     var
19438                     // Start of current week: based on weekstart/current date
19439                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19440                     // Thursday of this week
19441                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19442                     // First Thursday of year, year from thursday
19443                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19444                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19445                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19446                     
19447                     fillMonths.cn.push({
19448                         tag: 'td',
19449                         cls: 'cw',
19450                         html: calWeek
19451                     });
19452                 }
19453             }
19454             
19455             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19456                 clsName += ' old';
19457             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19458                 clsName += ' new';
19459             }
19460             if (this.todayHighlight &&
19461                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19462                 prevMonth.getUTCMonth() == today.getMonth() &&
19463                 prevMonth.getUTCDate() == today.getDate()) {
19464                 clsName += ' today';
19465             }
19466             
19467             if (currentDate && prevMonth.valueOf() === currentDate) {
19468                 clsName += ' active';
19469             }
19470             
19471             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19472                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19473                     clsName += ' disabled';
19474             }
19475             
19476             fillMonths.cn.push({
19477                 tag: 'td',
19478                 cls: 'day ' + clsName,
19479                 html: prevMonth.getDate()
19480             });
19481             
19482             prevMonth.setDate(prevMonth.getDate()+1);
19483         }
19484           
19485         var currentYear = this.date && this.date.getUTCFullYear();
19486         var currentMonth = this.date && this.date.getUTCMonth();
19487         
19488         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19489         
19490         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19491             v.removeClass('active');
19492             
19493             if(currentYear === year && k === currentMonth){
19494                 v.addClass('active');
19495             }
19496             
19497             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19498                 v.addClass('disabled');
19499             }
19500             
19501         });
19502         
19503         
19504         year = parseInt(year/10, 10) * 10;
19505         
19506         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19507         
19508         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19509         
19510         year -= 1;
19511         for (var i = -1; i < 11; i++) {
19512             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19513                 tag: 'span',
19514                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19515                 html: year
19516             });
19517             
19518             year += 1;
19519         }
19520     },
19521     
19522     showMode: function(dir) 
19523     {
19524         if (dir) {
19525             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19526         }
19527         
19528         Roo.each(this.picker().select('>div',true).elements, function(v){
19529             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19530             v.hide();
19531         });
19532         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19533     },
19534     
19535     place: function()
19536     {
19537         if(this.isInline) {
19538             return;
19539         }
19540         
19541         this.picker().removeClass(['bottom', 'top']);
19542         
19543         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19544             /*
19545              * place to the top of element!
19546              *
19547              */
19548             
19549             this.picker().addClass('top');
19550             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19551             
19552             return;
19553         }
19554         
19555         this.picker().addClass('bottom');
19556         
19557         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19558     },
19559     
19560     parseDate : function(value)
19561     {
19562         if(!value || value instanceof Date){
19563             return value;
19564         }
19565         var v = Date.parseDate(value, this.format);
19566         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19567             v = Date.parseDate(value, 'Y-m-d');
19568         }
19569         if(!v && this.altFormats){
19570             if(!this.altFormatsArray){
19571                 this.altFormatsArray = this.altFormats.split("|");
19572             }
19573             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19574                 v = Date.parseDate(value, this.altFormatsArray[i]);
19575             }
19576         }
19577         return v;
19578     },
19579     
19580     formatDate : function(date, fmt)
19581     {   
19582         return (!date || !(date instanceof Date)) ?
19583         date : date.dateFormat(fmt || this.format);
19584     },
19585     
19586     onFocus : function()
19587     {
19588         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19589         this.showPopup();
19590     },
19591     
19592     onBlur : function()
19593     {
19594         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19595         
19596         var d = this.inputEl().getValue();
19597         
19598         this.setValue(d);
19599                 
19600         this.hidePopup();
19601     },
19602     
19603     showPopup : function()
19604     {
19605         this.picker().show();
19606         this.update();
19607         this.place();
19608         
19609         this.fireEvent('showpopup', this, this.date);
19610     },
19611     
19612     hidePopup : function()
19613     {
19614         if(this.isInline) {
19615             return;
19616         }
19617         this.picker().hide();
19618         this.viewMode = this.startViewMode;
19619         this.showMode();
19620         
19621         this.fireEvent('hidepopup', this, this.date);
19622         
19623     },
19624     
19625     onMousedown: function(e)
19626     {
19627         e.stopPropagation();
19628         e.preventDefault();
19629     },
19630     
19631     keyup: function(e)
19632     {
19633         Roo.bootstrap.DateField.superclass.keyup.call(this);
19634         this.update();
19635     },
19636
19637     setValue: function(v)
19638     {
19639         if(this.fireEvent('beforeselect', this, v) !== false){
19640             var d = new Date(this.parseDate(v) ).clearTime();
19641         
19642             if(isNaN(d.getTime())){
19643                 this.date = this.viewDate = '';
19644                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19645                 return;
19646             }
19647
19648             v = this.formatDate(d);
19649
19650             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19651
19652             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19653
19654             this.update();
19655
19656             this.fireEvent('select', this, this.date);
19657         }
19658     },
19659     
19660     getValue: function()
19661     {
19662         return this.formatDate(this.date);
19663     },
19664     
19665     fireKey: function(e)
19666     {
19667         if (!this.picker().isVisible()){
19668             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19669                 this.showPopup();
19670             }
19671             return;
19672         }
19673         
19674         var dateChanged = false,
19675         dir, day, month,
19676         newDate, newViewDate;
19677         
19678         switch(e.keyCode){
19679             case 27: // escape
19680                 this.hidePopup();
19681                 e.preventDefault();
19682                 break;
19683             case 37: // left
19684             case 39: // right
19685                 if (!this.keyboardNavigation) {
19686                     break;
19687                 }
19688                 dir = e.keyCode == 37 ? -1 : 1;
19689                 
19690                 if (e.ctrlKey){
19691                     newDate = this.moveYear(this.date, dir);
19692                     newViewDate = this.moveYear(this.viewDate, dir);
19693                 } else if (e.shiftKey){
19694                     newDate = this.moveMonth(this.date, dir);
19695                     newViewDate = this.moveMonth(this.viewDate, dir);
19696                 } else {
19697                     newDate = new Date(this.date);
19698                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19699                     newViewDate = new Date(this.viewDate);
19700                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19701                 }
19702                 if (this.dateWithinRange(newDate)){
19703                     this.date = newDate;
19704                     this.viewDate = newViewDate;
19705                     this.setValue(this.formatDate(this.date));
19706 //                    this.update();
19707                     e.preventDefault();
19708                     dateChanged = true;
19709                 }
19710                 break;
19711             case 38: // up
19712             case 40: // down
19713                 if (!this.keyboardNavigation) {
19714                     break;
19715                 }
19716                 dir = e.keyCode == 38 ? -1 : 1;
19717                 if (e.ctrlKey){
19718                     newDate = this.moveYear(this.date, dir);
19719                     newViewDate = this.moveYear(this.viewDate, dir);
19720                 } else if (e.shiftKey){
19721                     newDate = this.moveMonth(this.date, dir);
19722                     newViewDate = this.moveMonth(this.viewDate, dir);
19723                 } else {
19724                     newDate = new Date(this.date);
19725                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19726                     newViewDate = new Date(this.viewDate);
19727                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19728                 }
19729                 if (this.dateWithinRange(newDate)){
19730                     this.date = newDate;
19731                     this.viewDate = newViewDate;
19732                     this.setValue(this.formatDate(this.date));
19733 //                    this.update();
19734                     e.preventDefault();
19735                     dateChanged = true;
19736                 }
19737                 break;
19738             case 13: // enter
19739                 this.setValue(this.formatDate(this.date));
19740                 this.hidePopup();
19741                 e.preventDefault();
19742                 break;
19743             case 9: // tab
19744                 this.setValue(this.formatDate(this.date));
19745                 this.hidePopup();
19746                 break;
19747             case 16: // shift
19748             case 17: // ctrl
19749             case 18: // alt
19750                 break;
19751             default :
19752                 this.hidePopup();
19753                 
19754         }
19755     },
19756     
19757     
19758     onClick: function(e) 
19759     {
19760         e.stopPropagation();
19761         e.preventDefault();
19762         
19763         var target = e.getTarget();
19764         
19765         if(target.nodeName.toLowerCase() === 'i'){
19766             target = Roo.get(target).dom.parentNode;
19767         }
19768         
19769         var nodeName = target.nodeName;
19770         var className = target.className;
19771         var html = target.innerHTML;
19772         //Roo.log(nodeName);
19773         
19774         switch(nodeName.toLowerCase()) {
19775             case 'th':
19776                 switch(className) {
19777                     case 'switch':
19778                         this.showMode(1);
19779                         break;
19780                     case 'prev':
19781                     case 'next':
19782                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19783                         switch(this.viewMode){
19784                                 case 0:
19785                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19786                                         break;
19787                                 case 1:
19788                                 case 2:
19789                                         this.viewDate = this.moveYear(this.viewDate, dir);
19790                                         break;
19791                         }
19792                         this.fill();
19793                         break;
19794                     case 'today':
19795                         var date = new Date();
19796                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19797 //                        this.fill()
19798                         this.setValue(this.formatDate(this.date));
19799                         
19800                         this.hidePopup();
19801                         break;
19802                 }
19803                 break;
19804             case 'span':
19805                 if (className.indexOf('disabled') < 0) {
19806                     this.viewDate.setUTCDate(1);
19807                     if (className.indexOf('month') > -1) {
19808                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19809                     } else {
19810                         var year = parseInt(html, 10) || 0;
19811                         this.viewDate.setUTCFullYear(year);
19812                         
19813                     }
19814                     
19815                     if(this.singleMode){
19816                         this.setValue(this.formatDate(this.viewDate));
19817                         this.hidePopup();
19818                         return;
19819                     }
19820                     
19821                     this.showMode(-1);
19822                     this.fill();
19823                 }
19824                 break;
19825                 
19826             case 'td':
19827                 //Roo.log(className);
19828                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19829                     var day = parseInt(html, 10) || 1;
19830                     var year = this.viewDate.getUTCFullYear(),
19831                         month = this.viewDate.getUTCMonth();
19832
19833                     if (className.indexOf('old') > -1) {
19834                         if(month === 0 ){
19835                             month = 11;
19836                             year -= 1;
19837                         }else{
19838                             month -= 1;
19839                         }
19840                     } else if (className.indexOf('new') > -1) {
19841                         if (month == 11) {
19842                             month = 0;
19843                             year += 1;
19844                         } else {
19845                             month += 1;
19846                         }
19847                     }
19848                     //Roo.log([year,month,day]);
19849                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19850                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19851 //                    this.fill();
19852                     //Roo.log(this.formatDate(this.date));
19853                     this.setValue(this.formatDate(this.date));
19854                     this.hidePopup();
19855                 }
19856                 break;
19857         }
19858     },
19859     
19860     setStartDate: function(startDate)
19861     {
19862         this.startDate = startDate || -Infinity;
19863         if (this.startDate !== -Infinity) {
19864             this.startDate = this.parseDate(this.startDate);
19865         }
19866         this.update();
19867         this.updateNavArrows();
19868     },
19869
19870     setEndDate: function(endDate)
19871     {
19872         this.endDate = endDate || Infinity;
19873         if (this.endDate !== Infinity) {
19874             this.endDate = this.parseDate(this.endDate);
19875         }
19876         this.update();
19877         this.updateNavArrows();
19878     },
19879     
19880     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19881     {
19882         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19883         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19884             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19885         }
19886         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19887             return parseInt(d, 10);
19888         });
19889         this.update();
19890         this.updateNavArrows();
19891     },
19892     
19893     updateNavArrows: function() 
19894     {
19895         if(this.singleMode){
19896             return;
19897         }
19898         
19899         var d = new Date(this.viewDate),
19900         year = d.getUTCFullYear(),
19901         month = d.getUTCMonth();
19902         
19903         Roo.each(this.picker().select('.prev', true).elements, function(v){
19904             v.show();
19905             switch (this.viewMode) {
19906                 case 0:
19907
19908                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19909                         v.hide();
19910                     }
19911                     break;
19912                 case 1:
19913                 case 2:
19914                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19915                         v.hide();
19916                     }
19917                     break;
19918             }
19919         });
19920         
19921         Roo.each(this.picker().select('.next', true).elements, function(v){
19922             v.show();
19923             switch (this.viewMode) {
19924                 case 0:
19925
19926                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19927                         v.hide();
19928                     }
19929                     break;
19930                 case 1:
19931                 case 2:
19932                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19933                         v.hide();
19934                     }
19935                     break;
19936             }
19937         })
19938     },
19939     
19940     moveMonth: function(date, dir)
19941     {
19942         if (!dir) {
19943             return date;
19944         }
19945         var new_date = new Date(date.valueOf()),
19946         day = new_date.getUTCDate(),
19947         month = new_date.getUTCMonth(),
19948         mag = Math.abs(dir),
19949         new_month, test;
19950         dir = dir > 0 ? 1 : -1;
19951         if (mag == 1){
19952             test = dir == -1
19953             // If going back one month, make sure month is not current month
19954             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19955             ? function(){
19956                 return new_date.getUTCMonth() == month;
19957             }
19958             // If going forward one month, make sure month is as expected
19959             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19960             : function(){
19961                 return new_date.getUTCMonth() != new_month;
19962             };
19963             new_month = month + dir;
19964             new_date.setUTCMonth(new_month);
19965             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19966             if (new_month < 0 || new_month > 11) {
19967                 new_month = (new_month + 12) % 12;
19968             }
19969         } else {
19970             // For magnitudes >1, move one month at a time...
19971             for (var i=0; i<mag; i++) {
19972                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19973                 new_date = this.moveMonth(new_date, dir);
19974             }
19975             // ...then reset the day, keeping it in the new month
19976             new_month = new_date.getUTCMonth();
19977             new_date.setUTCDate(day);
19978             test = function(){
19979                 return new_month != new_date.getUTCMonth();
19980             };
19981         }
19982         // Common date-resetting loop -- if date is beyond end of month, make it
19983         // end of month
19984         while (test()){
19985             new_date.setUTCDate(--day);
19986             new_date.setUTCMonth(new_month);
19987         }
19988         return new_date;
19989     },
19990
19991     moveYear: function(date, dir)
19992     {
19993         return this.moveMonth(date, dir*12);
19994     },
19995
19996     dateWithinRange: function(date)
19997     {
19998         return date >= this.startDate && date <= this.endDate;
19999     },
20000
20001     
20002     remove: function() 
20003     {
20004         this.picker().remove();
20005     },
20006     
20007     validateValue : function(value)
20008     {
20009         if(this.getVisibilityEl().hasClass('hidden')){
20010             return true;
20011         }
20012         
20013         if(value.length < 1)  {
20014             if(this.allowBlank){
20015                 return true;
20016             }
20017             return false;
20018         }
20019         
20020         if(value.length < this.minLength){
20021             return false;
20022         }
20023         if(value.length > this.maxLength){
20024             return false;
20025         }
20026         if(this.vtype){
20027             var vt = Roo.form.VTypes;
20028             if(!vt[this.vtype](value, this)){
20029                 return false;
20030             }
20031         }
20032         if(typeof this.validator == "function"){
20033             var msg = this.validator(value);
20034             if(msg !== true){
20035                 return false;
20036             }
20037         }
20038         
20039         if(this.regex && !this.regex.test(value)){
20040             return false;
20041         }
20042         
20043         if(typeof(this.parseDate(value)) == 'undefined'){
20044             return false;
20045         }
20046         
20047         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
20048             return false;
20049         }      
20050         
20051         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
20052             return false;
20053         } 
20054         
20055         
20056         return true;
20057     },
20058     
20059     reset : function()
20060     {
20061         this.date = this.viewDate = '';
20062         
20063         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20064     }
20065    
20066 });
20067
20068 Roo.apply(Roo.bootstrap.DateField,  {
20069     
20070     head : {
20071         tag: 'thead',
20072         cn: [
20073         {
20074             tag: 'tr',
20075             cn: [
20076             {
20077                 tag: 'th',
20078                 cls: 'prev',
20079                 html: '<i class="fa fa-arrow-left"/>'
20080             },
20081             {
20082                 tag: 'th',
20083                 cls: 'switch',
20084                 colspan: '5'
20085             },
20086             {
20087                 tag: 'th',
20088                 cls: 'next',
20089                 html: '<i class="fa fa-arrow-right"/>'
20090             }
20091
20092             ]
20093         }
20094         ]
20095     },
20096     
20097     content : {
20098         tag: 'tbody',
20099         cn: [
20100         {
20101             tag: 'tr',
20102             cn: [
20103             {
20104                 tag: 'td',
20105                 colspan: '7'
20106             }
20107             ]
20108         }
20109         ]
20110     },
20111     
20112     footer : {
20113         tag: 'tfoot',
20114         cn: [
20115         {
20116             tag: 'tr',
20117             cn: [
20118             {
20119                 tag: 'th',
20120                 colspan: '7',
20121                 cls: 'today'
20122             }
20123                     
20124             ]
20125         }
20126         ]
20127     },
20128     
20129     dates:{
20130         en: {
20131             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
20132             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
20133             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
20134             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20135             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
20136             today: "Today"
20137         }
20138     },
20139     
20140     modes: [
20141     {
20142         clsName: 'days',
20143         navFnc: 'Month',
20144         navStep: 1
20145     },
20146     {
20147         clsName: 'months',
20148         navFnc: 'FullYear',
20149         navStep: 1
20150     },
20151     {
20152         clsName: 'years',
20153         navFnc: 'FullYear',
20154         navStep: 10
20155     }]
20156 });
20157
20158 Roo.apply(Roo.bootstrap.DateField,  {
20159   
20160     template : {
20161         tag: 'div',
20162         cls: 'datepicker dropdown-menu roo-dynamic',
20163         cn: [
20164         {
20165             tag: 'div',
20166             cls: 'datepicker-days',
20167             cn: [
20168             {
20169                 tag: 'table',
20170                 cls: 'table-condensed',
20171                 cn:[
20172                 Roo.bootstrap.DateField.head,
20173                 {
20174                     tag: 'tbody'
20175                 },
20176                 Roo.bootstrap.DateField.footer
20177                 ]
20178             }
20179             ]
20180         },
20181         {
20182             tag: 'div',
20183             cls: 'datepicker-months',
20184             cn: [
20185             {
20186                 tag: 'table',
20187                 cls: 'table-condensed',
20188                 cn:[
20189                 Roo.bootstrap.DateField.head,
20190                 Roo.bootstrap.DateField.content,
20191                 Roo.bootstrap.DateField.footer
20192                 ]
20193             }
20194             ]
20195         },
20196         {
20197             tag: 'div',
20198             cls: 'datepicker-years',
20199             cn: [
20200             {
20201                 tag: 'table',
20202                 cls: 'table-condensed',
20203                 cn:[
20204                 Roo.bootstrap.DateField.head,
20205                 Roo.bootstrap.DateField.content,
20206                 Roo.bootstrap.DateField.footer
20207                 ]
20208             }
20209             ]
20210         }
20211         ]
20212     }
20213 });
20214
20215  
20216
20217  /*
20218  * - LGPL
20219  *
20220  * TimeField
20221  * 
20222  */
20223
20224 /**
20225  * @class Roo.bootstrap.TimeField
20226  * @extends Roo.bootstrap.Input
20227  * Bootstrap DateField class
20228  * 
20229  * 
20230  * @constructor
20231  * Create a new TimeField
20232  * @param {Object} config The config object
20233  */
20234
20235 Roo.bootstrap.TimeField = function(config){
20236     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
20237     this.addEvents({
20238             /**
20239              * @event show
20240              * Fires when this field show.
20241              * @param {Roo.bootstrap.DateField} thisthis
20242              * @param {Mixed} date The date value
20243              */
20244             show : true,
20245             /**
20246              * @event show
20247              * Fires when this field hide.
20248              * @param {Roo.bootstrap.DateField} this
20249              * @param {Mixed} date The date value
20250              */
20251             hide : true,
20252             /**
20253              * @event select
20254              * Fires when select a date.
20255              * @param {Roo.bootstrap.DateField} this
20256              * @param {Mixed} date The date value
20257              */
20258             select : true
20259         });
20260 };
20261
20262 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
20263     
20264     /**
20265      * @cfg {String} format
20266      * The default time format string which can be overriden for localization support.  The format must be
20267      * valid according to {@link Date#parseDate} (defaults to 'H:i').
20268      */
20269     format : "H:i",
20270        
20271     onRender: function(ct, position)
20272     {
20273         
20274         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
20275                 
20276         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
20277         
20278         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20279         
20280         this.pop = this.picker().select('>.datepicker-time',true).first();
20281         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20282         
20283         this.picker().on('mousedown', this.onMousedown, this);
20284         this.picker().on('click', this.onClick, this);
20285         
20286         this.picker().addClass('datepicker-dropdown');
20287     
20288         this.fillTime();
20289         this.update();
20290             
20291         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20292         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20293         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20294         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20295         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20296         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20297
20298     },
20299     
20300     fireKey: function(e){
20301         if (!this.picker().isVisible()){
20302             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20303                 this.show();
20304             }
20305             return;
20306         }
20307
20308         e.preventDefault();
20309         
20310         switch(e.keyCode){
20311             case 27: // escape
20312                 this.hide();
20313                 break;
20314             case 37: // left
20315             case 39: // right
20316                 this.onTogglePeriod();
20317                 break;
20318             case 38: // up
20319                 this.onIncrementMinutes();
20320                 break;
20321             case 40: // down
20322                 this.onDecrementMinutes();
20323                 break;
20324             case 13: // enter
20325             case 9: // tab
20326                 this.setTime();
20327                 break;
20328         }
20329     },
20330     
20331     onClick: function(e) {
20332         e.stopPropagation();
20333         e.preventDefault();
20334     },
20335     
20336     picker : function()
20337     {
20338         return this.el.select('.datepicker', true).first();
20339     },
20340     
20341     fillTime: function()
20342     {    
20343         var time = this.pop.select('tbody', true).first();
20344         
20345         time.dom.innerHTML = '';
20346         
20347         time.createChild({
20348             tag: 'tr',
20349             cn: [
20350                 {
20351                     tag: 'td',
20352                     cn: [
20353                         {
20354                             tag: 'a',
20355                             href: '#',
20356                             cls: 'btn',
20357                             cn: [
20358                                 {
20359                                     tag: 'span',
20360                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
20361                                 }
20362                             ]
20363                         } 
20364                     ]
20365                 },
20366                 {
20367                     tag: 'td',
20368                     cls: 'separator'
20369                 },
20370                 {
20371                     tag: 'td',
20372                     cn: [
20373                         {
20374                             tag: 'a',
20375                             href: '#',
20376                             cls: 'btn',
20377                             cn: [
20378                                 {
20379                                     tag: 'span',
20380                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
20381                                 }
20382                             ]
20383                         }
20384                     ]
20385                 },
20386                 {
20387                     tag: 'td',
20388                     cls: 'separator'
20389                 }
20390             ]
20391         });
20392         
20393         time.createChild({
20394             tag: 'tr',
20395             cn: [
20396                 {
20397                     tag: 'td',
20398                     cn: [
20399                         {
20400                             tag: 'span',
20401                             cls: 'timepicker-hour',
20402                             html: '00'
20403                         }  
20404                     ]
20405                 },
20406                 {
20407                     tag: 'td',
20408                     cls: 'separator',
20409                     html: ':'
20410                 },
20411                 {
20412                     tag: 'td',
20413                     cn: [
20414                         {
20415                             tag: 'span',
20416                             cls: 'timepicker-minute',
20417                             html: '00'
20418                         }  
20419                     ]
20420                 },
20421                 {
20422                     tag: 'td',
20423                     cls: 'separator'
20424                 },
20425                 {
20426                     tag: 'td',
20427                     cn: [
20428                         {
20429                             tag: 'button',
20430                             type: 'button',
20431                             cls: 'btn btn-primary period',
20432                             html: 'AM'
20433                             
20434                         }
20435                     ]
20436                 }
20437             ]
20438         });
20439         
20440         time.createChild({
20441             tag: 'tr',
20442             cn: [
20443                 {
20444                     tag: 'td',
20445                     cn: [
20446                         {
20447                             tag: 'a',
20448                             href: '#',
20449                             cls: 'btn',
20450                             cn: [
20451                                 {
20452                                     tag: 'span',
20453                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20454                                 }
20455                             ]
20456                         }
20457                     ]
20458                 },
20459                 {
20460                     tag: 'td',
20461                     cls: 'separator'
20462                 },
20463                 {
20464                     tag: 'td',
20465                     cn: [
20466                         {
20467                             tag: 'a',
20468                             href: '#',
20469                             cls: 'btn',
20470                             cn: [
20471                                 {
20472                                     tag: 'span',
20473                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20474                                 }
20475                             ]
20476                         }
20477                     ]
20478                 },
20479                 {
20480                     tag: 'td',
20481                     cls: 'separator'
20482                 }
20483             ]
20484         });
20485         
20486     },
20487     
20488     update: function()
20489     {
20490         
20491         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20492         
20493         this.fill();
20494     },
20495     
20496     fill: function() 
20497     {
20498         var hours = this.time.getHours();
20499         var minutes = this.time.getMinutes();
20500         var period = 'AM';
20501         
20502         if(hours > 11){
20503             period = 'PM';
20504         }
20505         
20506         if(hours == 0){
20507             hours = 12;
20508         }
20509         
20510         
20511         if(hours > 12){
20512             hours = hours - 12;
20513         }
20514         
20515         if(hours < 10){
20516             hours = '0' + hours;
20517         }
20518         
20519         if(minutes < 10){
20520             minutes = '0' + minutes;
20521         }
20522         
20523         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20524         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20525         this.pop.select('button', true).first().dom.innerHTML = period;
20526         
20527     },
20528     
20529     place: function()
20530     {   
20531         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20532         
20533         var cls = ['bottom'];
20534         
20535         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20536             cls.pop();
20537             cls.push('top');
20538         }
20539         
20540         cls.push('right');
20541         
20542         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20543             cls.pop();
20544             cls.push('left');
20545         }
20546         
20547         this.picker().addClass(cls.join('-'));
20548         
20549         var _this = this;
20550         
20551         Roo.each(cls, function(c){
20552             if(c == 'bottom'){
20553                 _this.picker().setTop(_this.inputEl().getHeight());
20554                 return;
20555             }
20556             if(c == 'top'){
20557                 _this.picker().setTop(0 - _this.picker().getHeight());
20558                 return;
20559             }
20560             
20561             if(c == 'left'){
20562                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20563                 return;
20564             }
20565             if(c == 'right'){
20566                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20567                 return;
20568             }
20569         });
20570         
20571     },
20572   
20573     onFocus : function()
20574     {
20575         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20576         this.show();
20577     },
20578     
20579     onBlur : function()
20580     {
20581         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20582         this.hide();
20583     },
20584     
20585     show : function()
20586     {
20587         this.picker().show();
20588         this.pop.show();
20589         this.update();
20590         this.place();
20591         
20592         this.fireEvent('show', this, this.date);
20593     },
20594     
20595     hide : function()
20596     {
20597         this.picker().hide();
20598         this.pop.hide();
20599         
20600         this.fireEvent('hide', this, this.date);
20601     },
20602     
20603     setTime : function()
20604     {
20605         this.hide();
20606         this.setValue(this.time.format(this.format));
20607         
20608         this.fireEvent('select', this, this.date);
20609         
20610         
20611     },
20612     
20613     onMousedown: function(e){
20614         e.stopPropagation();
20615         e.preventDefault();
20616     },
20617     
20618     onIncrementHours: function()
20619     {
20620         Roo.log('onIncrementHours');
20621         this.time = this.time.add(Date.HOUR, 1);
20622         this.update();
20623         
20624     },
20625     
20626     onDecrementHours: function()
20627     {
20628         Roo.log('onDecrementHours');
20629         this.time = this.time.add(Date.HOUR, -1);
20630         this.update();
20631     },
20632     
20633     onIncrementMinutes: function()
20634     {
20635         Roo.log('onIncrementMinutes');
20636         this.time = this.time.add(Date.MINUTE, 1);
20637         this.update();
20638     },
20639     
20640     onDecrementMinutes: function()
20641     {
20642         Roo.log('onDecrementMinutes');
20643         this.time = this.time.add(Date.MINUTE, -1);
20644         this.update();
20645     },
20646     
20647     onTogglePeriod: function()
20648     {
20649         Roo.log('onTogglePeriod');
20650         this.time = this.time.add(Date.HOUR, 12);
20651         this.update();
20652     }
20653     
20654    
20655 });
20656
20657 Roo.apply(Roo.bootstrap.TimeField,  {
20658     
20659     content : {
20660         tag: 'tbody',
20661         cn: [
20662             {
20663                 tag: 'tr',
20664                 cn: [
20665                 {
20666                     tag: 'td',
20667                     colspan: '7'
20668                 }
20669                 ]
20670             }
20671         ]
20672     },
20673     
20674     footer : {
20675         tag: 'tfoot',
20676         cn: [
20677             {
20678                 tag: 'tr',
20679                 cn: [
20680                 {
20681                     tag: 'th',
20682                     colspan: '7',
20683                     cls: '',
20684                     cn: [
20685                         {
20686                             tag: 'button',
20687                             cls: 'btn btn-info ok',
20688                             html: 'OK'
20689                         }
20690                     ]
20691                 }
20692
20693                 ]
20694             }
20695         ]
20696     }
20697 });
20698
20699 Roo.apply(Roo.bootstrap.TimeField,  {
20700   
20701     template : {
20702         tag: 'div',
20703         cls: 'datepicker dropdown-menu',
20704         cn: [
20705             {
20706                 tag: 'div',
20707                 cls: 'datepicker-time',
20708                 cn: [
20709                 {
20710                     tag: 'table',
20711                     cls: 'table-condensed',
20712                     cn:[
20713                     Roo.bootstrap.TimeField.content,
20714                     Roo.bootstrap.TimeField.footer
20715                     ]
20716                 }
20717                 ]
20718             }
20719         ]
20720     }
20721 });
20722
20723  
20724
20725  /*
20726  * - LGPL
20727  *
20728  * MonthField
20729  * 
20730  */
20731
20732 /**
20733  * @class Roo.bootstrap.MonthField
20734  * @extends Roo.bootstrap.Input
20735  * Bootstrap MonthField class
20736  * 
20737  * @cfg {String} language default en
20738  * 
20739  * @constructor
20740  * Create a new MonthField
20741  * @param {Object} config The config object
20742  */
20743
20744 Roo.bootstrap.MonthField = function(config){
20745     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20746     
20747     this.addEvents({
20748         /**
20749          * @event show
20750          * Fires when this field show.
20751          * @param {Roo.bootstrap.MonthField} this
20752          * @param {Mixed} date The date value
20753          */
20754         show : true,
20755         /**
20756          * @event show
20757          * Fires when this field hide.
20758          * @param {Roo.bootstrap.MonthField} this
20759          * @param {Mixed} date The date value
20760          */
20761         hide : true,
20762         /**
20763          * @event select
20764          * Fires when select a date.
20765          * @param {Roo.bootstrap.MonthField} this
20766          * @param {String} oldvalue The old value
20767          * @param {String} newvalue The new value
20768          */
20769         select : true
20770     });
20771 };
20772
20773 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20774     
20775     onRender: function(ct, position)
20776     {
20777         
20778         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20779         
20780         this.language = this.language || 'en';
20781         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20782         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20783         
20784         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20785         this.isInline = false;
20786         this.isInput = true;
20787         this.component = this.el.select('.add-on', true).first() || false;
20788         this.component = (this.component && this.component.length === 0) ? false : this.component;
20789         this.hasInput = this.component && this.inputEL().length;
20790         
20791         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20792         
20793         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20794         
20795         this.picker().on('mousedown', this.onMousedown, this);
20796         this.picker().on('click', this.onClick, this);
20797         
20798         this.picker().addClass('datepicker-dropdown');
20799         
20800         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20801             v.setStyle('width', '189px');
20802         });
20803         
20804         this.fillMonths();
20805         
20806         this.update();
20807         
20808         if(this.isInline) {
20809             this.show();
20810         }
20811         
20812     },
20813     
20814     setValue: function(v, suppressEvent)
20815     {   
20816         var o = this.getValue();
20817         
20818         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20819         
20820         this.update();
20821
20822         if(suppressEvent !== true){
20823             this.fireEvent('select', this, o, v);
20824         }
20825         
20826     },
20827     
20828     getValue: function()
20829     {
20830         return this.value;
20831     },
20832     
20833     onClick: function(e) 
20834     {
20835         e.stopPropagation();
20836         e.preventDefault();
20837         
20838         var target = e.getTarget();
20839         
20840         if(target.nodeName.toLowerCase() === 'i'){
20841             target = Roo.get(target).dom.parentNode;
20842         }
20843         
20844         var nodeName = target.nodeName;
20845         var className = target.className;
20846         var html = target.innerHTML;
20847         
20848         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20849             return;
20850         }
20851         
20852         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20853         
20854         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20855         
20856         this.hide();
20857                         
20858     },
20859     
20860     picker : function()
20861     {
20862         return this.pickerEl;
20863     },
20864     
20865     fillMonths: function()
20866     {    
20867         var i = 0;
20868         var months = this.picker().select('>.datepicker-months td', true).first();
20869         
20870         months.dom.innerHTML = '';
20871         
20872         while (i < 12) {
20873             var month = {
20874                 tag: 'span',
20875                 cls: 'month',
20876                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20877             };
20878             
20879             months.createChild(month);
20880         }
20881         
20882     },
20883     
20884     update: function()
20885     {
20886         var _this = this;
20887         
20888         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20889             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20890         }
20891         
20892         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20893             e.removeClass('active');
20894             
20895             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20896                 e.addClass('active');
20897             }
20898         })
20899     },
20900     
20901     place: function()
20902     {
20903         if(this.isInline) {
20904             return;
20905         }
20906         
20907         this.picker().removeClass(['bottom', 'top']);
20908         
20909         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20910             /*
20911              * place to the top of element!
20912              *
20913              */
20914             
20915             this.picker().addClass('top');
20916             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20917             
20918             return;
20919         }
20920         
20921         this.picker().addClass('bottom');
20922         
20923         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20924     },
20925     
20926     onFocus : function()
20927     {
20928         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20929         this.show();
20930     },
20931     
20932     onBlur : function()
20933     {
20934         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20935         
20936         var d = this.inputEl().getValue();
20937         
20938         this.setValue(d);
20939                 
20940         this.hide();
20941     },
20942     
20943     show : function()
20944     {
20945         this.picker().show();
20946         this.picker().select('>.datepicker-months', true).first().show();
20947         this.update();
20948         this.place();
20949         
20950         this.fireEvent('show', this, this.date);
20951     },
20952     
20953     hide : function()
20954     {
20955         if(this.isInline) {
20956             return;
20957         }
20958         this.picker().hide();
20959         this.fireEvent('hide', this, this.date);
20960         
20961     },
20962     
20963     onMousedown: function(e)
20964     {
20965         e.stopPropagation();
20966         e.preventDefault();
20967     },
20968     
20969     keyup: function(e)
20970     {
20971         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20972         this.update();
20973     },
20974
20975     fireKey: function(e)
20976     {
20977         if (!this.picker().isVisible()){
20978             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20979                 this.show();
20980             }
20981             return;
20982         }
20983         
20984         var dir;
20985         
20986         switch(e.keyCode){
20987             case 27: // escape
20988                 this.hide();
20989                 e.preventDefault();
20990                 break;
20991             case 37: // left
20992             case 39: // right
20993                 dir = e.keyCode == 37 ? -1 : 1;
20994                 
20995                 this.vIndex = this.vIndex + dir;
20996                 
20997                 if(this.vIndex < 0){
20998                     this.vIndex = 0;
20999                 }
21000                 
21001                 if(this.vIndex > 11){
21002                     this.vIndex = 11;
21003                 }
21004                 
21005                 if(isNaN(this.vIndex)){
21006                     this.vIndex = 0;
21007                 }
21008                 
21009                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21010                 
21011                 break;
21012             case 38: // up
21013             case 40: // down
21014                 
21015                 dir = e.keyCode == 38 ? -1 : 1;
21016                 
21017                 this.vIndex = this.vIndex + dir * 4;
21018                 
21019                 if(this.vIndex < 0){
21020                     this.vIndex = 0;
21021                 }
21022                 
21023                 if(this.vIndex > 11){
21024                     this.vIndex = 11;
21025                 }
21026                 
21027                 if(isNaN(this.vIndex)){
21028                     this.vIndex = 0;
21029                 }
21030                 
21031                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21032                 break;
21033                 
21034             case 13: // enter
21035                 
21036                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21037                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21038                 }
21039                 
21040                 this.hide();
21041                 e.preventDefault();
21042                 break;
21043             case 9: // tab
21044                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21045                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21046                 }
21047                 this.hide();
21048                 break;
21049             case 16: // shift
21050             case 17: // ctrl
21051             case 18: // alt
21052                 break;
21053             default :
21054                 this.hide();
21055                 
21056         }
21057     },
21058     
21059     remove: function() 
21060     {
21061         this.picker().remove();
21062     }
21063    
21064 });
21065
21066 Roo.apply(Roo.bootstrap.MonthField,  {
21067     
21068     content : {
21069         tag: 'tbody',
21070         cn: [
21071         {
21072             tag: 'tr',
21073             cn: [
21074             {
21075                 tag: 'td',
21076                 colspan: '7'
21077             }
21078             ]
21079         }
21080         ]
21081     },
21082     
21083     dates:{
21084         en: {
21085             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21086             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
21087         }
21088     }
21089 });
21090
21091 Roo.apply(Roo.bootstrap.MonthField,  {
21092   
21093     template : {
21094         tag: 'div',
21095         cls: 'datepicker dropdown-menu roo-dynamic',
21096         cn: [
21097             {
21098                 tag: 'div',
21099                 cls: 'datepicker-months',
21100                 cn: [
21101                 {
21102                     tag: 'table',
21103                     cls: 'table-condensed',
21104                     cn:[
21105                         Roo.bootstrap.DateField.content
21106                     ]
21107                 }
21108                 ]
21109             }
21110         ]
21111     }
21112 });
21113
21114  
21115
21116  
21117  /*
21118  * - LGPL
21119  *
21120  * CheckBox
21121  * 
21122  */
21123
21124 /**
21125  * @class Roo.bootstrap.CheckBox
21126  * @extends Roo.bootstrap.Input
21127  * Bootstrap CheckBox class
21128  * 
21129  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
21130  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
21131  * @cfg {String} boxLabel The text that appears beside the checkbox
21132  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
21133  * @cfg {Boolean} checked initnal the element
21134  * @cfg {Boolean} inline inline the element (default false)
21135  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
21136  * @cfg {String} tooltip label tooltip
21137  * 
21138  * @constructor
21139  * Create a new CheckBox
21140  * @param {Object} config The config object
21141  */
21142
21143 Roo.bootstrap.CheckBox = function(config){
21144     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
21145    
21146     this.addEvents({
21147         /**
21148         * @event check
21149         * Fires when the element is checked or unchecked.
21150         * @param {Roo.bootstrap.CheckBox} this This input
21151         * @param {Boolean} checked The new checked value
21152         */
21153        check : true,
21154        /**
21155         * @event click
21156         * Fires when the element is click.
21157         * @param {Roo.bootstrap.CheckBox} this This input
21158         */
21159        click : true
21160     });
21161     
21162 };
21163
21164 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
21165   
21166     inputType: 'checkbox',
21167     inputValue: 1,
21168     valueOff: 0,
21169     boxLabel: false,
21170     checked: false,
21171     weight : false,
21172     inline: false,
21173     tooltip : '',
21174     
21175     // checkbox success does not make any sense really.. 
21176     invalidClass : "",
21177     validClass : "",
21178     
21179     
21180     getAutoCreate : function()
21181     {
21182         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
21183         
21184         var id = Roo.id();
21185         
21186         var cfg = {};
21187         
21188         cfg.cls = 'form-group ' + this.inputType; //input-group
21189         
21190         if(this.inline){
21191             cfg.cls += ' ' + this.inputType + '-inline';
21192         }
21193         
21194         var input =  {
21195             tag: 'input',
21196             id : id,
21197             type : this.inputType,
21198             value : this.inputValue,
21199             cls : 'roo-' + this.inputType, //'form-box',
21200             placeholder : this.placeholder || ''
21201             
21202         };
21203         
21204         if(this.inputType != 'radio'){
21205             var hidden =  {
21206                 tag: 'input',
21207                 type : 'hidden',
21208                 cls : 'roo-hidden-value',
21209                 value : this.checked ? this.inputValue : this.valueOff
21210             };
21211         }
21212         
21213             
21214         if (this.weight) { // Validity check?
21215             cfg.cls += " " + this.inputType + "-" + this.weight;
21216         }
21217         
21218         if (this.disabled) {
21219             input.disabled=true;
21220         }
21221         
21222         if(this.checked){
21223             input.checked = this.checked;
21224         }
21225         
21226         if (this.name) {
21227             
21228             input.name = this.name;
21229             
21230             if(this.inputType != 'radio'){
21231                 hidden.name = this.name;
21232                 input.name = '_hidden_' + this.name;
21233             }
21234         }
21235         
21236         if (this.size) {
21237             input.cls += ' input-' + this.size;
21238         }
21239         
21240         var settings=this;
21241         
21242         ['xs','sm','md','lg'].map(function(size){
21243             if (settings[size]) {
21244                 cfg.cls += ' col-' + size + '-' + settings[size];
21245             }
21246         });
21247         
21248         var inputblock = input;
21249          
21250         if (this.before || this.after) {
21251             
21252             inputblock = {
21253                 cls : 'input-group',
21254                 cn :  [] 
21255             };
21256             
21257             if (this.before) {
21258                 inputblock.cn.push({
21259                     tag :'span',
21260                     cls : 'input-group-addon',
21261                     html : this.before
21262                 });
21263             }
21264             
21265             inputblock.cn.push(input);
21266             
21267             if(this.inputType != 'radio'){
21268                 inputblock.cn.push(hidden);
21269             }
21270             
21271             if (this.after) {
21272                 inputblock.cn.push({
21273                     tag :'span',
21274                     cls : 'input-group-addon',
21275                     html : this.after
21276                 });
21277             }
21278             
21279         }
21280         var boxLabelCfg = false;
21281         
21282         if(this.boxLabel){
21283            
21284             boxLabelCfg = {
21285                 tag: 'label',
21286                 //'for': id, // box label is handled by onclick - so no for...
21287                 cls: 'box-label',
21288                 html: this.boxLabel
21289             };
21290             if(this.tooltip){
21291                 boxLabelCfg.tooltip = this.tooltip;
21292             }
21293              
21294         }
21295         
21296         
21297         if (align ==='left' && this.fieldLabel.length) {
21298 //                Roo.log("left and has label");
21299             cfg.cn = [
21300                 {
21301                     tag: 'label',
21302                     'for' :  id,
21303                     cls : 'control-label',
21304                     html : this.fieldLabel
21305                 },
21306                 {
21307                     cls : "", 
21308                     cn: [
21309                         inputblock
21310                     ]
21311                 }
21312             ];
21313             
21314             if (boxLabelCfg) {
21315                 cfg.cn[1].cn.push(boxLabelCfg);
21316             }
21317             
21318             if(this.labelWidth > 12){
21319                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21320             }
21321             
21322             if(this.labelWidth < 13 && this.labelmd == 0){
21323                 this.labelmd = this.labelWidth;
21324             }
21325             
21326             if(this.labellg > 0){
21327                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21328                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21329             }
21330             
21331             if(this.labelmd > 0){
21332                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21333                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21334             }
21335             
21336             if(this.labelsm > 0){
21337                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21338                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21339             }
21340             
21341             if(this.labelxs > 0){
21342                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21343                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21344             }
21345             
21346         } else if ( this.fieldLabel.length) {
21347 //                Roo.log(" label");
21348                 cfg.cn = [
21349                    
21350                     {
21351                         tag: this.boxLabel ? 'span' : 'label',
21352                         'for': id,
21353                         cls: 'control-label box-input-label',
21354                         //cls : 'input-group-addon',
21355                         html : this.fieldLabel
21356                     },
21357                     
21358                     inputblock
21359                     
21360                 ];
21361                 if (boxLabelCfg) {
21362                     cfg.cn.push(boxLabelCfg);
21363                 }
21364
21365         } else {
21366             
21367 //                Roo.log(" no label && no align");
21368                 cfg.cn = [  inputblock ] ;
21369                 if (boxLabelCfg) {
21370                     cfg.cn.push(boxLabelCfg);
21371                 }
21372
21373                 
21374         }
21375         
21376        
21377         
21378         if(this.inputType != 'radio'){
21379             cfg.cn.push(hidden);
21380         }
21381         
21382         return cfg;
21383         
21384     },
21385     
21386     /**
21387      * return the real input element.
21388      */
21389     inputEl: function ()
21390     {
21391         return this.el.select('input.roo-' + this.inputType,true).first();
21392     },
21393     hiddenEl: function ()
21394     {
21395         return this.el.select('input.roo-hidden-value',true).first();
21396     },
21397     
21398     labelEl: function()
21399     {
21400         return this.el.select('label.control-label',true).first();
21401     },
21402     /* depricated... */
21403     
21404     label: function()
21405     {
21406         return this.labelEl();
21407     },
21408     
21409     boxLabelEl: function()
21410     {
21411         return this.el.select('label.box-label',true).first();
21412     },
21413     
21414     initEvents : function()
21415     {
21416 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21417         
21418         this.inputEl().on('click', this.onClick,  this);
21419         
21420         if (this.boxLabel) { 
21421             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
21422         }
21423         
21424         this.startValue = this.getValue();
21425         
21426         if(this.groupId){
21427             Roo.bootstrap.CheckBox.register(this);
21428         }
21429     },
21430     
21431     onClick : function(e)
21432     {   
21433         if(this.fireEvent('click', this, e) !== false){
21434             this.setChecked(!this.checked);
21435         }
21436         
21437     },
21438     
21439     setChecked : function(state,suppressEvent)
21440     {
21441         this.startValue = this.getValue();
21442
21443         if(this.inputType == 'radio'){
21444             
21445             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21446                 e.dom.checked = false;
21447             });
21448             
21449             this.inputEl().dom.checked = true;
21450             
21451             this.inputEl().dom.value = this.inputValue;
21452             
21453             if(suppressEvent !== true){
21454                 this.fireEvent('check', this, true);
21455             }
21456             
21457             this.validate();
21458             
21459             return;
21460         }
21461         
21462         this.checked = state;
21463         
21464         this.inputEl().dom.checked = state;
21465         
21466         
21467         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21468         
21469         if(suppressEvent !== true){
21470             this.fireEvent('check', this, state);
21471         }
21472         
21473         this.validate();
21474     },
21475     
21476     getValue : function()
21477     {
21478         if(this.inputType == 'radio'){
21479             return this.getGroupValue();
21480         }
21481         
21482         return this.hiddenEl().dom.value;
21483         
21484     },
21485     
21486     getGroupValue : function()
21487     {
21488         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21489             return '';
21490         }
21491         
21492         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21493     },
21494     
21495     setValue : function(v,suppressEvent)
21496     {
21497         if(this.inputType == 'radio'){
21498             this.setGroupValue(v, suppressEvent);
21499             return;
21500         }
21501         
21502         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21503         
21504         this.validate();
21505     },
21506     
21507     setGroupValue : function(v, suppressEvent)
21508     {
21509         this.startValue = this.getValue();
21510         
21511         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21512             e.dom.checked = false;
21513             
21514             if(e.dom.value == v){
21515                 e.dom.checked = true;
21516             }
21517         });
21518         
21519         if(suppressEvent !== true){
21520             this.fireEvent('check', this, true);
21521         }
21522
21523         this.validate();
21524         
21525         return;
21526     },
21527     
21528     validate : function()
21529     {
21530         if(this.getVisibilityEl().hasClass('hidden')){
21531             return true;
21532         }
21533         
21534         if(
21535                 this.disabled || 
21536                 (this.inputType == 'radio' && this.validateRadio()) ||
21537                 (this.inputType == 'checkbox' && this.validateCheckbox())
21538         ){
21539             this.markValid();
21540             return true;
21541         }
21542         
21543         this.markInvalid();
21544         return false;
21545     },
21546     
21547     validateRadio : function()
21548     {
21549         if(this.getVisibilityEl().hasClass('hidden')){
21550             return true;
21551         }
21552         
21553         if(this.allowBlank){
21554             return true;
21555         }
21556         
21557         var valid = false;
21558         
21559         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21560             if(!e.dom.checked){
21561                 return;
21562             }
21563             
21564             valid = true;
21565             
21566             return false;
21567         });
21568         
21569         return valid;
21570     },
21571     
21572     validateCheckbox : function()
21573     {
21574         if(!this.groupId){
21575             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21576             //return (this.getValue() == this.inputValue) ? true : false;
21577         }
21578         
21579         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21580         
21581         if(!group){
21582             return false;
21583         }
21584         
21585         var r = false;
21586         
21587         for(var i in group){
21588             if(group[i].el.isVisible(true)){
21589                 r = false;
21590                 break;
21591             }
21592             
21593             r = true;
21594         }
21595         
21596         for(var i in group){
21597             if(r){
21598                 break;
21599             }
21600             
21601             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21602         }
21603         
21604         return r;
21605     },
21606     
21607     /**
21608      * Mark this field as valid
21609      */
21610     markValid : function()
21611     {
21612         var _this = this;
21613         
21614         this.fireEvent('valid', this);
21615         
21616         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21617         
21618         if(this.groupId){
21619             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21620         }
21621         
21622         if(label){
21623             label.markValid();
21624         }
21625
21626         if(this.inputType == 'radio'){
21627             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21628                 var fg = e.findParent('.form-group', false, true);
21629                 if (Roo.bootstrap.version == 3) {
21630                     fg.removeClass([_this.invalidClass, _this.validClass]);
21631                     fg.addClass(_this.validClass);
21632                 } else {
21633                     fg.removeClass(['is-valid', 'is-invalid']);
21634                     fg.addClass('is-valid');
21635                 }
21636             });
21637             
21638             return;
21639         }
21640
21641         if(!this.groupId){
21642             var fg = this.el.findParent('.form-group', false, true);
21643             if (Roo.bootstrap.version == 3) {
21644                 fg.removeClass([this.invalidClass, this.validClass]);
21645                 fg.addClass(this.validClass);
21646             } else {
21647                 fg.removeClass(['is-valid', 'is-invalid']);
21648                 fg.addClass('is-valid');
21649             }
21650             return;
21651         }
21652         
21653         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21654         
21655         if(!group){
21656             return;
21657         }
21658         
21659         for(var i in group){
21660             var fg = group[i].el.findParent('.form-group', false, true);
21661             if (Roo.bootstrap.version == 3) {
21662                 fg.removeClass([this.invalidClass, this.validClass]);
21663                 fg.addClass(this.validClass);
21664             } else {
21665                 fg.removeClass(['is-valid', 'is-invalid']);
21666                 fg.addClass('is-valid');
21667             }
21668         }
21669     },
21670     
21671      /**
21672      * Mark this field as invalid
21673      * @param {String} msg The validation message
21674      */
21675     markInvalid : function(msg)
21676     {
21677         if(this.allowBlank){
21678             return;
21679         }
21680         
21681         var _this = this;
21682         
21683         this.fireEvent('invalid', this, msg);
21684         
21685         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21686         
21687         if(this.groupId){
21688             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21689         }
21690         
21691         if(label){
21692             label.markInvalid();
21693         }
21694             
21695         if(this.inputType == 'radio'){
21696             
21697             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21698                 var fg = e.findParent('.form-group', false, true);
21699                 if (Roo.bootstrap.version == 3) {
21700                     fg.removeClass([_this.invalidClass, _this.validClass]);
21701                     fg.addClass(_this.invalidClass);
21702                 } else {
21703                     fg.removeClass(['is-invalid', 'is-valid']);
21704                     fg.addClass('is-invalid');
21705                 }
21706             });
21707             
21708             return;
21709         }
21710         
21711         if(!this.groupId){
21712             var fg = this.el.findParent('.form-group', false, true);
21713             if (Roo.bootstrap.version == 3) {
21714                 fg.removeClass([_this.invalidClass, _this.validClass]);
21715                 fg.addClass(_this.invalidClass);
21716             } else {
21717                 fg.removeClass(['is-invalid', 'is-valid']);
21718                 fg.addClass('is-invalid');
21719             }
21720             return;
21721         }
21722         
21723         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21724         
21725         if(!group){
21726             return;
21727         }
21728         
21729         for(var i in group){
21730             var fg = group[i].el.findParent('.form-group', false, true);
21731             if (Roo.bootstrap.version == 3) {
21732                 fg.removeClass([_this.invalidClass, _this.validClass]);
21733                 fg.addClass(_this.invalidClass);
21734             } else {
21735                 fg.removeClass(['is-invalid', 'is-valid']);
21736                 fg.addClass('is-invalid');
21737             }
21738         }
21739         
21740     },
21741     
21742     clearInvalid : function()
21743     {
21744         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21745         
21746         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21747         
21748         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21749         
21750         if (label && label.iconEl) {
21751             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21752             label.iconEl.removeClass(['is-invalid', 'is-valid']);
21753         }
21754     },
21755     
21756     disable : function()
21757     {
21758         if(this.inputType != 'radio'){
21759             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21760             return;
21761         }
21762         
21763         var _this = this;
21764         
21765         if(this.rendered){
21766             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21767                 _this.getActionEl().addClass(this.disabledClass);
21768                 e.dom.disabled = true;
21769             });
21770         }
21771         
21772         this.disabled = true;
21773         this.fireEvent("disable", this);
21774         return this;
21775     },
21776
21777     enable : function()
21778     {
21779         if(this.inputType != 'radio'){
21780             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21781             return;
21782         }
21783         
21784         var _this = this;
21785         
21786         if(this.rendered){
21787             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21788                 _this.getActionEl().removeClass(this.disabledClass);
21789                 e.dom.disabled = false;
21790             });
21791         }
21792         
21793         this.disabled = false;
21794         this.fireEvent("enable", this);
21795         return this;
21796     },
21797     
21798     setBoxLabel : function(v)
21799     {
21800         this.boxLabel = v;
21801         
21802         if(this.rendered){
21803             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21804         }
21805     }
21806
21807 });
21808
21809 Roo.apply(Roo.bootstrap.CheckBox, {
21810     
21811     groups: {},
21812     
21813      /**
21814     * register a CheckBox Group
21815     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21816     */
21817     register : function(checkbox)
21818     {
21819         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21820             this.groups[checkbox.groupId] = {};
21821         }
21822         
21823         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21824             return;
21825         }
21826         
21827         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21828         
21829     },
21830     /**
21831     * fetch a CheckBox Group based on the group ID
21832     * @param {string} the group ID
21833     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21834     */
21835     get: function(groupId) {
21836         if (typeof(this.groups[groupId]) == 'undefined') {
21837             return false;
21838         }
21839         
21840         return this.groups[groupId] ;
21841     }
21842     
21843     
21844 });
21845 /*
21846  * - LGPL
21847  *
21848  * RadioItem
21849  * 
21850  */
21851
21852 /**
21853  * @class Roo.bootstrap.Radio
21854  * @extends Roo.bootstrap.Component
21855  * Bootstrap Radio class
21856  * @cfg {String} boxLabel - the label associated
21857  * @cfg {String} value - the value of radio
21858  * 
21859  * @constructor
21860  * Create a new Radio
21861  * @param {Object} config The config object
21862  */
21863 Roo.bootstrap.Radio = function(config){
21864     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21865     
21866 };
21867
21868 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21869     
21870     boxLabel : '',
21871     
21872     value : '',
21873     
21874     getAutoCreate : function()
21875     {
21876         var cfg = {
21877             tag : 'div',
21878             cls : 'form-group radio',
21879             cn : [
21880                 {
21881                     tag : 'label',
21882                     cls : 'box-label',
21883                     html : this.boxLabel
21884                 }
21885             ]
21886         };
21887         
21888         return cfg;
21889     },
21890     
21891     initEvents : function() 
21892     {
21893         this.parent().register(this);
21894         
21895         this.el.on('click', this.onClick, this);
21896         
21897     },
21898     
21899     onClick : function(e)
21900     {
21901         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21902             this.setChecked(true);
21903         }
21904     },
21905     
21906     setChecked : function(state, suppressEvent)
21907     {
21908         this.parent().setValue(this.value, suppressEvent);
21909         
21910     },
21911     
21912     setBoxLabel : function(v)
21913     {
21914         this.boxLabel = v;
21915         
21916         if(this.rendered){
21917             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21918         }
21919     }
21920     
21921 });
21922  
21923
21924  /*
21925  * - LGPL
21926  *
21927  * Input
21928  * 
21929  */
21930
21931 /**
21932  * @class Roo.bootstrap.SecurePass
21933  * @extends Roo.bootstrap.Input
21934  * Bootstrap SecurePass class
21935  *
21936  * 
21937  * @constructor
21938  * Create a new SecurePass
21939  * @param {Object} config The config object
21940  */
21941  
21942 Roo.bootstrap.SecurePass = function (config) {
21943     // these go here, so the translation tool can replace them..
21944     this.errors = {
21945         PwdEmpty: "Please type a password, and then retype it to confirm.",
21946         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21947         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21948         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21949         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21950         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21951         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21952         TooWeak: "Your password is Too Weak."
21953     },
21954     this.meterLabel = "Password strength:";
21955     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21956     this.meterClass = [
21957         "roo-password-meter-tooweak", 
21958         "roo-password-meter-weak", 
21959         "roo-password-meter-medium", 
21960         "roo-password-meter-strong", 
21961         "roo-password-meter-grey"
21962     ];
21963     
21964     this.errors = {};
21965     
21966     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21967 }
21968
21969 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21970     /**
21971      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21972      * {
21973      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21974      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21975      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21976      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21977      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21978      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21979      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21980      * })
21981      */
21982     // private
21983     
21984     meterWidth: 300,
21985     errorMsg :'',    
21986     errors: false,
21987     imageRoot: '/',
21988     /**
21989      * @cfg {String/Object} Label for the strength meter (defaults to
21990      * 'Password strength:')
21991      */
21992     // private
21993     meterLabel: '',
21994     /**
21995      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21996      * ['Weak', 'Medium', 'Strong'])
21997      */
21998     // private    
21999     pwdStrengths: false,    
22000     // private
22001     strength: 0,
22002     // private
22003     _lastPwd: null,
22004     // private
22005     kCapitalLetter: 0,
22006     kSmallLetter: 1,
22007     kDigit: 2,
22008     kPunctuation: 3,
22009     
22010     insecure: false,
22011     // private
22012     initEvents: function ()
22013     {
22014         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
22015
22016         if (this.el.is('input[type=password]') && Roo.isSafari) {
22017             this.el.on('keydown', this.SafariOnKeyDown, this);
22018         }
22019
22020         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
22021     },
22022     // private
22023     onRender: function (ct, position)
22024     {
22025         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
22026         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
22027         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
22028
22029         this.trigger.createChild({
22030                    cn: [
22031                     {
22032                     //id: 'PwdMeter',
22033                     tag: 'div',
22034                     cls: 'roo-password-meter-grey col-xs-12',
22035                     style: {
22036                         //width: 0,
22037                         //width: this.meterWidth + 'px'                                                
22038                         }
22039                     },
22040                     {                            
22041                          cls: 'roo-password-meter-text'                          
22042                     }
22043                 ]            
22044         });
22045
22046          
22047         if (this.hideTrigger) {
22048             this.trigger.setDisplayed(false);
22049         }
22050         this.setSize(this.width || '', this.height || '');
22051     },
22052     // private
22053     onDestroy: function ()
22054     {
22055         if (this.trigger) {
22056             this.trigger.removeAllListeners();
22057             this.trigger.remove();
22058         }
22059         if (this.wrap) {
22060             this.wrap.remove();
22061         }
22062         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
22063     },
22064     // private
22065     checkStrength: function ()
22066     {
22067         var pwd = this.inputEl().getValue();
22068         if (pwd == this._lastPwd) {
22069             return;
22070         }
22071
22072         var strength;
22073         if (this.ClientSideStrongPassword(pwd)) {
22074             strength = 3;
22075         } else if (this.ClientSideMediumPassword(pwd)) {
22076             strength = 2;
22077         } else if (this.ClientSideWeakPassword(pwd)) {
22078             strength = 1;
22079         } else {
22080             strength = 0;
22081         }
22082         
22083         Roo.log('strength1: ' + strength);
22084         
22085         //var pm = this.trigger.child('div/div/div').dom;
22086         var pm = this.trigger.child('div/div');
22087         pm.removeClass(this.meterClass);
22088         pm.addClass(this.meterClass[strength]);
22089                 
22090         
22091         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
22092                 
22093         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
22094         
22095         this._lastPwd = pwd;
22096     },
22097     reset: function ()
22098     {
22099         Roo.bootstrap.SecurePass.superclass.reset.call(this);
22100         
22101         this._lastPwd = '';
22102         
22103         var pm = this.trigger.child('div/div');
22104         pm.removeClass(this.meterClass);
22105         pm.addClass('roo-password-meter-grey');        
22106         
22107         
22108         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
22109         
22110         pt.innerHTML = '';
22111         this.inputEl().dom.type='password';
22112     },
22113     // private
22114     validateValue: function (value)
22115     {
22116         
22117         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
22118             return false;
22119         }
22120         if (value.length == 0) {
22121             if (this.allowBlank) {
22122                 this.clearInvalid();
22123                 return true;
22124             }
22125
22126             this.markInvalid(this.errors.PwdEmpty);
22127             this.errorMsg = this.errors.PwdEmpty;
22128             return false;
22129         }
22130         
22131         if(this.insecure){
22132             return true;
22133         }
22134         
22135         if ('[\x21-\x7e]*'.match(value)) {
22136             this.markInvalid(this.errors.PwdBadChar);
22137             this.errorMsg = this.errors.PwdBadChar;
22138             return false;
22139         }
22140         if (value.length < 6) {
22141             this.markInvalid(this.errors.PwdShort);
22142             this.errorMsg = this.errors.PwdShort;
22143             return false;
22144         }
22145         if (value.length > 16) {
22146             this.markInvalid(this.errors.PwdLong);
22147             this.errorMsg = this.errors.PwdLong;
22148             return false;
22149         }
22150         var strength;
22151         if (this.ClientSideStrongPassword(value)) {
22152             strength = 3;
22153         } else if (this.ClientSideMediumPassword(value)) {
22154             strength = 2;
22155         } else if (this.ClientSideWeakPassword(value)) {
22156             strength = 1;
22157         } else {
22158             strength = 0;
22159         }
22160
22161         
22162         if (strength < 2) {
22163             //this.markInvalid(this.errors.TooWeak);
22164             this.errorMsg = this.errors.TooWeak;
22165             //return false;
22166         }
22167         
22168         
22169         console.log('strength2: ' + strength);
22170         
22171         //var pm = this.trigger.child('div/div/div').dom;
22172         
22173         var pm = this.trigger.child('div/div');
22174         pm.removeClass(this.meterClass);
22175         pm.addClass(this.meterClass[strength]);
22176                 
22177         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
22178                 
22179         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
22180         
22181         this.errorMsg = ''; 
22182         return true;
22183     },
22184     // private
22185     CharacterSetChecks: function (type)
22186     {
22187         this.type = type;
22188         this.fResult = false;
22189     },
22190     // private
22191     isctype: function (character, type)
22192     {
22193         switch (type) {  
22194             case this.kCapitalLetter:
22195                 if (character >= 'A' && character <= 'Z') {
22196                     return true;
22197                 }
22198                 break;
22199             
22200             case this.kSmallLetter:
22201                 if (character >= 'a' && character <= 'z') {
22202                     return true;
22203                 }
22204                 break;
22205             
22206             case this.kDigit:
22207                 if (character >= '0' && character <= '9') {
22208                     return true;
22209                 }
22210                 break;
22211             
22212             case this.kPunctuation:
22213                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
22214                     return true;
22215                 }
22216                 break;
22217             
22218             default:
22219                 return false;
22220         }
22221
22222     },
22223     // private
22224     IsLongEnough: function (pwd, size)
22225     {
22226         return !(pwd == null || isNaN(size) || pwd.length < size);
22227     },
22228     // private
22229     SpansEnoughCharacterSets: function (word, nb)
22230     {
22231         if (!this.IsLongEnough(word, nb))
22232         {
22233             return false;
22234         }
22235
22236         var characterSetChecks = new Array(
22237             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
22238             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
22239         );
22240         
22241         for (var index = 0; index < word.length; ++index) {
22242             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22243                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
22244                     characterSetChecks[nCharSet].fResult = true;
22245                     break;
22246                 }
22247             }
22248         }
22249
22250         var nCharSets = 0;
22251         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22252             if (characterSetChecks[nCharSet].fResult) {
22253                 ++nCharSets;
22254             }
22255         }
22256
22257         if (nCharSets < nb) {
22258             return false;
22259         }
22260         return true;
22261     },
22262     // private
22263     ClientSideStrongPassword: function (pwd)
22264     {
22265         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
22266     },
22267     // private
22268     ClientSideMediumPassword: function (pwd)
22269     {
22270         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
22271     },
22272     // private
22273     ClientSideWeakPassword: function (pwd)
22274     {
22275         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
22276     }
22277           
22278 })//<script type="text/javascript">
22279
22280 /*
22281  * Based  Ext JS Library 1.1.1
22282  * Copyright(c) 2006-2007, Ext JS, LLC.
22283  * LGPL
22284  *
22285  */
22286  
22287 /**
22288  * @class Roo.HtmlEditorCore
22289  * @extends Roo.Component
22290  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
22291  *
22292  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22293  */
22294
22295 Roo.HtmlEditorCore = function(config){
22296     
22297     
22298     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22299     
22300     
22301     this.addEvents({
22302         /**
22303          * @event initialize
22304          * Fires when the editor is fully initialized (including the iframe)
22305          * @param {Roo.HtmlEditorCore} this
22306          */
22307         initialize: true,
22308         /**
22309          * @event activate
22310          * Fires when the editor is first receives the focus. Any insertion must wait
22311          * until after this event.
22312          * @param {Roo.HtmlEditorCore} this
22313          */
22314         activate: true,
22315          /**
22316          * @event beforesync
22317          * Fires before the textarea is updated with content from the editor iframe. Return false
22318          * to cancel the sync.
22319          * @param {Roo.HtmlEditorCore} this
22320          * @param {String} html
22321          */
22322         beforesync: true,
22323          /**
22324          * @event beforepush
22325          * Fires before the iframe editor is updated with content from the textarea. Return false
22326          * to cancel the push.
22327          * @param {Roo.HtmlEditorCore} this
22328          * @param {String} html
22329          */
22330         beforepush: true,
22331          /**
22332          * @event sync
22333          * Fires when the textarea is updated with content from the editor iframe.
22334          * @param {Roo.HtmlEditorCore} this
22335          * @param {String} html
22336          */
22337         sync: true,
22338          /**
22339          * @event push
22340          * Fires when the iframe editor is updated with content from the textarea.
22341          * @param {Roo.HtmlEditorCore} this
22342          * @param {String} html
22343          */
22344         push: true,
22345         
22346         /**
22347          * @event editorevent
22348          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22349          * @param {Roo.HtmlEditorCore} this
22350          */
22351         editorevent: true
22352         
22353     });
22354     
22355     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22356     
22357     // defaults : white / black...
22358     this.applyBlacklists();
22359     
22360     
22361     
22362 };
22363
22364
22365 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
22366
22367
22368      /**
22369      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
22370      */
22371     
22372     owner : false,
22373     
22374      /**
22375      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22376      *                        Roo.resizable.
22377      */
22378     resizable : false,
22379      /**
22380      * @cfg {Number} height (in pixels)
22381      */   
22382     height: 300,
22383    /**
22384      * @cfg {Number} width (in pixels)
22385      */   
22386     width: 500,
22387     
22388     /**
22389      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22390      * 
22391      */
22392     stylesheets: false,
22393     
22394     // id of frame..
22395     frameId: false,
22396     
22397     // private properties
22398     validationEvent : false,
22399     deferHeight: true,
22400     initialized : false,
22401     activated : false,
22402     sourceEditMode : false,
22403     onFocus : Roo.emptyFn,
22404     iframePad:3,
22405     hideMode:'offsets',
22406     
22407     clearUp: true,
22408     
22409     // blacklist + whitelisted elements..
22410     black: false,
22411     white: false,
22412      
22413     bodyCls : '',
22414
22415     /**
22416      * Protected method that will not generally be called directly. It
22417      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22418      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22419      */
22420     getDocMarkup : function(){
22421         // body styles..
22422         var st = '';
22423         
22424         // inherit styels from page...?? 
22425         if (this.stylesheets === false) {
22426             
22427             Roo.get(document.head).select('style').each(function(node) {
22428                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22429             });
22430             
22431             Roo.get(document.head).select('link').each(function(node) { 
22432                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22433             });
22434             
22435         } else if (!this.stylesheets.length) {
22436                 // simple..
22437                 st = '<style type="text/css">' +
22438                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22439                    '</style>';
22440         } else { 
22441             st = '<style type="text/css">' +
22442                     this.stylesheets +
22443                 '</style>';
22444         }
22445         
22446         st +=  '<style type="text/css">' +
22447             'IMG { cursor: pointer } ' +
22448         '</style>';
22449
22450         var cls = 'roo-htmleditor-body';
22451         
22452         if(this.bodyCls.length){
22453             cls += ' ' + this.bodyCls;
22454         }
22455         
22456         return '<html><head>' + st  +
22457             //<style type="text/css">' +
22458             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22459             //'</style>' +
22460             ' </head><body class="' +  cls + '"></body></html>';
22461     },
22462
22463     // private
22464     onRender : function(ct, position)
22465     {
22466         var _t = this;
22467         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22468         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22469         
22470         
22471         this.el.dom.style.border = '0 none';
22472         this.el.dom.setAttribute('tabIndex', -1);
22473         this.el.addClass('x-hidden hide');
22474         
22475         
22476         
22477         if(Roo.isIE){ // fix IE 1px bogus margin
22478             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22479         }
22480        
22481         
22482         this.frameId = Roo.id();
22483         
22484          
22485         
22486         var iframe = this.owner.wrap.createChild({
22487             tag: 'iframe',
22488             cls: 'form-control', // bootstrap..
22489             id: this.frameId,
22490             name: this.frameId,
22491             frameBorder : 'no',
22492             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22493         }, this.el
22494         );
22495         
22496         
22497         this.iframe = iframe.dom;
22498
22499          this.assignDocWin();
22500         
22501         this.doc.designMode = 'on';
22502        
22503         this.doc.open();
22504         this.doc.write(this.getDocMarkup());
22505         this.doc.close();
22506
22507         
22508         var task = { // must defer to wait for browser to be ready
22509             run : function(){
22510                 //console.log("run task?" + this.doc.readyState);
22511                 this.assignDocWin();
22512                 if(this.doc.body || this.doc.readyState == 'complete'){
22513                     try {
22514                         this.doc.designMode="on";
22515                     } catch (e) {
22516                         return;
22517                     }
22518                     Roo.TaskMgr.stop(task);
22519                     this.initEditor.defer(10, this);
22520                 }
22521             },
22522             interval : 10,
22523             duration: 10000,
22524             scope: this
22525         };
22526         Roo.TaskMgr.start(task);
22527
22528     },
22529
22530     // private
22531     onResize : function(w, h)
22532     {
22533          Roo.log('resize: ' +w + ',' + h );
22534         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22535         if(!this.iframe){
22536             return;
22537         }
22538         if(typeof w == 'number'){
22539             
22540             this.iframe.style.width = w + 'px';
22541         }
22542         if(typeof h == 'number'){
22543             
22544             this.iframe.style.height = h + 'px';
22545             if(this.doc){
22546                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22547             }
22548         }
22549         
22550     },
22551
22552     /**
22553      * Toggles the editor between standard and source edit mode.
22554      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22555      */
22556     toggleSourceEdit : function(sourceEditMode){
22557         
22558         this.sourceEditMode = sourceEditMode === true;
22559         
22560         if(this.sourceEditMode){
22561  
22562             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22563             
22564         }else{
22565             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22566             //this.iframe.className = '';
22567             this.deferFocus();
22568         }
22569         //this.setSize(this.owner.wrap.getSize());
22570         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22571     },
22572
22573     
22574   
22575
22576     /**
22577      * Protected method that will not generally be called directly. If you need/want
22578      * custom HTML cleanup, this is the method you should override.
22579      * @param {String} html The HTML to be cleaned
22580      * return {String} The cleaned HTML
22581      */
22582     cleanHtml : function(html){
22583         html = String(html);
22584         if(html.length > 5){
22585             if(Roo.isSafari){ // strip safari nonsense
22586                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22587             }
22588         }
22589         if(html == '&nbsp;'){
22590             html = '';
22591         }
22592         return html;
22593     },
22594
22595     /**
22596      * HTML Editor -> Textarea
22597      * Protected method that will not generally be called directly. Syncs the contents
22598      * of the editor iframe with the textarea.
22599      */
22600     syncValue : function(){
22601         if(this.initialized){
22602             var bd = (this.doc.body || this.doc.documentElement);
22603             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22604             var html = bd.innerHTML;
22605             if(Roo.isSafari){
22606                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22607                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22608                 if(m && m[1]){
22609                     html = '<div style="'+m[0]+'">' + html + '</div>';
22610                 }
22611             }
22612             html = this.cleanHtml(html);
22613             // fix up the special chars.. normaly like back quotes in word...
22614             // however we do not want to do this with chinese..
22615             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
22616                 
22617                 var cc = match.charCodeAt();
22618
22619                 // Get the character value, handling surrogate pairs
22620                 if (match.length == 2) {
22621                     // It's a surrogate pair, calculate the Unicode code point
22622                     var high = match.charCodeAt(0) - 0xD800;
22623                     var low  = match.charCodeAt(1) - 0xDC00;
22624                     cc = (high * 0x400) + low + 0x10000;
22625                 }  else if (
22626                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22627                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22628                     (cc >= 0xf900 && cc < 0xfb00 )
22629                 ) {
22630                         return match;
22631                 }  
22632          
22633                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
22634                 return "&#" + cc + ";";
22635                 
22636                 
22637             });
22638             
22639             
22640              
22641             if(this.owner.fireEvent('beforesync', this, html) !== false){
22642                 this.el.dom.value = html;
22643                 this.owner.fireEvent('sync', this, html);
22644             }
22645         }
22646     },
22647
22648     /**
22649      * Protected method that will not generally be called directly. Pushes the value of the textarea
22650      * into the iframe editor.
22651      */
22652     pushValue : function(){
22653         if(this.initialized){
22654             var v = this.el.dom.value.trim();
22655             
22656 //            if(v.length < 1){
22657 //                v = '&#160;';
22658 //            }
22659             
22660             if(this.owner.fireEvent('beforepush', this, v) !== false){
22661                 var d = (this.doc.body || this.doc.documentElement);
22662                 d.innerHTML = v;
22663                 this.cleanUpPaste();
22664                 this.el.dom.value = d.innerHTML;
22665                 this.owner.fireEvent('push', this, v);
22666             }
22667         }
22668     },
22669
22670     // private
22671     deferFocus : function(){
22672         this.focus.defer(10, this);
22673     },
22674
22675     // doc'ed in Field
22676     focus : function(){
22677         if(this.win && !this.sourceEditMode){
22678             this.win.focus();
22679         }else{
22680             this.el.focus();
22681         }
22682     },
22683     
22684     assignDocWin: function()
22685     {
22686         var iframe = this.iframe;
22687         
22688          if(Roo.isIE){
22689             this.doc = iframe.contentWindow.document;
22690             this.win = iframe.contentWindow;
22691         } else {
22692 //            if (!Roo.get(this.frameId)) {
22693 //                return;
22694 //            }
22695 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22696 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22697             
22698             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22699                 return;
22700             }
22701             
22702             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22703             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22704         }
22705     },
22706     
22707     // private
22708     initEditor : function(){
22709         //console.log("INIT EDITOR");
22710         this.assignDocWin();
22711         
22712         
22713         
22714         this.doc.designMode="on";
22715         this.doc.open();
22716         this.doc.write(this.getDocMarkup());
22717         this.doc.close();
22718         
22719         var dbody = (this.doc.body || this.doc.documentElement);
22720         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22721         // this copies styles from the containing element into thsi one..
22722         // not sure why we need all of this..
22723         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22724         
22725         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22726         //ss['background-attachment'] = 'fixed'; // w3c
22727         dbody.bgProperties = 'fixed'; // ie
22728         //Roo.DomHelper.applyStyles(dbody, ss);
22729         Roo.EventManager.on(this.doc, {
22730             //'mousedown': this.onEditorEvent,
22731             'mouseup': this.onEditorEvent,
22732             'dblclick': this.onEditorEvent,
22733             'click': this.onEditorEvent,
22734             'keyup': this.onEditorEvent,
22735             buffer:100,
22736             scope: this
22737         });
22738         if(Roo.isGecko){
22739             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22740         }
22741         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22742             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22743         }
22744         this.initialized = true;
22745
22746         this.owner.fireEvent('initialize', this);
22747         this.pushValue();
22748     },
22749
22750     // private
22751     onDestroy : function(){
22752         
22753         
22754         
22755         if(this.rendered){
22756             
22757             //for (var i =0; i < this.toolbars.length;i++) {
22758             //    // fixme - ask toolbars for heights?
22759             //    this.toolbars[i].onDestroy();
22760            // }
22761             
22762             //this.wrap.dom.innerHTML = '';
22763             //this.wrap.remove();
22764         }
22765     },
22766
22767     // private
22768     onFirstFocus : function(){
22769         
22770         this.assignDocWin();
22771         
22772         
22773         this.activated = true;
22774          
22775     
22776         if(Roo.isGecko){ // prevent silly gecko errors
22777             this.win.focus();
22778             var s = this.win.getSelection();
22779             if(!s.focusNode || s.focusNode.nodeType != 3){
22780                 var r = s.getRangeAt(0);
22781                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22782                 r.collapse(true);
22783                 this.deferFocus();
22784             }
22785             try{
22786                 this.execCmd('useCSS', true);
22787                 this.execCmd('styleWithCSS', false);
22788             }catch(e){}
22789         }
22790         this.owner.fireEvent('activate', this);
22791     },
22792
22793     // private
22794     adjustFont: function(btn){
22795         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22796         //if(Roo.isSafari){ // safari
22797         //    adjust *= 2;
22798        // }
22799         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22800         if(Roo.isSafari){ // safari
22801             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22802             v =  (v < 10) ? 10 : v;
22803             v =  (v > 48) ? 48 : v;
22804             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22805             
22806         }
22807         
22808         
22809         v = Math.max(1, v+adjust);
22810         
22811         this.execCmd('FontSize', v  );
22812     },
22813
22814     onEditorEvent : function(e)
22815     {
22816         this.owner.fireEvent('editorevent', this, e);
22817       //  this.updateToolbar();
22818         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22819     },
22820
22821     insertTag : function(tg)
22822     {
22823         // could be a bit smarter... -> wrap the current selected tRoo..
22824         if (tg.toLowerCase() == 'span' ||
22825             tg.toLowerCase() == 'code' ||
22826             tg.toLowerCase() == 'sup' ||
22827             tg.toLowerCase() == 'sub' 
22828             ) {
22829             
22830             range = this.createRange(this.getSelection());
22831             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22832             wrappingNode.appendChild(range.extractContents());
22833             range.insertNode(wrappingNode);
22834
22835             return;
22836             
22837             
22838             
22839         }
22840         this.execCmd("formatblock",   tg);
22841         
22842     },
22843     
22844     insertText : function(txt)
22845     {
22846         
22847         
22848         var range = this.createRange();
22849         range.deleteContents();
22850                //alert(Sender.getAttribute('label'));
22851                
22852         range.insertNode(this.doc.createTextNode(txt));
22853     } ,
22854     
22855      
22856
22857     /**
22858      * Executes a Midas editor command on the editor document and performs necessary focus and
22859      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22860      * @param {String} cmd The Midas command
22861      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22862      */
22863     relayCmd : function(cmd, value){
22864         this.win.focus();
22865         this.execCmd(cmd, value);
22866         this.owner.fireEvent('editorevent', this);
22867         //this.updateToolbar();
22868         this.owner.deferFocus();
22869     },
22870
22871     /**
22872      * Executes a Midas editor command directly on the editor document.
22873      * For visual commands, you should use {@link #relayCmd} instead.
22874      * <b>This should only be called after the editor is initialized.</b>
22875      * @param {String} cmd The Midas command
22876      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22877      */
22878     execCmd : function(cmd, value){
22879         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22880         this.syncValue();
22881     },
22882  
22883  
22884    
22885     /**
22886      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22887      * to insert tRoo.
22888      * @param {String} text | dom node.. 
22889      */
22890     insertAtCursor : function(text)
22891     {
22892         
22893         if(!this.activated){
22894             return;
22895         }
22896         /*
22897         if(Roo.isIE){
22898             this.win.focus();
22899             var r = this.doc.selection.createRange();
22900             if(r){
22901                 r.collapse(true);
22902                 r.pasteHTML(text);
22903                 this.syncValue();
22904                 this.deferFocus();
22905             
22906             }
22907             return;
22908         }
22909         */
22910         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22911             this.win.focus();
22912             
22913             
22914             // from jquery ui (MIT licenced)
22915             var range, node;
22916             var win = this.win;
22917             
22918             if (win.getSelection && win.getSelection().getRangeAt) {
22919                 range = win.getSelection().getRangeAt(0);
22920                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22921                 range.insertNode(node);
22922             } else if (win.document.selection && win.document.selection.createRange) {
22923                 // no firefox support
22924                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22925                 win.document.selection.createRange().pasteHTML(txt);
22926             } else {
22927                 // no firefox support
22928                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22929                 this.execCmd('InsertHTML', txt);
22930             } 
22931             
22932             this.syncValue();
22933             
22934             this.deferFocus();
22935         }
22936     },
22937  // private
22938     mozKeyPress : function(e){
22939         if(e.ctrlKey){
22940             var c = e.getCharCode(), cmd;
22941           
22942             if(c > 0){
22943                 c = String.fromCharCode(c).toLowerCase();
22944                 switch(c){
22945                     case 'b':
22946                         cmd = 'bold';
22947                         break;
22948                     case 'i':
22949                         cmd = 'italic';
22950                         break;
22951                     
22952                     case 'u':
22953                         cmd = 'underline';
22954                         break;
22955                     
22956                     case 'v':
22957                         this.cleanUpPaste.defer(100, this);
22958                         return;
22959                         
22960                 }
22961                 if(cmd){
22962                     this.win.focus();
22963                     this.execCmd(cmd);
22964                     this.deferFocus();
22965                     e.preventDefault();
22966                 }
22967                 
22968             }
22969         }
22970     },
22971
22972     // private
22973     fixKeys : function(){ // load time branching for fastest keydown performance
22974         if(Roo.isIE){
22975             return function(e){
22976                 var k = e.getKey(), r;
22977                 if(k == e.TAB){
22978                     e.stopEvent();
22979                     r = this.doc.selection.createRange();
22980                     if(r){
22981                         r.collapse(true);
22982                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22983                         this.deferFocus();
22984                     }
22985                     return;
22986                 }
22987                 
22988                 if(k == e.ENTER){
22989                     r = this.doc.selection.createRange();
22990                     if(r){
22991                         var target = r.parentElement();
22992                         if(!target || target.tagName.toLowerCase() != 'li'){
22993                             e.stopEvent();
22994                             r.pasteHTML('<br />');
22995                             r.collapse(false);
22996                             r.select();
22997                         }
22998                     }
22999                 }
23000                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23001                     this.cleanUpPaste.defer(100, this);
23002                     return;
23003                 }
23004                 
23005                 
23006             };
23007         }else if(Roo.isOpera){
23008             return function(e){
23009                 var k = e.getKey();
23010                 if(k == e.TAB){
23011                     e.stopEvent();
23012                     this.win.focus();
23013                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
23014                     this.deferFocus();
23015                 }
23016                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23017                     this.cleanUpPaste.defer(100, this);
23018                     return;
23019                 }
23020                 
23021             };
23022         }else if(Roo.isSafari){
23023             return function(e){
23024                 var k = e.getKey();
23025                 
23026                 if(k == e.TAB){
23027                     e.stopEvent();
23028                     this.execCmd('InsertText','\t');
23029                     this.deferFocus();
23030                     return;
23031                 }
23032                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23033                     this.cleanUpPaste.defer(100, this);
23034                     return;
23035                 }
23036                 
23037              };
23038         }
23039     }(),
23040     
23041     getAllAncestors: function()
23042     {
23043         var p = this.getSelectedNode();
23044         var a = [];
23045         if (!p) {
23046             a.push(p); // push blank onto stack..
23047             p = this.getParentElement();
23048         }
23049         
23050         
23051         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
23052             a.push(p);
23053             p = p.parentNode;
23054         }
23055         a.push(this.doc.body);
23056         return a;
23057     },
23058     lastSel : false,
23059     lastSelNode : false,
23060     
23061     
23062     getSelection : function() 
23063     {
23064         this.assignDocWin();
23065         return Roo.isIE ? this.doc.selection : this.win.getSelection();
23066     },
23067     
23068     getSelectedNode: function() 
23069     {
23070         // this may only work on Gecko!!!
23071         
23072         // should we cache this!!!!
23073         
23074         
23075         
23076          
23077         var range = this.createRange(this.getSelection()).cloneRange();
23078         
23079         if (Roo.isIE) {
23080             var parent = range.parentElement();
23081             while (true) {
23082                 var testRange = range.duplicate();
23083                 testRange.moveToElementText(parent);
23084                 if (testRange.inRange(range)) {
23085                     break;
23086                 }
23087                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
23088                     break;
23089                 }
23090                 parent = parent.parentElement;
23091             }
23092             return parent;
23093         }
23094         
23095         // is ancestor a text element.
23096         var ac =  range.commonAncestorContainer;
23097         if (ac.nodeType == 3) {
23098             ac = ac.parentNode;
23099         }
23100         
23101         var ar = ac.childNodes;
23102          
23103         var nodes = [];
23104         var other_nodes = [];
23105         var has_other_nodes = false;
23106         for (var i=0;i<ar.length;i++) {
23107             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
23108                 continue;
23109             }
23110             // fullly contained node.
23111             
23112             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
23113                 nodes.push(ar[i]);
23114                 continue;
23115             }
23116             
23117             // probably selected..
23118             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
23119                 other_nodes.push(ar[i]);
23120                 continue;
23121             }
23122             // outer..
23123             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
23124                 continue;
23125             }
23126             
23127             
23128             has_other_nodes = true;
23129         }
23130         if (!nodes.length && other_nodes.length) {
23131             nodes= other_nodes;
23132         }
23133         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
23134             return false;
23135         }
23136         
23137         return nodes[0];
23138     },
23139     createRange: function(sel)
23140     {
23141         // this has strange effects when using with 
23142         // top toolbar - not sure if it's a great idea.
23143         //this.editor.contentWindow.focus();
23144         if (typeof sel != "undefined") {
23145             try {
23146                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
23147             } catch(e) {
23148                 return this.doc.createRange();
23149             }
23150         } else {
23151             return this.doc.createRange();
23152         }
23153     },
23154     getParentElement: function()
23155     {
23156         
23157         this.assignDocWin();
23158         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
23159         
23160         var range = this.createRange(sel);
23161          
23162         try {
23163             var p = range.commonAncestorContainer;
23164             while (p.nodeType == 3) { // text node
23165                 p = p.parentNode;
23166             }
23167             return p;
23168         } catch (e) {
23169             return null;
23170         }
23171     
23172     },
23173     /***
23174      *
23175      * Range intersection.. the hard stuff...
23176      *  '-1' = before
23177      *  '0' = hits..
23178      *  '1' = after.
23179      *         [ -- selected range --- ]
23180      *   [fail]                        [fail]
23181      *
23182      *    basically..
23183      *      if end is before start or  hits it. fail.
23184      *      if start is after end or hits it fail.
23185      *
23186      *   if either hits (but other is outside. - then it's not 
23187      *   
23188      *    
23189      **/
23190     
23191     
23192     // @see http://www.thismuchiknow.co.uk/?p=64.
23193     rangeIntersectsNode : function(range, node)
23194     {
23195         var nodeRange = node.ownerDocument.createRange();
23196         try {
23197             nodeRange.selectNode(node);
23198         } catch (e) {
23199             nodeRange.selectNodeContents(node);
23200         }
23201     
23202         var rangeStartRange = range.cloneRange();
23203         rangeStartRange.collapse(true);
23204     
23205         var rangeEndRange = range.cloneRange();
23206         rangeEndRange.collapse(false);
23207     
23208         var nodeStartRange = nodeRange.cloneRange();
23209         nodeStartRange.collapse(true);
23210     
23211         var nodeEndRange = nodeRange.cloneRange();
23212         nodeEndRange.collapse(false);
23213     
23214         return rangeStartRange.compareBoundaryPoints(
23215                  Range.START_TO_START, nodeEndRange) == -1 &&
23216                rangeEndRange.compareBoundaryPoints(
23217                  Range.START_TO_START, nodeStartRange) == 1;
23218         
23219          
23220     },
23221     rangeCompareNode : function(range, node)
23222     {
23223         var nodeRange = node.ownerDocument.createRange();
23224         try {
23225             nodeRange.selectNode(node);
23226         } catch (e) {
23227             nodeRange.selectNodeContents(node);
23228         }
23229         
23230         
23231         range.collapse(true);
23232     
23233         nodeRange.collapse(true);
23234      
23235         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
23236         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
23237          
23238         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
23239         
23240         var nodeIsBefore   =  ss == 1;
23241         var nodeIsAfter    = ee == -1;
23242         
23243         if (nodeIsBefore && nodeIsAfter) {
23244             return 0; // outer
23245         }
23246         if (!nodeIsBefore && nodeIsAfter) {
23247             return 1; //right trailed.
23248         }
23249         
23250         if (nodeIsBefore && !nodeIsAfter) {
23251             return 2;  // left trailed.
23252         }
23253         // fully contined.
23254         return 3;
23255     },
23256
23257     // private? - in a new class?
23258     cleanUpPaste :  function()
23259     {
23260         // cleans up the whole document..
23261         Roo.log('cleanuppaste');
23262         
23263         this.cleanUpChildren(this.doc.body);
23264         var clean = this.cleanWordChars(this.doc.body.innerHTML);
23265         if (clean != this.doc.body.innerHTML) {
23266             this.doc.body.innerHTML = clean;
23267         }
23268         
23269     },
23270     
23271     cleanWordChars : function(input) {// change the chars to hex code
23272         var he = Roo.HtmlEditorCore;
23273         
23274         var output = input;
23275         Roo.each(he.swapCodes, function(sw) { 
23276             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
23277             
23278             output = output.replace(swapper, sw[1]);
23279         });
23280         
23281         return output;
23282     },
23283     
23284     
23285     cleanUpChildren : function (n)
23286     {
23287         if (!n.childNodes.length) {
23288             return;
23289         }
23290         for (var i = n.childNodes.length-1; i > -1 ; i--) {
23291            this.cleanUpChild(n.childNodes[i]);
23292         }
23293     },
23294     
23295     
23296         
23297     
23298     cleanUpChild : function (node)
23299     {
23300         var ed = this;
23301         //console.log(node);
23302         if (node.nodeName == "#text") {
23303             // clean up silly Windows -- stuff?
23304             return; 
23305         }
23306         if (node.nodeName == "#comment") {
23307             node.parentNode.removeChild(node);
23308             // clean up silly Windows -- stuff?
23309             return; 
23310         }
23311         var lcname = node.tagName.toLowerCase();
23312         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
23313         // whitelist of tags..
23314         
23315         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
23316             // remove node.
23317             node.parentNode.removeChild(node);
23318             return;
23319             
23320         }
23321         
23322         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23323         
23324         // spans with no attributes - just remove them..
23325         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
23326             remove_keep_children = true;
23327         }
23328         
23329         // remove <a name=....> as rendering on yahoo mailer is borked with this.
23330         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23331         
23332         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23333         //    remove_keep_children = true;
23334         //}
23335         
23336         if (remove_keep_children) {
23337             this.cleanUpChildren(node);
23338             // inserts everything just before this node...
23339             while (node.childNodes.length) {
23340                 var cn = node.childNodes[0];
23341                 node.removeChild(cn);
23342                 node.parentNode.insertBefore(cn, node);
23343             }
23344             node.parentNode.removeChild(node);
23345             return;
23346         }
23347         
23348         if (!node.attributes || !node.attributes.length) {
23349             
23350           
23351             
23352             
23353             this.cleanUpChildren(node);
23354             return;
23355         }
23356         
23357         function cleanAttr(n,v)
23358         {
23359             
23360             if (v.match(/^\./) || v.match(/^\//)) {
23361                 return;
23362             }
23363             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23364                 return;
23365             }
23366             if (v.match(/^#/)) {
23367                 return;
23368             }
23369 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23370             node.removeAttribute(n);
23371             
23372         }
23373         
23374         var cwhite = this.cwhite;
23375         var cblack = this.cblack;
23376             
23377         function cleanStyle(n,v)
23378         {
23379             if (v.match(/expression/)) { //XSS?? should we even bother..
23380                 node.removeAttribute(n);
23381                 return;
23382             }
23383             
23384             var parts = v.split(/;/);
23385             var clean = [];
23386             
23387             Roo.each(parts, function(p) {
23388                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23389                 if (!p.length) {
23390                     return true;
23391                 }
23392                 var l = p.split(':').shift().replace(/\s+/g,'');
23393                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23394                 
23395                 if ( cwhite.length && cblack.indexOf(l) > -1) {
23396 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23397                     //node.removeAttribute(n);
23398                     return true;
23399                 }
23400                 //Roo.log()
23401                 // only allow 'c whitelisted system attributes'
23402                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
23403 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23404                     //node.removeAttribute(n);
23405                     return true;
23406                 }
23407                 
23408                 
23409                  
23410                 
23411                 clean.push(p);
23412                 return true;
23413             });
23414             if (clean.length) { 
23415                 node.setAttribute(n, clean.join(';'));
23416             } else {
23417                 node.removeAttribute(n);
23418             }
23419             
23420         }
23421         
23422         
23423         for (var i = node.attributes.length-1; i > -1 ; i--) {
23424             var a = node.attributes[i];
23425             //console.log(a);
23426             
23427             if (a.name.toLowerCase().substr(0,2)=='on')  {
23428                 node.removeAttribute(a.name);
23429                 continue;
23430             }
23431             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23432                 node.removeAttribute(a.name);
23433                 continue;
23434             }
23435             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23436                 cleanAttr(a.name,a.value); // fixme..
23437                 continue;
23438             }
23439             if (a.name == 'style') {
23440                 cleanStyle(a.name,a.value);
23441                 continue;
23442             }
23443             /// clean up MS crap..
23444             // tecnically this should be a list of valid class'es..
23445             
23446             
23447             if (a.name == 'class') {
23448                 if (a.value.match(/^Mso/)) {
23449                     node.removeAttribute('class');
23450                 }
23451                 
23452                 if (a.value.match(/^body$/)) {
23453                     node.removeAttribute('class');
23454                 }
23455                 continue;
23456             }
23457             
23458             // style cleanup!?
23459             // class cleanup?
23460             
23461         }
23462         
23463         
23464         this.cleanUpChildren(node);
23465         
23466         
23467     },
23468     
23469     /**
23470      * Clean up MS wordisms...
23471      */
23472     cleanWord : function(node)
23473     {
23474         if (!node) {
23475             this.cleanWord(this.doc.body);
23476             return;
23477         }
23478         
23479         if(
23480                 node.nodeName == 'SPAN' &&
23481                 !node.hasAttributes() &&
23482                 node.childNodes.length == 1 &&
23483                 node.firstChild.nodeName == "#text"  
23484         ) {
23485             var textNode = node.firstChild;
23486             node.removeChild(textNode);
23487             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
23488                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
23489             }
23490             node.parentNode.insertBefore(textNode, node);
23491             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
23492                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
23493             }
23494             node.parentNode.removeChild(node);
23495         }
23496         
23497         if (node.nodeName == "#text") {
23498             // clean up silly Windows -- stuff?
23499             return; 
23500         }
23501         if (node.nodeName == "#comment") {
23502             node.parentNode.removeChild(node);
23503             // clean up silly Windows -- stuff?
23504             return; 
23505         }
23506         
23507         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23508             node.parentNode.removeChild(node);
23509             return;
23510         }
23511         //Roo.log(node.tagName);
23512         // remove - but keep children..
23513         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
23514             //Roo.log('-- removed');
23515             while (node.childNodes.length) {
23516                 var cn = node.childNodes[0];
23517                 node.removeChild(cn);
23518                 node.parentNode.insertBefore(cn, node);
23519                 // move node to parent - and clean it..
23520                 this.cleanWord(cn);
23521             }
23522             node.parentNode.removeChild(node);
23523             /// no need to iterate chidlren = it's got none..
23524             //this.iterateChildren(node, this.cleanWord);
23525             return;
23526         }
23527         // clean styles
23528         if (node.className.length) {
23529             
23530             var cn = node.className.split(/\W+/);
23531             var cna = [];
23532             Roo.each(cn, function(cls) {
23533                 if (cls.match(/Mso[a-zA-Z]+/)) {
23534                     return;
23535                 }
23536                 cna.push(cls);
23537             });
23538             node.className = cna.length ? cna.join(' ') : '';
23539             if (!cna.length) {
23540                 node.removeAttribute("class");
23541             }
23542         }
23543         
23544         if (node.hasAttribute("lang")) {
23545             node.removeAttribute("lang");
23546         }
23547         
23548         if (node.hasAttribute("style")) {
23549             
23550             var styles = node.getAttribute("style").split(";");
23551             var nstyle = [];
23552             Roo.each(styles, function(s) {
23553                 if (!s.match(/:/)) {
23554                     return;
23555                 }
23556                 var kv = s.split(":");
23557                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23558                     return;
23559                 }
23560                 // what ever is left... we allow.
23561                 nstyle.push(s);
23562             });
23563             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23564             if (!nstyle.length) {
23565                 node.removeAttribute('style');
23566             }
23567         }
23568         this.iterateChildren(node, this.cleanWord);
23569         
23570         
23571         
23572     },
23573     /**
23574      * iterateChildren of a Node, calling fn each time, using this as the scole..
23575      * @param {DomNode} node node to iterate children of.
23576      * @param {Function} fn method of this class to call on each item.
23577      */
23578     iterateChildren : function(node, fn)
23579     {
23580         if (!node.childNodes.length) {
23581                 return;
23582         }
23583         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23584            fn.call(this, node.childNodes[i])
23585         }
23586     },
23587     
23588     
23589     /**
23590      * cleanTableWidths.
23591      *
23592      * Quite often pasting from word etc.. results in tables with column and widths.
23593      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23594      *
23595      */
23596     cleanTableWidths : function(node)
23597     {
23598          
23599          
23600         if (!node) {
23601             this.cleanTableWidths(this.doc.body);
23602             return;
23603         }
23604         
23605         // ignore list...
23606         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23607             return; 
23608         }
23609         Roo.log(node.tagName);
23610         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23611             this.iterateChildren(node, this.cleanTableWidths);
23612             return;
23613         }
23614         if (node.hasAttribute('width')) {
23615             node.removeAttribute('width');
23616         }
23617         
23618          
23619         if (node.hasAttribute("style")) {
23620             // pretty basic...
23621             
23622             var styles = node.getAttribute("style").split(";");
23623             var nstyle = [];
23624             Roo.each(styles, function(s) {
23625                 if (!s.match(/:/)) {
23626                     return;
23627                 }
23628                 var kv = s.split(":");
23629                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23630                     return;
23631                 }
23632                 // what ever is left... we allow.
23633                 nstyle.push(s);
23634             });
23635             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23636             if (!nstyle.length) {
23637                 node.removeAttribute('style');
23638             }
23639         }
23640         
23641         this.iterateChildren(node, this.cleanTableWidths);
23642         
23643         
23644     },
23645     
23646     
23647     
23648     
23649     domToHTML : function(currentElement, depth, nopadtext) {
23650         
23651         depth = depth || 0;
23652         nopadtext = nopadtext || false;
23653     
23654         if (!currentElement) {
23655             return this.domToHTML(this.doc.body);
23656         }
23657         
23658         //Roo.log(currentElement);
23659         var j;
23660         var allText = false;
23661         var nodeName = currentElement.nodeName;
23662         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23663         
23664         if  (nodeName == '#text') {
23665             
23666             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23667         }
23668         
23669         
23670         var ret = '';
23671         if (nodeName != 'BODY') {
23672              
23673             var i = 0;
23674             // Prints the node tagName, such as <A>, <IMG>, etc
23675             if (tagName) {
23676                 var attr = [];
23677                 for(i = 0; i < currentElement.attributes.length;i++) {
23678                     // quoting?
23679                     var aname = currentElement.attributes.item(i).name;
23680                     if (!currentElement.attributes.item(i).value.length) {
23681                         continue;
23682                     }
23683                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23684                 }
23685                 
23686                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23687             } 
23688             else {
23689                 
23690                 // eack
23691             }
23692         } else {
23693             tagName = false;
23694         }
23695         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23696             return ret;
23697         }
23698         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23699             nopadtext = true;
23700         }
23701         
23702         
23703         // Traverse the tree
23704         i = 0;
23705         var currentElementChild = currentElement.childNodes.item(i);
23706         var allText = true;
23707         var innerHTML  = '';
23708         lastnode = '';
23709         while (currentElementChild) {
23710             // Formatting code (indent the tree so it looks nice on the screen)
23711             var nopad = nopadtext;
23712             if (lastnode == 'SPAN') {
23713                 nopad  = true;
23714             }
23715             // text
23716             if  (currentElementChild.nodeName == '#text') {
23717                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23718                 toadd = nopadtext ? toadd : toadd.trim();
23719                 if (!nopad && toadd.length > 80) {
23720                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23721                 }
23722                 innerHTML  += toadd;
23723                 
23724                 i++;
23725                 currentElementChild = currentElement.childNodes.item(i);
23726                 lastNode = '';
23727                 continue;
23728             }
23729             allText = false;
23730             
23731             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23732                 
23733             // Recursively traverse the tree structure of the child node
23734             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23735             lastnode = currentElementChild.nodeName;
23736             i++;
23737             currentElementChild=currentElement.childNodes.item(i);
23738         }
23739         
23740         ret += innerHTML;
23741         
23742         if (!allText) {
23743                 // The remaining code is mostly for formatting the tree
23744             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23745         }
23746         
23747         
23748         if (tagName) {
23749             ret+= "</"+tagName+">";
23750         }
23751         return ret;
23752         
23753     },
23754         
23755     applyBlacklists : function()
23756     {
23757         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23758         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23759         
23760         this.white = [];
23761         this.black = [];
23762         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23763             if (b.indexOf(tag) > -1) {
23764                 return;
23765             }
23766             this.white.push(tag);
23767             
23768         }, this);
23769         
23770         Roo.each(w, function(tag) {
23771             if (b.indexOf(tag) > -1) {
23772                 return;
23773             }
23774             if (this.white.indexOf(tag) > -1) {
23775                 return;
23776             }
23777             this.white.push(tag);
23778             
23779         }, this);
23780         
23781         
23782         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23783             if (w.indexOf(tag) > -1) {
23784                 return;
23785             }
23786             this.black.push(tag);
23787             
23788         }, this);
23789         
23790         Roo.each(b, function(tag) {
23791             if (w.indexOf(tag) > -1) {
23792                 return;
23793             }
23794             if (this.black.indexOf(tag) > -1) {
23795                 return;
23796             }
23797             this.black.push(tag);
23798             
23799         }, this);
23800         
23801         
23802         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23803         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23804         
23805         this.cwhite = [];
23806         this.cblack = [];
23807         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23808             if (b.indexOf(tag) > -1) {
23809                 return;
23810             }
23811             this.cwhite.push(tag);
23812             
23813         }, this);
23814         
23815         Roo.each(w, function(tag) {
23816             if (b.indexOf(tag) > -1) {
23817                 return;
23818             }
23819             if (this.cwhite.indexOf(tag) > -1) {
23820                 return;
23821             }
23822             this.cwhite.push(tag);
23823             
23824         }, this);
23825         
23826         
23827         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23828             if (w.indexOf(tag) > -1) {
23829                 return;
23830             }
23831             this.cblack.push(tag);
23832             
23833         }, this);
23834         
23835         Roo.each(b, function(tag) {
23836             if (w.indexOf(tag) > -1) {
23837                 return;
23838             }
23839             if (this.cblack.indexOf(tag) > -1) {
23840                 return;
23841             }
23842             this.cblack.push(tag);
23843             
23844         }, this);
23845     },
23846     
23847     setStylesheets : function(stylesheets)
23848     {
23849         if(typeof(stylesheets) == 'string'){
23850             Roo.get(this.iframe.contentDocument.head).createChild({
23851                 tag : 'link',
23852                 rel : 'stylesheet',
23853                 type : 'text/css',
23854                 href : stylesheets
23855             });
23856             
23857             return;
23858         }
23859         var _this = this;
23860      
23861         Roo.each(stylesheets, function(s) {
23862             if(!s.length){
23863                 return;
23864             }
23865             
23866             Roo.get(_this.iframe.contentDocument.head).createChild({
23867                 tag : 'link',
23868                 rel : 'stylesheet',
23869                 type : 'text/css',
23870                 href : s
23871             });
23872         });
23873
23874         
23875     },
23876     
23877     removeStylesheets : function()
23878     {
23879         var _this = this;
23880         
23881         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23882             s.remove();
23883         });
23884     },
23885     
23886     setStyle : function(style)
23887     {
23888         Roo.get(this.iframe.contentDocument.head).createChild({
23889             tag : 'style',
23890             type : 'text/css',
23891             html : style
23892         });
23893
23894         return;
23895     }
23896     
23897     // hide stuff that is not compatible
23898     /**
23899      * @event blur
23900      * @hide
23901      */
23902     /**
23903      * @event change
23904      * @hide
23905      */
23906     /**
23907      * @event focus
23908      * @hide
23909      */
23910     /**
23911      * @event specialkey
23912      * @hide
23913      */
23914     /**
23915      * @cfg {String} fieldClass @hide
23916      */
23917     /**
23918      * @cfg {String} focusClass @hide
23919      */
23920     /**
23921      * @cfg {String} autoCreate @hide
23922      */
23923     /**
23924      * @cfg {String} inputType @hide
23925      */
23926     /**
23927      * @cfg {String} invalidClass @hide
23928      */
23929     /**
23930      * @cfg {String} invalidText @hide
23931      */
23932     /**
23933      * @cfg {String} msgFx @hide
23934      */
23935     /**
23936      * @cfg {String} validateOnBlur @hide
23937      */
23938 });
23939
23940 Roo.HtmlEditorCore.white = [
23941         'area', 'br', 'img', 'input', 'hr', 'wbr',
23942         
23943        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23944        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23945        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23946        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23947        'table',   'ul',         'xmp', 
23948        
23949        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23950       'thead',   'tr', 
23951      
23952       'dir', 'menu', 'ol', 'ul', 'dl',
23953        
23954       'embed',  'object'
23955 ];
23956
23957
23958 Roo.HtmlEditorCore.black = [
23959     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23960         'applet', // 
23961         'base',   'basefont', 'bgsound', 'blink',  'body', 
23962         'frame',  'frameset', 'head',    'html',   'ilayer', 
23963         'iframe', 'layer',  'link',     'meta',    'object',   
23964         'script', 'style' ,'title',  'xml' // clean later..
23965 ];
23966 Roo.HtmlEditorCore.clean = [
23967     'script', 'style', 'title', 'xml'
23968 ];
23969 Roo.HtmlEditorCore.remove = [
23970     'font'
23971 ];
23972 // attributes..
23973
23974 Roo.HtmlEditorCore.ablack = [
23975     'on'
23976 ];
23977     
23978 Roo.HtmlEditorCore.aclean = [ 
23979     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23980 ];
23981
23982 // protocols..
23983 Roo.HtmlEditorCore.pwhite= [
23984         'http',  'https',  'mailto'
23985 ];
23986
23987 // white listed style attributes.
23988 Roo.HtmlEditorCore.cwhite= [
23989       //  'text-align', /// default is to allow most things..
23990       
23991          
23992 //        'font-size'//??
23993 ];
23994
23995 // black listed style attributes.
23996 Roo.HtmlEditorCore.cblack= [
23997       //  'font-size' -- this can be set by the project 
23998 ];
23999
24000
24001 Roo.HtmlEditorCore.swapCodes   =[ 
24002     [    8211, "--" ], 
24003     [    8212, "--" ], 
24004     [    8216,  "'" ],  
24005     [    8217, "'" ],  
24006     [    8220, '"' ],  
24007     [    8221, '"' ],  
24008     [    8226, "*" ],  
24009     [    8230, "..." ]
24010 ]; 
24011
24012     /*
24013  * - LGPL
24014  *
24015  * HtmlEditor
24016  * 
24017  */
24018
24019 /**
24020  * @class Roo.bootstrap.HtmlEditor
24021  * @extends Roo.bootstrap.TextArea
24022  * Bootstrap HtmlEditor class
24023
24024  * @constructor
24025  * Create a new HtmlEditor
24026  * @param {Object} config The config object
24027  */
24028
24029 Roo.bootstrap.HtmlEditor = function(config){
24030     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
24031     if (!this.toolbars) {
24032         this.toolbars = [];
24033     }
24034     
24035     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
24036     this.addEvents({
24037             /**
24038              * @event initialize
24039              * Fires when the editor is fully initialized (including the iframe)
24040              * @param {HtmlEditor} this
24041              */
24042             initialize: true,
24043             /**
24044              * @event activate
24045              * Fires when the editor is first receives the focus. Any insertion must wait
24046              * until after this event.
24047              * @param {HtmlEditor} this
24048              */
24049             activate: true,
24050              /**
24051              * @event beforesync
24052              * Fires before the textarea is updated with content from the editor iframe. Return false
24053              * to cancel the sync.
24054              * @param {HtmlEditor} this
24055              * @param {String} html
24056              */
24057             beforesync: true,
24058              /**
24059              * @event beforepush
24060              * Fires before the iframe editor is updated with content from the textarea. Return false
24061              * to cancel the push.
24062              * @param {HtmlEditor} this
24063              * @param {String} html
24064              */
24065             beforepush: true,
24066              /**
24067              * @event sync
24068              * Fires when the textarea is updated with content from the editor iframe.
24069              * @param {HtmlEditor} this
24070              * @param {String} html
24071              */
24072             sync: true,
24073              /**
24074              * @event push
24075              * Fires when the iframe editor is updated with content from the textarea.
24076              * @param {HtmlEditor} this
24077              * @param {String} html
24078              */
24079             push: true,
24080              /**
24081              * @event editmodechange
24082              * Fires when the editor switches edit modes
24083              * @param {HtmlEditor} this
24084              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24085              */
24086             editmodechange: true,
24087             /**
24088              * @event editorevent
24089              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24090              * @param {HtmlEditor} this
24091              */
24092             editorevent: true,
24093             /**
24094              * @event firstfocus
24095              * Fires when on first focus - needed by toolbars..
24096              * @param {HtmlEditor} this
24097              */
24098             firstfocus: true,
24099             /**
24100              * @event autosave
24101              * Auto save the htmlEditor value as a file into Events
24102              * @param {HtmlEditor} this
24103              */
24104             autosave: true,
24105             /**
24106              * @event savedpreview
24107              * preview the saved version of htmlEditor
24108              * @param {HtmlEditor} this
24109              */
24110             savedpreview: true
24111         });
24112 };
24113
24114
24115 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
24116     
24117     
24118       /**
24119      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
24120      */
24121     toolbars : false,
24122     
24123      /**
24124     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
24125     */
24126     btns : [],
24127    
24128      /**
24129      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24130      *                        Roo.resizable.
24131      */
24132     resizable : false,
24133      /**
24134      * @cfg {Number} height (in pixels)
24135      */   
24136     height: 300,
24137    /**
24138      * @cfg {Number} width (in pixels)
24139      */   
24140     width: false,
24141     
24142     /**
24143      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24144      * 
24145      */
24146     stylesheets: false,
24147     
24148     // id of frame..
24149     frameId: false,
24150     
24151     // private properties
24152     validationEvent : false,
24153     deferHeight: true,
24154     initialized : false,
24155     activated : false,
24156     
24157     onFocus : Roo.emptyFn,
24158     iframePad:3,
24159     hideMode:'offsets',
24160     
24161     tbContainer : false,
24162     
24163     bodyCls : '',
24164     
24165     toolbarContainer :function() {
24166         return this.wrap.select('.x-html-editor-tb',true).first();
24167     },
24168
24169     /**
24170      * Protected method that will not generally be called directly. It
24171      * is called when the editor creates its toolbar. Override this method if you need to
24172      * add custom toolbar buttons.
24173      * @param {HtmlEditor} editor
24174      */
24175     createToolbar : function(){
24176         Roo.log('renewing');
24177         Roo.log("create toolbars");
24178         
24179         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
24180         this.toolbars[0].render(this.toolbarContainer());
24181         
24182         return;
24183         
24184 //        if (!editor.toolbars || !editor.toolbars.length) {
24185 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
24186 //        }
24187 //        
24188 //        for (var i =0 ; i < editor.toolbars.length;i++) {
24189 //            editor.toolbars[i] = Roo.factory(
24190 //                    typeof(editor.toolbars[i]) == 'string' ?
24191 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
24192 //                Roo.bootstrap.HtmlEditor);
24193 //            editor.toolbars[i].init(editor);
24194 //        }
24195     },
24196
24197      
24198     // private
24199     onRender : function(ct, position)
24200     {
24201        // Roo.log("Call onRender: " + this.xtype);
24202         var _t = this;
24203         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
24204       
24205         this.wrap = this.inputEl().wrap({
24206             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24207         });
24208         
24209         this.editorcore.onRender(ct, position);
24210          
24211         if (this.resizable) {
24212             this.resizeEl = new Roo.Resizable(this.wrap, {
24213                 pinned : true,
24214                 wrap: true,
24215                 dynamic : true,
24216                 minHeight : this.height,
24217                 height: this.height,
24218                 handles : this.resizable,
24219                 width: this.width,
24220                 listeners : {
24221                     resize : function(r, w, h) {
24222                         _t.onResize(w,h); // -something
24223                     }
24224                 }
24225             });
24226             
24227         }
24228         this.createToolbar(this);
24229        
24230         
24231         if(!this.width && this.resizable){
24232             this.setSize(this.wrap.getSize());
24233         }
24234         if (this.resizeEl) {
24235             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24236             // should trigger onReize..
24237         }
24238         
24239     },
24240
24241     // private
24242     onResize : function(w, h)
24243     {
24244         Roo.log('resize: ' +w + ',' + h );
24245         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
24246         var ew = false;
24247         var eh = false;
24248         
24249         if(this.inputEl() ){
24250             if(typeof w == 'number'){
24251                 var aw = w - this.wrap.getFrameWidth('lr');
24252                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
24253                 ew = aw;
24254             }
24255             if(typeof h == 'number'){
24256                  var tbh = -11;  // fixme it needs to tool bar size!
24257                 for (var i =0; i < this.toolbars.length;i++) {
24258                     // fixme - ask toolbars for heights?
24259                     tbh += this.toolbars[i].el.getHeight();
24260                     //if (this.toolbars[i].footer) {
24261                     //    tbh += this.toolbars[i].footer.el.getHeight();
24262                     //}
24263                 }
24264               
24265                 
24266                 
24267                 
24268                 
24269                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24270                 ah -= 5; // knock a few pixes off for look..
24271                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
24272                 var eh = ah;
24273             }
24274         }
24275         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
24276         this.editorcore.onResize(ew,eh);
24277         
24278     },
24279
24280     /**
24281      * Toggles the editor between standard and source edit mode.
24282      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24283      */
24284     toggleSourceEdit : function(sourceEditMode)
24285     {
24286         this.editorcore.toggleSourceEdit(sourceEditMode);
24287         
24288         if(this.editorcore.sourceEditMode){
24289             Roo.log('editor - showing textarea');
24290             
24291 //            Roo.log('in');
24292 //            Roo.log(this.syncValue());
24293             this.syncValue();
24294             this.inputEl().removeClass(['hide', 'x-hidden']);
24295             this.inputEl().dom.removeAttribute('tabIndex');
24296             this.inputEl().focus();
24297         }else{
24298             Roo.log('editor - hiding textarea');
24299 //            Roo.log('out')
24300 //            Roo.log(this.pushValue()); 
24301             this.pushValue();
24302             
24303             this.inputEl().addClass(['hide', 'x-hidden']);
24304             this.inputEl().dom.setAttribute('tabIndex', -1);
24305             //this.deferFocus();
24306         }
24307          
24308         if(this.resizable){
24309             this.setSize(this.wrap.getSize());
24310         }
24311         
24312         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
24313     },
24314  
24315     // private (for BoxComponent)
24316     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24317
24318     // private (for BoxComponent)
24319     getResizeEl : function(){
24320         return this.wrap;
24321     },
24322
24323     // private (for BoxComponent)
24324     getPositionEl : function(){
24325         return this.wrap;
24326     },
24327
24328     // private
24329     initEvents : function(){
24330         this.originalValue = this.getValue();
24331     },
24332
24333 //    /**
24334 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24335 //     * @method
24336 //     */
24337 //    markInvalid : Roo.emptyFn,
24338 //    /**
24339 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24340 //     * @method
24341 //     */
24342 //    clearInvalid : Roo.emptyFn,
24343
24344     setValue : function(v){
24345         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
24346         this.editorcore.pushValue();
24347     },
24348
24349      
24350     // private
24351     deferFocus : function(){
24352         this.focus.defer(10, this);
24353     },
24354
24355     // doc'ed in Field
24356     focus : function(){
24357         this.editorcore.focus();
24358         
24359     },
24360       
24361
24362     // private
24363     onDestroy : function(){
24364         
24365         
24366         
24367         if(this.rendered){
24368             
24369             for (var i =0; i < this.toolbars.length;i++) {
24370                 // fixme - ask toolbars for heights?
24371                 this.toolbars[i].onDestroy();
24372             }
24373             
24374             this.wrap.dom.innerHTML = '';
24375             this.wrap.remove();
24376         }
24377     },
24378
24379     // private
24380     onFirstFocus : function(){
24381         //Roo.log("onFirstFocus");
24382         this.editorcore.onFirstFocus();
24383          for (var i =0; i < this.toolbars.length;i++) {
24384             this.toolbars[i].onFirstFocus();
24385         }
24386         
24387     },
24388     
24389     // private
24390     syncValue : function()
24391     {   
24392         this.editorcore.syncValue();
24393     },
24394     
24395     pushValue : function()
24396     {   
24397         this.editorcore.pushValue();
24398     }
24399      
24400     
24401     // hide stuff that is not compatible
24402     /**
24403      * @event blur
24404      * @hide
24405      */
24406     /**
24407      * @event change
24408      * @hide
24409      */
24410     /**
24411      * @event focus
24412      * @hide
24413      */
24414     /**
24415      * @event specialkey
24416      * @hide
24417      */
24418     /**
24419      * @cfg {String} fieldClass @hide
24420      */
24421     /**
24422      * @cfg {String} focusClass @hide
24423      */
24424     /**
24425      * @cfg {String} autoCreate @hide
24426      */
24427     /**
24428      * @cfg {String} inputType @hide
24429      */
24430      
24431     /**
24432      * @cfg {String} invalidText @hide
24433      */
24434     /**
24435      * @cfg {String} msgFx @hide
24436      */
24437     /**
24438      * @cfg {String} validateOnBlur @hide
24439      */
24440 });
24441  
24442     
24443    
24444    
24445    
24446       
24447 Roo.namespace('Roo.bootstrap.htmleditor');
24448 /**
24449  * @class Roo.bootstrap.HtmlEditorToolbar1
24450  * Basic Toolbar
24451  * 
24452  * @example
24453  * Usage:
24454  *
24455  new Roo.bootstrap.HtmlEditor({
24456     ....
24457     toolbars : [
24458         new Roo.bootstrap.HtmlEditorToolbar1({
24459             disable : { fonts: 1 , format: 1, ..., ... , ...],
24460             btns : [ .... ]
24461         })
24462     }
24463      
24464  * 
24465  * @cfg {Object} disable List of elements to disable..
24466  * @cfg {Array} btns List of additional buttons.
24467  * 
24468  * 
24469  * NEEDS Extra CSS? 
24470  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24471  */
24472  
24473 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24474 {
24475     
24476     Roo.apply(this, config);
24477     
24478     // default disabled, based on 'good practice'..
24479     this.disable = this.disable || {};
24480     Roo.applyIf(this.disable, {
24481         fontSize : true,
24482         colors : true,
24483         specialElements : true
24484     });
24485     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24486     
24487     this.editor = config.editor;
24488     this.editorcore = config.editor.editorcore;
24489     
24490     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24491     
24492     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24493     // dont call parent... till later.
24494 }
24495 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
24496      
24497     bar : true,
24498     
24499     editor : false,
24500     editorcore : false,
24501     
24502     
24503     formats : [
24504         "p" ,  
24505         "h1","h2","h3","h4","h5","h6", 
24506         "pre", "code", 
24507         "abbr", "acronym", "address", "cite", "samp", "var",
24508         'div','span'
24509     ],
24510     
24511     onRender : function(ct, position)
24512     {
24513        // Roo.log("Call onRender: " + this.xtype);
24514         
24515        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24516        Roo.log(this.el);
24517        this.el.dom.style.marginBottom = '0';
24518        var _this = this;
24519        var editorcore = this.editorcore;
24520        var editor= this.editor;
24521        
24522        var children = [];
24523        var btn = function(id,cmd , toggle, handler, html){
24524        
24525             var  event = toggle ? 'toggle' : 'click';
24526        
24527             var a = {
24528                 size : 'sm',
24529                 xtype: 'Button',
24530                 xns: Roo.bootstrap,
24531                 //glyphicon : id,
24532                 fa: id,
24533                 cmd : id || cmd,
24534                 enableToggle:toggle !== false,
24535                 html : html || '',
24536                 pressed : toggle ? false : null,
24537                 listeners : {}
24538             };
24539             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24540                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24541             };
24542             children.push(a);
24543             return a;
24544        }
24545        
24546     //    var cb_box = function...
24547         
24548         var style = {
24549                 xtype: 'Button',
24550                 size : 'sm',
24551                 xns: Roo.bootstrap,
24552                 fa : 'font',
24553                 //html : 'submit'
24554                 menu : {
24555                     xtype: 'Menu',
24556                     xns: Roo.bootstrap,
24557                     items:  []
24558                 }
24559         };
24560         Roo.each(this.formats, function(f) {
24561             style.menu.items.push({
24562                 xtype :'MenuItem',
24563                 xns: Roo.bootstrap,
24564                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24565                 tagname : f,
24566                 listeners : {
24567                     click : function()
24568                     {
24569                         editorcore.insertTag(this.tagname);
24570                         editor.focus();
24571                     }
24572                 }
24573                 
24574             });
24575         });
24576         children.push(style);   
24577         
24578         btn('bold',false,true);
24579         btn('italic',false,true);
24580         btn('align-left', 'justifyleft',true);
24581         btn('align-center', 'justifycenter',true);
24582         btn('align-right' , 'justifyright',true);
24583         btn('link', false, false, function(btn) {
24584             //Roo.log("create link?");
24585             var url = prompt(this.createLinkText, this.defaultLinkValue);
24586             if(url && url != 'http:/'+'/'){
24587                 this.editorcore.relayCmd('createlink', url);
24588             }
24589         }),
24590         btn('list','insertunorderedlist',true);
24591         btn('pencil', false,true, function(btn){
24592                 Roo.log(this);
24593                 this.toggleSourceEdit(btn.pressed);
24594         });
24595         
24596         if (this.editor.btns.length > 0) {
24597             for (var i = 0; i<this.editor.btns.length; i++) {
24598                 children.push(this.editor.btns[i]);
24599             }
24600         }
24601         
24602         /*
24603         var cog = {
24604                 xtype: 'Button',
24605                 size : 'sm',
24606                 xns: Roo.bootstrap,
24607                 glyphicon : 'cog',
24608                 //html : 'submit'
24609                 menu : {
24610                     xtype: 'Menu',
24611                     xns: Roo.bootstrap,
24612                     items:  []
24613                 }
24614         };
24615         
24616         cog.menu.items.push({
24617             xtype :'MenuItem',
24618             xns: Roo.bootstrap,
24619             html : Clean styles,
24620             tagname : f,
24621             listeners : {
24622                 click : function()
24623                 {
24624                     editorcore.insertTag(this.tagname);
24625                     editor.focus();
24626                 }
24627             }
24628             
24629         });
24630        */
24631         
24632          
24633        this.xtype = 'NavSimplebar';
24634         
24635         for(var i=0;i< children.length;i++) {
24636             
24637             this.buttons.add(this.addxtypeChild(children[i]));
24638             
24639         }
24640         
24641         editor.on('editorevent', this.updateToolbar, this);
24642     },
24643     onBtnClick : function(id)
24644     {
24645        this.editorcore.relayCmd(id);
24646        this.editorcore.focus();
24647     },
24648     
24649     /**
24650      * Protected method that will not generally be called directly. It triggers
24651      * a toolbar update by reading the markup state of the current selection in the editor.
24652      */
24653     updateToolbar: function(){
24654
24655         if(!this.editorcore.activated){
24656             this.editor.onFirstFocus(); // is this neeed?
24657             return;
24658         }
24659
24660         var btns = this.buttons; 
24661         var doc = this.editorcore.doc;
24662         btns.get('bold').setActive(doc.queryCommandState('bold'));
24663         btns.get('italic').setActive(doc.queryCommandState('italic'));
24664         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24665         
24666         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24667         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24668         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24669         
24670         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24671         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24672          /*
24673         
24674         var ans = this.editorcore.getAllAncestors();
24675         if (this.formatCombo) {
24676             
24677             
24678             var store = this.formatCombo.store;
24679             this.formatCombo.setValue("");
24680             for (var i =0; i < ans.length;i++) {
24681                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24682                     // select it..
24683                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24684                     break;
24685                 }
24686             }
24687         }
24688         
24689         
24690         
24691         // hides menus... - so this cant be on a menu...
24692         Roo.bootstrap.MenuMgr.hideAll();
24693         */
24694         Roo.bootstrap.MenuMgr.hideAll();
24695         //this.editorsyncValue();
24696     },
24697     onFirstFocus: function() {
24698         this.buttons.each(function(item){
24699            item.enable();
24700         });
24701     },
24702     toggleSourceEdit : function(sourceEditMode){
24703         
24704           
24705         if(sourceEditMode){
24706             Roo.log("disabling buttons");
24707            this.buttons.each( function(item){
24708                 if(item.cmd != 'pencil'){
24709                     item.disable();
24710                 }
24711             });
24712           
24713         }else{
24714             Roo.log("enabling buttons");
24715             if(this.editorcore.initialized){
24716                 this.buttons.each( function(item){
24717                     item.enable();
24718                 });
24719             }
24720             
24721         }
24722         Roo.log("calling toggole on editor");
24723         // tell the editor that it's been pressed..
24724         this.editor.toggleSourceEdit(sourceEditMode);
24725        
24726     }
24727 });
24728
24729
24730
24731
24732
24733 /**
24734  * @class Roo.bootstrap.Table.AbstractSelectionModel
24735  * @extends Roo.util.Observable
24736  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24737  * implemented by descendant classes.  This class should not be directly instantiated.
24738  * @constructor
24739  */
24740 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24741     this.locked = false;
24742     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24743 };
24744
24745
24746 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24747     /** @ignore Called by the grid automatically. Do not call directly. */
24748     init : function(grid){
24749         this.grid = grid;
24750         this.initEvents();
24751     },
24752
24753     /**
24754      * Locks the selections.
24755      */
24756     lock : function(){
24757         this.locked = true;
24758     },
24759
24760     /**
24761      * Unlocks the selections.
24762      */
24763     unlock : function(){
24764         this.locked = false;
24765     },
24766
24767     /**
24768      * Returns true if the selections are locked.
24769      * @return {Boolean}
24770      */
24771     isLocked : function(){
24772         return this.locked;
24773     }
24774 });
24775 /**
24776  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24777  * @class Roo.bootstrap.Table.RowSelectionModel
24778  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24779  * It supports multiple selections and keyboard selection/navigation. 
24780  * @constructor
24781  * @param {Object} config
24782  */
24783
24784 Roo.bootstrap.Table.RowSelectionModel = function(config){
24785     Roo.apply(this, config);
24786     this.selections = new Roo.util.MixedCollection(false, function(o){
24787         return o.id;
24788     });
24789
24790     this.last = false;
24791     this.lastActive = false;
24792
24793     this.addEvents({
24794         /**
24795              * @event selectionchange
24796              * Fires when the selection changes
24797              * @param {SelectionModel} this
24798              */
24799             "selectionchange" : true,
24800         /**
24801              * @event afterselectionchange
24802              * Fires after the selection changes (eg. by key press or clicking)
24803              * @param {SelectionModel} this
24804              */
24805             "afterselectionchange" : true,
24806         /**
24807              * @event beforerowselect
24808              * Fires when a row is selected being selected, return false to cancel.
24809              * @param {SelectionModel} this
24810              * @param {Number} rowIndex The selected index
24811              * @param {Boolean} keepExisting False if other selections will be cleared
24812              */
24813             "beforerowselect" : true,
24814         /**
24815              * @event rowselect
24816              * Fires when a row is selected.
24817              * @param {SelectionModel} this
24818              * @param {Number} rowIndex The selected index
24819              * @param {Roo.data.Record} r The record
24820              */
24821             "rowselect" : true,
24822         /**
24823              * @event rowdeselect
24824              * Fires when a row is deselected.
24825              * @param {SelectionModel} this
24826              * @param {Number} rowIndex The selected index
24827              */
24828         "rowdeselect" : true
24829     });
24830     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24831     this.locked = false;
24832  };
24833
24834 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24835     /**
24836      * @cfg {Boolean} singleSelect
24837      * True to allow selection of only one row at a time (defaults to false)
24838      */
24839     singleSelect : false,
24840
24841     // private
24842     initEvents : function()
24843     {
24844
24845         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24846         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24847         //}else{ // allow click to work like normal
24848          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24849         //}
24850         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24851         this.grid.on("rowclick", this.handleMouseDown, this);
24852         
24853         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24854             "up" : function(e){
24855                 if(!e.shiftKey){
24856                     this.selectPrevious(e.shiftKey);
24857                 }else if(this.last !== false && this.lastActive !== false){
24858                     var last = this.last;
24859                     this.selectRange(this.last,  this.lastActive-1);
24860                     this.grid.getView().focusRow(this.lastActive);
24861                     if(last !== false){
24862                         this.last = last;
24863                     }
24864                 }else{
24865                     this.selectFirstRow();
24866                 }
24867                 this.fireEvent("afterselectionchange", this);
24868             },
24869             "down" : function(e){
24870                 if(!e.shiftKey){
24871                     this.selectNext(e.shiftKey);
24872                 }else if(this.last !== false && this.lastActive !== false){
24873                     var last = this.last;
24874                     this.selectRange(this.last,  this.lastActive+1);
24875                     this.grid.getView().focusRow(this.lastActive);
24876                     if(last !== false){
24877                         this.last = last;
24878                     }
24879                 }else{
24880                     this.selectFirstRow();
24881                 }
24882                 this.fireEvent("afterselectionchange", this);
24883             },
24884             scope: this
24885         });
24886         this.grid.store.on('load', function(){
24887             this.selections.clear();
24888         },this);
24889         /*
24890         var view = this.grid.view;
24891         view.on("refresh", this.onRefresh, this);
24892         view.on("rowupdated", this.onRowUpdated, this);
24893         view.on("rowremoved", this.onRemove, this);
24894         */
24895     },
24896
24897     // private
24898     onRefresh : function()
24899     {
24900         var ds = this.grid.store, i, v = this.grid.view;
24901         var s = this.selections;
24902         s.each(function(r){
24903             if((i = ds.indexOfId(r.id)) != -1){
24904                 v.onRowSelect(i);
24905             }else{
24906                 s.remove(r);
24907             }
24908         });
24909     },
24910
24911     // private
24912     onRemove : function(v, index, r){
24913         this.selections.remove(r);
24914     },
24915
24916     // private
24917     onRowUpdated : function(v, index, r){
24918         if(this.isSelected(r)){
24919             v.onRowSelect(index);
24920         }
24921     },
24922
24923     /**
24924      * Select records.
24925      * @param {Array} records The records to select
24926      * @param {Boolean} keepExisting (optional) True to keep existing selections
24927      */
24928     selectRecords : function(records, keepExisting)
24929     {
24930         if(!keepExisting){
24931             this.clearSelections();
24932         }
24933             var ds = this.grid.store;
24934         for(var i = 0, len = records.length; i < len; i++){
24935             this.selectRow(ds.indexOf(records[i]), true);
24936         }
24937     },
24938
24939     /**
24940      * Gets the number of selected rows.
24941      * @return {Number}
24942      */
24943     getCount : function(){
24944         return this.selections.length;
24945     },
24946
24947     /**
24948      * Selects the first row in the grid.
24949      */
24950     selectFirstRow : function(){
24951         this.selectRow(0);
24952     },
24953
24954     /**
24955      * Select the last row.
24956      * @param {Boolean} keepExisting (optional) True to keep existing selections
24957      */
24958     selectLastRow : function(keepExisting){
24959         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24960         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24961     },
24962
24963     /**
24964      * Selects the row immediately following the last selected row.
24965      * @param {Boolean} keepExisting (optional) True to keep existing selections
24966      */
24967     selectNext : function(keepExisting)
24968     {
24969             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24970             this.selectRow(this.last+1, keepExisting);
24971             this.grid.getView().focusRow(this.last);
24972         }
24973     },
24974
24975     /**
24976      * Selects the row that precedes the last selected row.
24977      * @param {Boolean} keepExisting (optional) True to keep existing selections
24978      */
24979     selectPrevious : function(keepExisting){
24980         if(this.last){
24981             this.selectRow(this.last-1, keepExisting);
24982             this.grid.getView().focusRow(this.last);
24983         }
24984     },
24985
24986     /**
24987      * Returns the selected records
24988      * @return {Array} Array of selected records
24989      */
24990     getSelections : function(){
24991         return [].concat(this.selections.items);
24992     },
24993
24994     /**
24995      * Returns the first selected record.
24996      * @return {Record}
24997      */
24998     getSelected : function(){
24999         return this.selections.itemAt(0);
25000     },
25001
25002
25003     /**
25004      * Clears all selections.
25005      */
25006     clearSelections : function(fast)
25007     {
25008         if(this.locked) {
25009             return;
25010         }
25011         if(fast !== true){
25012                 var ds = this.grid.store;
25013             var s = this.selections;
25014             s.each(function(r){
25015                 this.deselectRow(ds.indexOfId(r.id));
25016             }, this);
25017             s.clear();
25018         }else{
25019             this.selections.clear();
25020         }
25021         this.last = false;
25022     },
25023
25024
25025     /**
25026      * Selects all rows.
25027      */
25028     selectAll : function(){
25029         if(this.locked) {
25030             return;
25031         }
25032         this.selections.clear();
25033         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
25034             this.selectRow(i, true);
25035         }
25036     },
25037
25038     /**
25039      * Returns True if there is a selection.
25040      * @return {Boolean}
25041      */
25042     hasSelection : function(){
25043         return this.selections.length > 0;
25044     },
25045
25046     /**
25047      * Returns True if the specified row is selected.
25048      * @param {Number/Record} record The record or index of the record to check
25049      * @return {Boolean}
25050      */
25051     isSelected : function(index){
25052             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
25053         return (r && this.selections.key(r.id) ? true : false);
25054     },
25055
25056     /**
25057      * Returns True if the specified record id is selected.
25058      * @param {String} id The id of record to check
25059      * @return {Boolean}
25060      */
25061     isIdSelected : function(id){
25062         return (this.selections.key(id) ? true : false);
25063     },
25064
25065
25066     // private
25067     handleMouseDBClick : function(e, t){
25068         
25069     },
25070     // private
25071     handleMouseDown : function(e, t)
25072     {
25073             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
25074         if(this.isLocked() || rowIndex < 0 ){
25075             return;
25076         };
25077         if(e.shiftKey && this.last !== false){
25078             var last = this.last;
25079             this.selectRange(last, rowIndex, e.ctrlKey);
25080             this.last = last; // reset the last
25081             t.focus();
25082     
25083         }else{
25084             var isSelected = this.isSelected(rowIndex);
25085             //Roo.log("select row:" + rowIndex);
25086             if(isSelected){
25087                 this.deselectRow(rowIndex);
25088             } else {
25089                         this.selectRow(rowIndex, true);
25090             }
25091     
25092             /*
25093                 if(e.button !== 0 && isSelected){
25094                 alert('rowIndex 2: ' + rowIndex);
25095                     view.focusRow(rowIndex);
25096                 }else if(e.ctrlKey && isSelected){
25097                     this.deselectRow(rowIndex);
25098                 }else if(!isSelected){
25099                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
25100                     view.focusRow(rowIndex);
25101                 }
25102             */
25103         }
25104         this.fireEvent("afterselectionchange", this);
25105     },
25106     // private
25107     handleDragableRowClick :  function(grid, rowIndex, e) 
25108     {
25109         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
25110             this.selectRow(rowIndex, false);
25111             grid.view.focusRow(rowIndex);
25112              this.fireEvent("afterselectionchange", this);
25113         }
25114     },
25115     
25116     /**
25117      * Selects multiple rows.
25118      * @param {Array} rows Array of the indexes of the row to select
25119      * @param {Boolean} keepExisting (optional) True to keep existing selections
25120      */
25121     selectRows : function(rows, keepExisting){
25122         if(!keepExisting){
25123             this.clearSelections();
25124         }
25125         for(var i = 0, len = rows.length; i < len; i++){
25126             this.selectRow(rows[i], true);
25127         }
25128     },
25129
25130     /**
25131      * Selects a range of rows. All rows in between startRow and endRow are also selected.
25132      * @param {Number} startRow The index of the first row in the range
25133      * @param {Number} endRow The index of the last row in the range
25134      * @param {Boolean} keepExisting (optional) True to retain existing selections
25135      */
25136     selectRange : function(startRow, endRow, keepExisting){
25137         if(this.locked) {
25138             return;
25139         }
25140         if(!keepExisting){
25141             this.clearSelections();
25142         }
25143         if(startRow <= endRow){
25144             for(var i = startRow; i <= endRow; i++){
25145                 this.selectRow(i, true);
25146             }
25147         }else{
25148             for(var i = startRow; i >= endRow; i--){
25149                 this.selectRow(i, true);
25150             }
25151         }
25152     },
25153
25154     /**
25155      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
25156      * @param {Number} startRow The index of the first row in the range
25157      * @param {Number} endRow The index of the last row in the range
25158      */
25159     deselectRange : function(startRow, endRow, preventViewNotify){
25160         if(this.locked) {
25161             return;
25162         }
25163         for(var i = startRow; i <= endRow; i++){
25164             this.deselectRow(i, preventViewNotify);
25165         }
25166     },
25167
25168     /**
25169      * Selects a row.
25170      * @param {Number} row The index of the row to select
25171      * @param {Boolean} keepExisting (optional) True to keep existing selections
25172      */
25173     selectRow : function(index, keepExisting, preventViewNotify)
25174     {
25175             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
25176             return;
25177         }
25178         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
25179             if(!keepExisting || this.singleSelect){
25180                 this.clearSelections();
25181             }
25182             
25183             var r = this.grid.store.getAt(index);
25184             //console.log('selectRow - record id :' + r.id);
25185             
25186             this.selections.add(r);
25187             this.last = this.lastActive = index;
25188             if(!preventViewNotify){
25189                 var proxy = new Roo.Element(
25190                                 this.grid.getRowDom(index)
25191                 );
25192                 proxy.addClass('bg-info info');
25193             }
25194             this.fireEvent("rowselect", this, index, r);
25195             this.fireEvent("selectionchange", this);
25196         }
25197     },
25198
25199     /**
25200      * Deselects a row.
25201      * @param {Number} row The index of the row to deselect
25202      */
25203     deselectRow : function(index, preventViewNotify)
25204     {
25205         if(this.locked) {
25206             return;
25207         }
25208         if(this.last == index){
25209             this.last = false;
25210         }
25211         if(this.lastActive == index){
25212             this.lastActive = false;
25213         }
25214         
25215         var r = this.grid.store.getAt(index);
25216         if (!r) {
25217             return;
25218         }
25219         
25220         this.selections.remove(r);
25221         //.console.log('deselectRow - record id :' + r.id);
25222         if(!preventViewNotify){
25223         
25224             var proxy = new Roo.Element(
25225                 this.grid.getRowDom(index)
25226             );
25227             proxy.removeClass('bg-info info');
25228         }
25229         this.fireEvent("rowdeselect", this, index);
25230         this.fireEvent("selectionchange", this);
25231     },
25232
25233     // private
25234     restoreLast : function(){
25235         if(this._last){
25236             this.last = this._last;
25237         }
25238     },
25239
25240     // private
25241     acceptsNav : function(row, col, cm){
25242         return !cm.isHidden(col) && cm.isCellEditable(col, row);
25243     },
25244
25245     // private
25246     onEditorKey : function(field, e){
25247         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
25248         if(k == e.TAB){
25249             e.stopEvent();
25250             ed.completeEdit();
25251             if(e.shiftKey){
25252                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
25253             }else{
25254                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
25255             }
25256         }else if(k == e.ENTER && !e.ctrlKey){
25257             e.stopEvent();
25258             ed.completeEdit();
25259             if(e.shiftKey){
25260                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
25261             }else{
25262                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
25263             }
25264         }else if(k == e.ESC){
25265             ed.cancelEdit();
25266         }
25267         if(newCell){
25268             g.startEditing(newCell[0], newCell[1]);
25269         }
25270     }
25271 });
25272 /*
25273  * Based on:
25274  * Ext JS Library 1.1.1
25275  * Copyright(c) 2006-2007, Ext JS, LLC.
25276  *
25277  * Originally Released Under LGPL - original licence link has changed is not relivant.
25278  *
25279  * Fork - LGPL
25280  * <script type="text/javascript">
25281  */
25282  
25283 /**
25284  * @class Roo.bootstrap.PagingToolbar
25285  * @extends Roo.bootstrap.NavSimplebar
25286  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
25287  * @constructor
25288  * Create a new PagingToolbar
25289  * @param {Object} config The config object
25290  * @param {Roo.data.Store} store
25291  */
25292 Roo.bootstrap.PagingToolbar = function(config)
25293 {
25294     // old args format still supported... - xtype is prefered..
25295         // created from xtype...
25296     
25297     this.ds = config.dataSource;
25298     
25299     if (config.store && !this.ds) {
25300         this.store= Roo.factory(config.store, Roo.data);
25301         this.ds = this.store;
25302         this.ds.xmodule = this.xmodule || false;
25303     }
25304     
25305     this.toolbarItems = [];
25306     if (config.items) {
25307         this.toolbarItems = config.items;
25308     }
25309     
25310     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
25311     
25312     this.cursor = 0;
25313     
25314     if (this.ds) { 
25315         this.bind(this.ds);
25316     }
25317     
25318     if (Roo.bootstrap.version == 4) {
25319         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
25320     } else {
25321         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
25322     }
25323     
25324 };
25325
25326 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
25327     /**
25328      * @cfg {Roo.data.Store} dataSource
25329      * The underlying data store providing the paged data
25330      */
25331     /**
25332      * @cfg {String/HTMLElement/Element} container
25333      * container The id or element that will contain the toolbar
25334      */
25335     /**
25336      * @cfg {Boolean} displayInfo
25337      * True to display the displayMsg (defaults to false)
25338      */
25339     /**
25340      * @cfg {Number} pageSize
25341      * The number of records to display per page (defaults to 20)
25342      */
25343     pageSize: 20,
25344     /**
25345      * @cfg {String} displayMsg
25346      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
25347      */
25348     displayMsg : 'Displaying {0} - {1} of {2}',
25349     /**
25350      * @cfg {String} emptyMsg
25351      * The message to display when no records are found (defaults to "No data to display")
25352      */
25353     emptyMsg : 'No data to display',
25354     /**
25355      * Customizable piece of the default paging text (defaults to "Page")
25356      * @type String
25357      */
25358     beforePageText : "Page",
25359     /**
25360      * Customizable piece of the default paging text (defaults to "of %0")
25361      * @type String
25362      */
25363     afterPageText : "of {0}",
25364     /**
25365      * Customizable piece of the default paging text (defaults to "First Page")
25366      * @type String
25367      */
25368     firstText : "First Page",
25369     /**
25370      * Customizable piece of the default paging text (defaults to "Previous Page")
25371      * @type String
25372      */
25373     prevText : "Previous Page",
25374     /**
25375      * Customizable piece of the default paging text (defaults to "Next Page")
25376      * @type String
25377      */
25378     nextText : "Next Page",
25379     /**
25380      * Customizable piece of the default paging text (defaults to "Last Page")
25381      * @type String
25382      */
25383     lastText : "Last Page",
25384     /**
25385      * Customizable piece of the default paging text (defaults to "Refresh")
25386      * @type String
25387      */
25388     refreshText : "Refresh",
25389
25390     buttons : false,
25391     // private
25392     onRender : function(ct, position) 
25393     {
25394         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25395         this.navgroup.parentId = this.id;
25396         this.navgroup.onRender(this.el, null);
25397         // add the buttons to the navgroup
25398         
25399         if(this.displayInfo){
25400             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25401             this.displayEl = this.el.select('.x-paging-info', true).first();
25402 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25403 //            this.displayEl = navel.el.select('span',true).first();
25404         }
25405         
25406         var _this = this;
25407         
25408         if(this.buttons){
25409             Roo.each(_this.buttons, function(e){ // this might need to use render????
25410                Roo.factory(e).render(_this.el);
25411             });
25412         }
25413             
25414         Roo.each(_this.toolbarItems, function(e) {
25415             _this.navgroup.addItem(e);
25416         });
25417         
25418         
25419         this.first = this.navgroup.addItem({
25420             tooltip: this.firstText,
25421             cls: "prev btn-outline-secondary",
25422             html : ' <i class="fa fa-step-backward"></i>',
25423             disabled: true,
25424             preventDefault: true,
25425             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25426         });
25427         
25428         this.prev =  this.navgroup.addItem({
25429             tooltip: this.prevText,
25430             cls: "prev btn-outline-secondary",
25431             html : ' <i class="fa fa-backward"></i>',
25432             disabled: true,
25433             preventDefault: true,
25434             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
25435         });
25436     //this.addSeparator();
25437         
25438         
25439         var field = this.navgroup.addItem( {
25440             tagtype : 'span',
25441             cls : 'x-paging-position  btn-outline-secondary',
25442              disabled: true,
25443             html : this.beforePageText  +
25444                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25445                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
25446          } ); //?? escaped?
25447         
25448         this.field = field.el.select('input', true).first();
25449         this.field.on("keydown", this.onPagingKeydown, this);
25450         this.field.on("focus", function(){this.dom.select();});
25451     
25452     
25453         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
25454         //this.field.setHeight(18);
25455         //this.addSeparator();
25456         this.next = this.navgroup.addItem({
25457             tooltip: this.nextText,
25458             cls: "next btn-outline-secondary",
25459             html : ' <i class="fa fa-forward"></i>',
25460             disabled: true,
25461             preventDefault: true,
25462             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
25463         });
25464         this.last = this.navgroup.addItem({
25465             tooltip: this.lastText,
25466             html : ' <i class="fa fa-step-forward"></i>',
25467             cls: "next btn-outline-secondary",
25468             disabled: true,
25469             preventDefault: true,
25470             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
25471         });
25472     //this.addSeparator();
25473         this.loading = this.navgroup.addItem({
25474             tooltip: this.refreshText,
25475             cls: "btn-outline-secondary",
25476             html : ' <i class="fa fa-refresh"></i>',
25477             preventDefault: true,
25478             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25479         });
25480         
25481     },
25482
25483     // private
25484     updateInfo : function(){
25485         if(this.displayEl){
25486             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25487             var msg = count == 0 ?
25488                 this.emptyMsg :
25489                 String.format(
25490                     this.displayMsg,
25491                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
25492                 );
25493             this.displayEl.update(msg);
25494         }
25495     },
25496
25497     // private
25498     onLoad : function(ds, r, o)
25499     {
25500         this.cursor = o.params.start ? o.params.start : 0;
25501         
25502         var d = this.getPageData(),
25503             ap = d.activePage,
25504             ps = d.pages;
25505         
25506         
25507         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25508         this.field.dom.value = ap;
25509         this.first.setDisabled(ap == 1);
25510         this.prev.setDisabled(ap == 1);
25511         this.next.setDisabled(ap == ps);
25512         this.last.setDisabled(ap == ps);
25513         this.loading.enable();
25514         this.updateInfo();
25515     },
25516
25517     // private
25518     getPageData : function(){
25519         var total = this.ds.getTotalCount();
25520         return {
25521             total : total,
25522             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25523             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25524         };
25525     },
25526
25527     // private
25528     onLoadError : function(){
25529         this.loading.enable();
25530     },
25531
25532     // private
25533     onPagingKeydown : function(e){
25534         var k = e.getKey();
25535         var d = this.getPageData();
25536         if(k == e.RETURN){
25537             var v = this.field.dom.value, pageNum;
25538             if(!v || isNaN(pageNum = parseInt(v, 10))){
25539                 this.field.dom.value = d.activePage;
25540                 return;
25541             }
25542             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25543             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25544             e.stopEvent();
25545         }
25546         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))
25547         {
25548           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25549           this.field.dom.value = pageNum;
25550           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25551           e.stopEvent();
25552         }
25553         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25554         {
25555           var v = this.field.dom.value, pageNum; 
25556           var increment = (e.shiftKey) ? 10 : 1;
25557           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25558                 increment *= -1;
25559           }
25560           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25561             this.field.dom.value = d.activePage;
25562             return;
25563           }
25564           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25565           {
25566             this.field.dom.value = parseInt(v, 10) + increment;
25567             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25568             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25569           }
25570           e.stopEvent();
25571         }
25572     },
25573
25574     // private
25575     beforeLoad : function(){
25576         if(this.loading){
25577             this.loading.disable();
25578         }
25579     },
25580
25581     // private
25582     onClick : function(which){
25583         
25584         var ds = this.ds;
25585         if (!ds) {
25586             return;
25587         }
25588         
25589         switch(which){
25590             case "first":
25591                 ds.load({params:{start: 0, limit: this.pageSize}});
25592             break;
25593             case "prev":
25594                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25595             break;
25596             case "next":
25597                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25598             break;
25599             case "last":
25600                 var total = ds.getTotalCount();
25601                 var extra = total % this.pageSize;
25602                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25603                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25604             break;
25605             case "refresh":
25606                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25607             break;
25608         }
25609     },
25610
25611     /**
25612      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25613      * @param {Roo.data.Store} store The data store to unbind
25614      */
25615     unbind : function(ds){
25616         ds.un("beforeload", this.beforeLoad, this);
25617         ds.un("load", this.onLoad, this);
25618         ds.un("loadexception", this.onLoadError, this);
25619         ds.un("remove", this.updateInfo, this);
25620         ds.un("add", this.updateInfo, this);
25621         this.ds = undefined;
25622     },
25623
25624     /**
25625      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25626      * @param {Roo.data.Store} store The data store to bind
25627      */
25628     bind : function(ds){
25629         ds.on("beforeload", this.beforeLoad, this);
25630         ds.on("load", this.onLoad, this);
25631         ds.on("loadexception", this.onLoadError, this);
25632         ds.on("remove", this.updateInfo, this);
25633         ds.on("add", this.updateInfo, this);
25634         this.ds = ds;
25635     }
25636 });/*
25637  * - LGPL
25638  *
25639  * element
25640  * 
25641  */
25642
25643 /**
25644  * @class Roo.bootstrap.MessageBar
25645  * @extends Roo.bootstrap.Component
25646  * Bootstrap MessageBar class
25647  * @cfg {String} html contents of the MessageBar
25648  * @cfg {String} weight (info | success | warning | danger) default info
25649  * @cfg {String} beforeClass insert the bar before the given class
25650  * @cfg {Boolean} closable (true | false) default false
25651  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25652  * 
25653  * @constructor
25654  * Create a new Element
25655  * @param {Object} config The config object
25656  */
25657
25658 Roo.bootstrap.MessageBar = function(config){
25659     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25660 };
25661
25662 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25663     
25664     html: '',
25665     weight: 'info',
25666     closable: false,
25667     fixed: false,
25668     beforeClass: 'bootstrap-sticky-wrap',
25669     
25670     getAutoCreate : function(){
25671         
25672         var cfg = {
25673             tag: 'div',
25674             cls: 'alert alert-dismissable alert-' + this.weight,
25675             cn: [
25676                 {
25677                     tag: 'span',
25678                     cls: 'message',
25679                     html: this.html || ''
25680                 }
25681             ]
25682         };
25683         
25684         if(this.fixed){
25685             cfg.cls += ' alert-messages-fixed';
25686         }
25687         
25688         if(this.closable){
25689             cfg.cn.push({
25690                 tag: 'button',
25691                 cls: 'close',
25692                 html: 'x'
25693             });
25694         }
25695         
25696         return cfg;
25697     },
25698     
25699     onRender : function(ct, position)
25700     {
25701         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25702         
25703         if(!this.el){
25704             var cfg = Roo.apply({},  this.getAutoCreate());
25705             cfg.id = Roo.id();
25706             
25707             if (this.cls) {
25708                 cfg.cls += ' ' + this.cls;
25709             }
25710             if (this.style) {
25711                 cfg.style = this.style;
25712             }
25713             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25714             
25715             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25716         }
25717         
25718         this.el.select('>button.close').on('click', this.hide, this);
25719         
25720     },
25721     
25722     show : function()
25723     {
25724         if (!this.rendered) {
25725             this.render();
25726         }
25727         
25728         this.el.show();
25729         
25730         this.fireEvent('show', this);
25731         
25732     },
25733     
25734     hide : function()
25735     {
25736         if (!this.rendered) {
25737             this.render();
25738         }
25739         
25740         this.el.hide();
25741         
25742         this.fireEvent('hide', this);
25743     },
25744     
25745     update : function()
25746     {
25747 //        var e = this.el.dom.firstChild;
25748 //        
25749 //        if(this.closable){
25750 //            e = e.nextSibling;
25751 //        }
25752 //        
25753 //        e.data = this.html || '';
25754
25755         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25756     }
25757    
25758 });
25759
25760  
25761
25762      /*
25763  * - LGPL
25764  *
25765  * Graph
25766  * 
25767  */
25768
25769
25770 /**
25771  * @class Roo.bootstrap.Graph
25772  * @extends Roo.bootstrap.Component
25773  * Bootstrap Graph class
25774 > Prameters
25775  -sm {number} sm 4
25776  -md {number} md 5
25777  @cfg {String} graphtype  bar | vbar | pie
25778  @cfg {number} g_x coodinator | centre x (pie)
25779  @cfg {number} g_y coodinator | centre y (pie)
25780  @cfg {number} g_r radius (pie)
25781  @cfg {number} g_height height of the chart (respected by all elements in the set)
25782  @cfg {number} g_width width of the chart (respected by all elements in the set)
25783  @cfg {Object} title The title of the chart
25784     
25785  -{Array}  values
25786  -opts (object) options for the chart 
25787      o {
25788      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25789      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25790      o vgutter (number)
25791      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.
25792      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25793      o to
25794      o stretch (boolean)
25795      o }
25796  -opts (object) options for the pie
25797      o{
25798      o cut
25799      o startAngle (number)
25800      o endAngle (number)
25801      } 
25802  *
25803  * @constructor
25804  * Create a new Input
25805  * @param {Object} config The config object
25806  */
25807
25808 Roo.bootstrap.Graph = function(config){
25809     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25810     
25811     this.addEvents({
25812         // img events
25813         /**
25814          * @event click
25815          * The img click event for the img.
25816          * @param {Roo.EventObject} e
25817          */
25818         "click" : true
25819     });
25820 };
25821
25822 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25823     
25824     sm: 4,
25825     md: 5,
25826     graphtype: 'bar',
25827     g_height: 250,
25828     g_width: 400,
25829     g_x: 50,
25830     g_y: 50,
25831     g_r: 30,
25832     opts:{
25833         //g_colors: this.colors,
25834         g_type: 'soft',
25835         g_gutter: '20%'
25836
25837     },
25838     title : false,
25839
25840     getAutoCreate : function(){
25841         
25842         var cfg = {
25843             tag: 'div',
25844             html : null
25845         };
25846         
25847         
25848         return  cfg;
25849     },
25850
25851     onRender : function(ct,position){
25852         
25853         
25854         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25855         
25856         if (typeof(Raphael) == 'undefined') {
25857             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25858             return;
25859         }
25860         
25861         this.raphael = Raphael(this.el.dom);
25862         
25863                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25864                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25865                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25866                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25867                 /*
25868                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25869                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25870                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25871                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25872                 
25873                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25874                 r.barchart(330, 10, 300, 220, data1);
25875                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25876                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25877                 */
25878                 
25879                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25880                 // r.barchart(30, 30, 560, 250,  xdata, {
25881                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25882                 //     axis : "0 0 1 1",
25883                 //     axisxlabels :  xdata
25884                 //     //yvalues : cols,
25885                    
25886                 // });
25887 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25888 //        
25889 //        this.load(null,xdata,{
25890 //                axis : "0 0 1 1",
25891 //                axisxlabels :  xdata
25892 //                });
25893
25894     },
25895
25896     load : function(graphtype,xdata,opts)
25897     {
25898         this.raphael.clear();
25899         if(!graphtype) {
25900             graphtype = this.graphtype;
25901         }
25902         if(!opts){
25903             opts = this.opts;
25904         }
25905         var r = this.raphael,
25906             fin = function () {
25907                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25908             },
25909             fout = function () {
25910                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25911             },
25912             pfin = function() {
25913                 this.sector.stop();
25914                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25915
25916                 if (this.label) {
25917                     this.label[0].stop();
25918                     this.label[0].attr({ r: 7.5 });
25919                     this.label[1].attr({ "font-weight": 800 });
25920                 }
25921             },
25922             pfout = function() {
25923                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25924
25925                 if (this.label) {
25926                     this.label[0].animate({ r: 5 }, 500, "bounce");
25927                     this.label[1].attr({ "font-weight": 400 });
25928                 }
25929             };
25930
25931         switch(graphtype){
25932             case 'bar':
25933                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25934                 break;
25935             case 'hbar':
25936                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25937                 break;
25938             case 'pie':
25939 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25940 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25941 //            
25942                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25943                 
25944                 break;
25945
25946         }
25947         
25948         if(this.title){
25949             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25950         }
25951         
25952     },
25953     
25954     setTitle: function(o)
25955     {
25956         this.title = o;
25957     },
25958     
25959     initEvents: function() {
25960         
25961         if(!this.href){
25962             this.el.on('click', this.onClick, this);
25963         }
25964     },
25965     
25966     onClick : function(e)
25967     {
25968         Roo.log('img onclick');
25969         this.fireEvent('click', this, e);
25970     }
25971    
25972 });
25973
25974  
25975 /*
25976  * - LGPL
25977  *
25978  * numberBox
25979  * 
25980  */
25981 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25982
25983 /**
25984  * @class Roo.bootstrap.dash.NumberBox
25985  * @extends Roo.bootstrap.Component
25986  * Bootstrap NumberBox class
25987  * @cfg {String} headline Box headline
25988  * @cfg {String} content Box content
25989  * @cfg {String} icon Box icon
25990  * @cfg {String} footer Footer text
25991  * @cfg {String} fhref Footer href
25992  * 
25993  * @constructor
25994  * Create a new NumberBox
25995  * @param {Object} config The config object
25996  */
25997
25998
25999 Roo.bootstrap.dash.NumberBox = function(config){
26000     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
26001     
26002 };
26003
26004 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
26005     
26006     headline : '',
26007     content : '',
26008     icon : '',
26009     footer : '',
26010     fhref : '',
26011     ficon : '',
26012     
26013     getAutoCreate : function(){
26014         
26015         var cfg = {
26016             tag : 'div',
26017             cls : 'small-box ',
26018             cn : [
26019                 {
26020                     tag : 'div',
26021                     cls : 'inner',
26022                     cn :[
26023                         {
26024                             tag : 'h3',
26025                             cls : 'roo-headline',
26026                             html : this.headline
26027                         },
26028                         {
26029                             tag : 'p',
26030                             cls : 'roo-content',
26031                             html : this.content
26032                         }
26033                     ]
26034                 }
26035             ]
26036         };
26037         
26038         if(this.icon){
26039             cfg.cn.push({
26040                 tag : 'div',
26041                 cls : 'icon',
26042                 cn :[
26043                     {
26044                         tag : 'i',
26045                         cls : 'ion ' + this.icon
26046                     }
26047                 ]
26048             });
26049         }
26050         
26051         if(this.footer){
26052             var footer = {
26053                 tag : 'a',
26054                 cls : 'small-box-footer',
26055                 href : this.fhref || '#',
26056                 html : this.footer
26057             };
26058             
26059             cfg.cn.push(footer);
26060             
26061         }
26062         
26063         return  cfg;
26064     },
26065
26066     onRender : function(ct,position){
26067         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
26068
26069
26070        
26071                 
26072     },
26073
26074     setHeadline: function (value)
26075     {
26076         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
26077     },
26078     
26079     setFooter: function (value, href)
26080     {
26081         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
26082         
26083         if(href){
26084             this.el.select('a.small-box-footer',true).first().attr('href', href);
26085         }
26086         
26087     },
26088
26089     setContent: function (value)
26090     {
26091         this.el.select('.roo-content',true).first().dom.innerHTML = value;
26092     },
26093
26094     initEvents: function() 
26095     {   
26096         
26097     }
26098     
26099 });
26100
26101  
26102 /*
26103  * - LGPL
26104  *
26105  * TabBox
26106  * 
26107  */
26108 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26109
26110 /**
26111  * @class Roo.bootstrap.dash.TabBox
26112  * @extends Roo.bootstrap.Component
26113  * Bootstrap TabBox class
26114  * @cfg {String} title Title of the TabBox
26115  * @cfg {String} icon Icon of the TabBox
26116  * @cfg {Boolean} showtabs (true|false) show the tabs default true
26117  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
26118  * 
26119  * @constructor
26120  * Create a new TabBox
26121  * @param {Object} config The config object
26122  */
26123
26124
26125 Roo.bootstrap.dash.TabBox = function(config){
26126     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
26127     this.addEvents({
26128         // raw events
26129         /**
26130          * @event addpane
26131          * When a pane is added
26132          * @param {Roo.bootstrap.dash.TabPane} pane
26133          */
26134         "addpane" : true,
26135         /**
26136          * @event activatepane
26137          * When a pane is activated
26138          * @param {Roo.bootstrap.dash.TabPane} pane
26139          */
26140         "activatepane" : true
26141         
26142          
26143     });
26144     
26145     this.panes = [];
26146 };
26147
26148 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
26149
26150     title : '',
26151     icon : false,
26152     showtabs : true,
26153     tabScrollable : false,
26154     
26155     getChildContainer : function()
26156     {
26157         return this.el.select('.tab-content', true).first();
26158     },
26159     
26160     getAutoCreate : function(){
26161         
26162         var header = {
26163             tag: 'li',
26164             cls: 'pull-left header',
26165             html: this.title,
26166             cn : []
26167         };
26168         
26169         if(this.icon){
26170             header.cn.push({
26171                 tag: 'i',
26172                 cls: 'fa ' + this.icon
26173             });
26174         }
26175         
26176         var h = {
26177             tag: 'ul',
26178             cls: 'nav nav-tabs pull-right',
26179             cn: [
26180                 header
26181             ]
26182         };
26183         
26184         if(this.tabScrollable){
26185             h = {
26186                 tag: 'div',
26187                 cls: 'tab-header',
26188                 cn: [
26189                     {
26190                         tag: 'ul',
26191                         cls: 'nav nav-tabs pull-right',
26192                         cn: [
26193                             header
26194                         ]
26195                     }
26196                 ]
26197             };
26198         }
26199         
26200         var cfg = {
26201             tag: 'div',
26202             cls: 'nav-tabs-custom',
26203             cn: [
26204                 h,
26205                 {
26206                     tag: 'div',
26207                     cls: 'tab-content no-padding',
26208                     cn: []
26209                 }
26210             ]
26211         };
26212
26213         return  cfg;
26214     },
26215     initEvents : function()
26216     {
26217         //Roo.log('add add pane handler');
26218         this.on('addpane', this.onAddPane, this);
26219     },
26220      /**
26221      * Updates the box title
26222      * @param {String} html to set the title to.
26223      */
26224     setTitle : function(value)
26225     {
26226         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
26227     },
26228     onAddPane : function(pane)
26229     {
26230         this.panes.push(pane);
26231         //Roo.log('addpane');
26232         //Roo.log(pane);
26233         // tabs are rendere left to right..
26234         if(!this.showtabs){
26235             return;
26236         }
26237         
26238         var ctr = this.el.select('.nav-tabs', true).first();
26239          
26240          
26241         var existing = ctr.select('.nav-tab',true);
26242         var qty = existing.getCount();;
26243         
26244         
26245         var tab = ctr.createChild({
26246             tag : 'li',
26247             cls : 'nav-tab' + (qty ? '' : ' active'),
26248             cn : [
26249                 {
26250                     tag : 'a',
26251                     href:'#',
26252                     html : pane.title
26253                 }
26254             ]
26255         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
26256         pane.tab = tab;
26257         
26258         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
26259         if (!qty) {
26260             pane.el.addClass('active');
26261         }
26262         
26263                 
26264     },
26265     onTabClick : function(ev,un,ob,pane)
26266     {
26267         //Roo.log('tab - prev default');
26268         ev.preventDefault();
26269         
26270         
26271         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
26272         pane.tab.addClass('active');
26273         //Roo.log(pane.title);
26274         this.getChildContainer().select('.tab-pane',true).removeClass('active');
26275         // technically we should have a deactivate event.. but maybe add later.
26276         // and it should not de-activate the selected tab...
26277         this.fireEvent('activatepane', pane);
26278         pane.el.addClass('active');
26279         pane.fireEvent('activate');
26280         
26281         
26282     },
26283     
26284     getActivePane : function()
26285     {
26286         var r = false;
26287         Roo.each(this.panes, function(p) {
26288             if(p.el.hasClass('active')){
26289                 r = p;
26290                 return false;
26291             }
26292             
26293             return;
26294         });
26295         
26296         return r;
26297     }
26298     
26299     
26300 });
26301
26302  
26303 /*
26304  * - LGPL
26305  *
26306  * Tab pane
26307  * 
26308  */
26309 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26310 /**
26311  * @class Roo.bootstrap.TabPane
26312  * @extends Roo.bootstrap.Component
26313  * Bootstrap TabPane class
26314  * @cfg {Boolean} active (false | true) Default false
26315  * @cfg {String} title title of panel
26316
26317  * 
26318  * @constructor
26319  * Create a new TabPane
26320  * @param {Object} config The config object
26321  */
26322
26323 Roo.bootstrap.dash.TabPane = function(config){
26324     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
26325     
26326     this.addEvents({
26327         // raw events
26328         /**
26329          * @event activate
26330          * When a pane is activated
26331          * @param {Roo.bootstrap.dash.TabPane} pane
26332          */
26333         "activate" : true
26334          
26335     });
26336 };
26337
26338 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
26339     
26340     active : false,
26341     title : '',
26342     
26343     // the tabBox that this is attached to.
26344     tab : false,
26345      
26346     getAutoCreate : function() 
26347     {
26348         var cfg = {
26349             tag: 'div',
26350             cls: 'tab-pane'
26351         };
26352         
26353         if(this.active){
26354             cfg.cls += ' active';
26355         }
26356         
26357         return cfg;
26358     },
26359     initEvents  : function()
26360     {
26361         //Roo.log('trigger add pane handler');
26362         this.parent().fireEvent('addpane', this)
26363     },
26364     
26365      /**
26366      * Updates the tab title 
26367      * @param {String} html to set the title to.
26368      */
26369     setTitle: function(str)
26370     {
26371         if (!this.tab) {
26372             return;
26373         }
26374         this.title = str;
26375         this.tab.select('a', true).first().dom.innerHTML = str;
26376         
26377     }
26378     
26379     
26380     
26381 });
26382
26383  
26384
26385
26386  /*
26387  * - LGPL
26388  *
26389  * menu
26390  * 
26391  */
26392 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26393
26394 /**
26395  * @class Roo.bootstrap.menu.Menu
26396  * @extends Roo.bootstrap.Component
26397  * Bootstrap Menu class - container for Menu
26398  * @cfg {String} html Text of the menu
26399  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26400  * @cfg {String} icon Font awesome icon
26401  * @cfg {String} pos Menu align to (top | bottom) default bottom
26402  * 
26403  * 
26404  * @constructor
26405  * Create a new Menu
26406  * @param {Object} config The config object
26407  */
26408
26409
26410 Roo.bootstrap.menu.Menu = function(config){
26411     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26412     
26413     this.addEvents({
26414         /**
26415          * @event beforeshow
26416          * Fires before this menu is displayed
26417          * @param {Roo.bootstrap.menu.Menu} this
26418          */
26419         beforeshow : true,
26420         /**
26421          * @event beforehide
26422          * Fires before this menu is hidden
26423          * @param {Roo.bootstrap.menu.Menu} this
26424          */
26425         beforehide : true,
26426         /**
26427          * @event show
26428          * Fires after this menu is displayed
26429          * @param {Roo.bootstrap.menu.Menu} this
26430          */
26431         show : true,
26432         /**
26433          * @event hide
26434          * Fires after this menu is hidden
26435          * @param {Roo.bootstrap.menu.Menu} this
26436          */
26437         hide : true,
26438         /**
26439          * @event click
26440          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26441          * @param {Roo.bootstrap.menu.Menu} this
26442          * @param {Roo.EventObject} e
26443          */
26444         click : true
26445     });
26446     
26447 };
26448
26449 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
26450     
26451     submenu : false,
26452     html : '',
26453     weight : 'default',
26454     icon : false,
26455     pos : 'bottom',
26456     
26457     
26458     getChildContainer : function() {
26459         if(this.isSubMenu){
26460             return this.el;
26461         }
26462         
26463         return this.el.select('ul.dropdown-menu', true).first();  
26464     },
26465     
26466     getAutoCreate : function()
26467     {
26468         var text = [
26469             {
26470                 tag : 'span',
26471                 cls : 'roo-menu-text',
26472                 html : this.html
26473             }
26474         ];
26475         
26476         if(this.icon){
26477             text.unshift({
26478                 tag : 'i',
26479                 cls : 'fa ' + this.icon
26480             })
26481         }
26482         
26483         
26484         var cfg = {
26485             tag : 'div',
26486             cls : 'btn-group',
26487             cn : [
26488                 {
26489                     tag : 'button',
26490                     cls : 'dropdown-button btn btn-' + this.weight,
26491                     cn : text
26492                 },
26493                 {
26494                     tag : 'button',
26495                     cls : 'dropdown-toggle btn btn-' + this.weight,
26496                     cn : [
26497                         {
26498                             tag : 'span',
26499                             cls : 'caret'
26500                         }
26501                     ]
26502                 },
26503                 {
26504                     tag : 'ul',
26505                     cls : 'dropdown-menu'
26506                 }
26507             ]
26508             
26509         };
26510         
26511         if(this.pos == 'top'){
26512             cfg.cls += ' dropup';
26513         }
26514         
26515         if(this.isSubMenu){
26516             cfg = {
26517                 tag : 'ul',
26518                 cls : 'dropdown-menu'
26519             }
26520         }
26521         
26522         return cfg;
26523     },
26524     
26525     onRender : function(ct, position)
26526     {
26527         this.isSubMenu = ct.hasClass('dropdown-submenu');
26528         
26529         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26530     },
26531     
26532     initEvents : function() 
26533     {
26534         if(this.isSubMenu){
26535             return;
26536         }
26537         
26538         this.hidden = true;
26539         
26540         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26541         this.triggerEl.on('click', this.onTriggerPress, this);
26542         
26543         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26544         this.buttonEl.on('click', this.onClick, this);
26545         
26546     },
26547     
26548     list : function()
26549     {
26550         if(this.isSubMenu){
26551             return this.el;
26552         }
26553         
26554         return this.el.select('ul.dropdown-menu', true).first();
26555     },
26556     
26557     onClick : function(e)
26558     {
26559         this.fireEvent("click", this, e);
26560     },
26561     
26562     onTriggerPress  : function(e)
26563     {   
26564         if (this.isVisible()) {
26565             this.hide();
26566         } else {
26567             this.show();
26568         }
26569     },
26570     
26571     isVisible : function(){
26572         return !this.hidden;
26573     },
26574     
26575     show : function()
26576     {
26577         this.fireEvent("beforeshow", this);
26578         
26579         this.hidden = false;
26580         this.el.addClass('open');
26581         
26582         Roo.get(document).on("mouseup", this.onMouseUp, this);
26583         
26584         this.fireEvent("show", this);
26585         
26586         
26587     },
26588     
26589     hide : function()
26590     {
26591         this.fireEvent("beforehide", this);
26592         
26593         this.hidden = true;
26594         this.el.removeClass('open');
26595         
26596         Roo.get(document).un("mouseup", this.onMouseUp);
26597         
26598         this.fireEvent("hide", this);
26599     },
26600     
26601     onMouseUp : function()
26602     {
26603         this.hide();
26604     }
26605     
26606 });
26607
26608  
26609  /*
26610  * - LGPL
26611  *
26612  * menu item
26613  * 
26614  */
26615 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26616
26617 /**
26618  * @class Roo.bootstrap.menu.Item
26619  * @extends Roo.bootstrap.Component
26620  * Bootstrap MenuItem class
26621  * @cfg {Boolean} submenu (true | false) default false
26622  * @cfg {String} html text of the item
26623  * @cfg {String} href the link
26624  * @cfg {Boolean} disable (true | false) default false
26625  * @cfg {Boolean} preventDefault (true | false) default true
26626  * @cfg {String} icon Font awesome icon
26627  * @cfg {String} pos Submenu align to (left | right) default right 
26628  * 
26629  * 
26630  * @constructor
26631  * Create a new Item
26632  * @param {Object} config The config object
26633  */
26634
26635
26636 Roo.bootstrap.menu.Item = function(config){
26637     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26638     this.addEvents({
26639         /**
26640          * @event mouseover
26641          * Fires when the mouse is hovering over this menu
26642          * @param {Roo.bootstrap.menu.Item} this
26643          * @param {Roo.EventObject} e
26644          */
26645         mouseover : true,
26646         /**
26647          * @event mouseout
26648          * Fires when the mouse exits this menu
26649          * @param {Roo.bootstrap.menu.Item} this
26650          * @param {Roo.EventObject} e
26651          */
26652         mouseout : true,
26653         // raw events
26654         /**
26655          * @event click
26656          * The raw click event for the entire grid.
26657          * @param {Roo.EventObject} e
26658          */
26659         click : true
26660     });
26661 };
26662
26663 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26664     
26665     submenu : false,
26666     href : '',
26667     html : '',
26668     preventDefault: true,
26669     disable : false,
26670     icon : false,
26671     pos : 'right',
26672     
26673     getAutoCreate : function()
26674     {
26675         var text = [
26676             {
26677                 tag : 'span',
26678                 cls : 'roo-menu-item-text',
26679                 html : this.html
26680             }
26681         ];
26682         
26683         if(this.icon){
26684             text.unshift({
26685                 tag : 'i',
26686                 cls : 'fa ' + this.icon
26687             })
26688         }
26689         
26690         var cfg = {
26691             tag : 'li',
26692             cn : [
26693                 {
26694                     tag : 'a',
26695                     href : this.href || '#',
26696                     cn : text
26697                 }
26698             ]
26699         };
26700         
26701         if(this.disable){
26702             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26703         }
26704         
26705         if(this.submenu){
26706             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26707             
26708             if(this.pos == 'left'){
26709                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26710             }
26711         }
26712         
26713         return cfg;
26714     },
26715     
26716     initEvents : function() 
26717     {
26718         this.el.on('mouseover', this.onMouseOver, this);
26719         this.el.on('mouseout', this.onMouseOut, this);
26720         
26721         this.el.select('a', true).first().on('click', this.onClick, this);
26722         
26723     },
26724     
26725     onClick : function(e)
26726     {
26727         if(this.preventDefault){
26728             e.preventDefault();
26729         }
26730         
26731         this.fireEvent("click", this, e);
26732     },
26733     
26734     onMouseOver : function(e)
26735     {
26736         if(this.submenu && this.pos == 'left'){
26737             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26738         }
26739         
26740         this.fireEvent("mouseover", this, e);
26741     },
26742     
26743     onMouseOut : function(e)
26744     {
26745         this.fireEvent("mouseout", this, e);
26746     }
26747 });
26748
26749  
26750
26751  /*
26752  * - LGPL
26753  *
26754  * menu separator
26755  * 
26756  */
26757 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26758
26759 /**
26760  * @class Roo.bootstrap.menu.Separator
26761  * @extends Roo.bootstrap.Component
26762  * Bootstrap Separator class
26763  * 
26764  * @constructor
26765  * Create a new Separator
26766  * @param {Object} config The config object
26767  */
26768
26769
26770 Roo.bootstrap.menu.Separator = function(config){
26771     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26772 };
26773
26774 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26775     
26776     getAutoCreate : function(){
26777         var cfg = {
26778             tag : 'li',
26779             cls: 'divider'
26780         };
26781         
26782         return cfg;
26783     }
26784    
26785 });
26786
26787  
26788
26789  /*
26790  * - LGPL
26791  *
26792  * Tooltip
26793  * 
26794  */
26795
26796 /**
26797  * @class Roo.bootstrap.Tooltip
26798  * Bootstrap Tooltip class
26799  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26800  * to determine which dom element triggers the tooltip.
26801  * 
26802  * It needs to add support for additional attributes like tooltip-position
26803  * 
26804  * @constructor
26805  * Create a new Toolti
26806  * @param {Object} config The config object
26807  */
26808
26809 Roo.bootstrap.Tooltip = function(config){
26810     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26811     
26812     this.alignment = Roo.bootstrap.Tooltip.alignment;
26813     
26814     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26815         this.alignment = config.alignment;
26816     }
26817     
26818 };
26819
26820 Roo.apply(Roo.bootstrap.Tooltip, {
26821     /**
26822      * @function init initialize tooltip monitoring.
26823      * @static
26824      */
26825     currentEl : false,
26826     currentTip : false,
26827     currentRegion : false,
26828     
26829     //  init : delay?
26830     
26831     init : function()
26832     {
26833         Roo.get(document).on('mouseover', this.enter ,this);
26834         Roo.get(document).on('mouseout', this.leave, this);
26835          
26836         
26837         this.currentTip = new Roo.bootstrap.Tooltip();
26838     },
26839     
26840     enter : function(ev)
26841     {
26842         var dom = ev.getTarget();
26843         
26844         //Roo.log(['enter',dom]);
26845         var el = Roo.fly(dom);
26846         if (this.currentEl) {
26847             //Roo.log(dom);
26848             //Roo.log(this.currentEl);
26849             //Roo.log(this.currentEl.contains(dom));
26850             if (this.currentEl == el) {
26851                 return;
26852             }
26853             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26854                 return;
26855             }
26856
26857         }
26858         
26859         if (this.currentTip.el) {
26860             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26861         }    
26862         //Roo.log(ev);
26863         
26864         if(!el || el.dom == document){
26865             return;
26866         }
26867         
26868         var bindEl = el;
26869         
26870         // you can not look for children, as if el is the body.. then everythign is the child..
26871         if (!el.attr('tooltip')) { //
26872             if (!el.select("[tooltip]").elements.length) {
26873                 return;
26874             }
26875             // is the mouse over this child...?
26876             bindEl = el.select("[tooltip]").first();
26877             var xy = ev.getXY();
26878             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26879                 //Roo.log("not in region.");
26880                 return;
26881             }
26882             //Roo.log("child element over..");
26883             
26884         }
26885         this.currentEl = bindEl;
26886         this.currentTip.bind(bindEl);
26887         this.currentRegion = Roo.lib.Region.getRegion(dom);
26888         this.currentTip.enter();
26889         
26890     },
26891     leave : function(ev)
26892     {
26893         var dom = ev.getTarget();
26894         //Roo.log(['leave',dom]);
26895         if (!this.currentEl) {
26896             return;
26897         }
26898         
26899         
26900         if (dom != this.currentEl.dom) {
26901             return;
26902         }
26903         var xy = ev.getXY();
26904         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26905             return;
26906         }
26907         // only activate leave if mouse cursor is outside... bounding box..
26908         
26909         
26910         
26911         
26912         if (this.currentTip) {
26913             this.currentTip.leave();
26914         }
26915         //Roo.log('clear currentEl');
26916         this.currentEl = false;
26917         
26918         
26919     },
26920     alignment : {
26921         'left' : ['r-l', [-2,0], 'right'],
26922         'right' : ['l-r', [2,0], 'left'],
26923         'bottom' : ['t-b', [0,2], 'top'],
26924         'top' : [ 'b-t', [0,-2], 'bottom']
26925     }
26926     
26927 });
26928
26929
26930 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26931     
26932     
26933     bindEl : false,
26934     
26935     delay : null, // can be { show : 300 , hide: 500}
26936     
26937     timeout : null,
26938     
26939     hoverState : null, //???
26940     
26941     placement : 'bottom', 
26942     
26943     alignment : false,
26944     
26945     getAutoCreate : function(){
26946     
26947         var cfg = {
26948            cls : 'tooltip',
26949            role : 'tooltip',
26950            cn : [
26951                 {
26952                     cls : 'tooltip-arrow'
26953                 },
26954                 {
26955                     cls : 'tooltip-inner'
26956                 }
26957            ]
26958         };
26959         
26960         return cfg;
26961     },
26962     bind : function(el)
26963     {
26964         this.bindEl = el;
26965     },
26966       
26967     
26968     enter : function () {
26969        
26970         if (this.timeout != null) {
26971             clearTimeout(this.timeout);
26972         }
26973         
26974         this.hoverState = 'in';
26975          //Roo.log("enter - show");
26976         if (!this.delay || !this.delay.show) {
26977             this.show();
26978             return;
26979         }
26980         var _t = this;
26981         this.timeout = setTimeout(function () {
26982             if (_t.hoverState == 'in') {
26983                 _t.show();
26984             }
26985         }, this.delay.show);
26986     },
26987     leave : function()
26988     {
26989         clearTimeout(this.timeout);
26990     
26991         this.hoverState = 'out';
26992          if (!this.delay || !this.delay.hide) {
26993             this.hide();
26994             return;
26995         }
26996        
26997         var _t = this;
26998         this.timeout = setTimeout(function () {
26999             //Roo.log("leave - timeout");
27000             
27001             if (_t.hoverState == 'out') {
27002                 _t.hide();
27003                 Roo.bootstrap.Tooltip.currentEl = false;
27004             }
27005         }, delay);
27006     },
27007     
27008     show : function (msg)
27009     {
27010         if (!this.el) {
27011             this.render(document.body);
27012         }
27013         // set content.
27014         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
27015         
27016         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
27017         
27018         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
27019         
27020         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
27021         
27022         var placement = typeof this.placement == 'function' ?
27023             this.placement.call(this, this.el, on_el) :
27024             this.placement;
27025             
27026         var autoToken = /\s?auto?\s?/i;
27027         var autoPlace = autoToken.test(placement);
27028         if (autoPlace) {
27029             placement = placement.replace(autoToken, '') || 'top';
27030         }
27031         
27032         //this.el.detach()
27033         //this.el.setXY([0,0]);
27034         this.el.show();
27035         //this.el.dom.style.display='block';
27036         
27037         //this.el.appendTo(on_el);
27038         
27039         var p = this.getPosition();
27040         var box = this.el.getBox();
27041         
27042         if (autoPlace) {
27043             // fixme..
27044         }
27045         
27046         var align = this.alignment[placement];
27047         
27048         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
27049         
27050         if(placement == 'top' || placement == 'bottom'){
27051             if(xy[0] < 0){
27052                 placement = 'right';
27053             }
27054             
27055             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
27056                 placement = 'left';
27057             }
27058             
27059             var scroll = Roo.select('body', true).first().getScroll();
27060             
27061             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
27062                 placement = 'top';
27063             }
27064             
27065             align = this.alignment[placement];
27066         }
27067         
27068         this.el.alignTo(this.bindEl, align[0],align[1]);
27069         //var arrow = this.el.select('.arrow',true).first();
27070         //arrow.set(align[2], 
27071         
27072         this.el.addClass(placement);
27073         
27074         this.el.addClass('in fade');
27075         
27076         this.hoverState = null;
27077         
27078         if (this.el.hasClass('fade')) {
27079             // fade it?
27080         }
27081         
27082     },
27083     hide : function()
27084     {
27085          
27086         if (!this.el) {
27087             return;
27088         }
27089         //this.el.setXY([0,0]);
27090         this.el.removeClass('in');
27091         //this.el.hide();
27092         
27093     }
27094     
27095 });
27096  
27097
27098  /*
27099  * - LGPL
27100  *
27101  * Location Picker
27102  * 
27103  */
27104
27105 /**
27106  * @class Roo.bootstrap.LocationPicker
27107  * @extends Roo.bootstrap.Component
27108  * Bootstrap LocationPicker class
27109  * @cfg {Number} latitude Position when init default 0
27110  * @cfg {Number} longitude Position when init default 0
27111  * @cfg {Number} zoom default 15
27112  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
27113  * @cfg {Boolean} mapTypeControl default false
27114  * @cfg {Boolean} disableDoubleClickZoom default false
27115  * @cfg {Boolean} scrollwheel default true
27116  * @cfg {Boolean} streetViewControl default false
27117  * @cfg {Number} radius default 0
27118  * @cfg {String} locationName
27119  * @cfg {Boolean} draggable default true
27120  * @cfg {Boolean} enableAutocomplete default false
27121  * @cfg {Boolean} enableReverseGeocode default true
27122  * @cfg {String} markerTitle
27123  * 
27124  * @constructor
27125  * Create a new LocationPicker
27126  * @param {Object} config The config object
27127  */
27128
27129
27130 Roo.bootstrap.LocationPicker = function(config){
27131     
27132     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
27133     
27134     this.addEvents({
27135         /**
27136          * @event initial
27137          * Fires when the picker initialized.
27138          * @param {Roo.bootstrap.LocationPicker} this
27139          * @param {Google Location} location
27140          */
27141         initial : true,
27142         /**
27143          * @event positionchanged
27144          * Fires when the picker position changed.
27145          * @param {Roo.bootstrap.LocationPicker} this
27146          * @param {Google Location} location
27147          */
27148         positionchanged : true,
27149         /**
27150          * @event resize
27151          * Fires when the map resize.
27152          * @param {Roo.bootstrap.LocationPicker} this
27153          */
27154         resize : true,
27155         /**
27156          * @event show
27157          * Fires when the map show.
27158          * @param {Roo.bootstrap.LocationPicker} this
27159          */
27160         show : true,
27161         /**
27162          * @event hide
27163          * Fires when the map hide.
27164          * @param {Roo.bootstrap.LocationPicker} this
27165          */
27166         hide : true,
27167         /**
27168          * @event mapClick
27169          * Fires when click the map.
27170          * @param {Roo.bootstrap.LocationPicker} this
27171          * @param {Map event} e
27172          */
27173         mapClick : true,
27174         /**
27175          * @event mapRightClick
27176          * Fires when right click the map.
27177          * @param {Roo.bootstrap.LocationPicker} this
27178          * @param {Map event} e
27179          */
27180         mapRightClick : true,
27181         /**
27182          * @event markerClick
27183          * Fires when click the marker.
27184          * @param {Roo.bootstrap.LocationPicker} this
27185          * @param {Map event} e
27186          */
27187         markerClick : true,
27188         /**
27189          * @event markerRightClick
27190          * Fires when right click the marker.
27191          * @param {Roo.bootstrap.LocationPicker} this
27192          * @param {Map event} e
27193          */
27194         markerRightClick : true,
27195         /**
27196          * @event OverlayViewDraw
27197          * Fires when OverlayView Draw
27198          * @param {Roo.bootstrap.LocationPicker} this
27199          */
27200         OverlayViewDraw : true,
27201         /**
27202          * @event OverlayViewOnAdd
27203          * Fires when OverlayView Draw
27204          * @param {Roo.bootstrap.LocationPicker} this
27205          */
27206         OverlayViewOnAdd : true,
27207         /**
27208          * @event OverlayViewOnRemove
27209          * Fires when OverlayView Draw
27210          * @param {Roo.bootstrap.LocationPicker} this
27211          */
27212         OverlayViewOnRemove : true,
27213         /**
27214          * @event OverlayViewShow
27215          * Fires when OverlayView Draw
27216          * @param {Roo.bootstrap.LocationPicker} this
27217          * @param {Pixel} cpx
27218          */
27219         OverlayViewShow : true,
27220         /**
27221          * @event OverlayViewHide
27222          * Fires when OverlayView Draw
27223          * @param {Roo.bootstrap.LocationPicker} this
27224          */
27225         OverlayViewHide : true,
27226         /**
27227          * @event loadexception
27228          * Fires when load google lib failed.
27229          * @param {Roo.bootstrap.LocationPicker} this
27230          */
27231         loadexception : true
27232     });
27233         
27234 };
27235
27236 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
27237     
27238     gMapContext: false,
27239     
27240     latitude: 0,
27241     longitude: 0,
27242     zoom: 15,
27243     mapTypeId: false,
27244     mapTypeControl: false,
27245     disableDoubleClickZoom: false,
27246     scrollwheel: true,
27247     streetViewControl: false,
27248     radius: 0,
27249     locationName: '',
27250     draggable: true,
27251     enableAutocomplete: false,
27252     enableReverseGeocode: true,
27253     markerTitle: '',
27254     
27255     getAutoCreate: function()
27256     {
27257
27258         var cfg = {
27259             tag: 'div',
27260             cls: 'roo-location-picker'
27261         };
27262         
27263         return cfg
27264     },
27265     
27266     initEvents: function(ct, position)
27267     {       
27268         if(!this.el.getWidth() || this.isApplied()){
27269             return;
27270         }
27271         
27272         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27273         
27274         this.initial();
27275     },
27276     
27277     initial: function()
27278     {
27279         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
27280             this.fireEvent('loadexception', this);
27281             return;
27282         }
27283         
27284         if(!this.mapTypeId){
27285             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
27286         }
27287         
27288         this.gMapContext = this.GMapContext();
27289         
27290         this.initOverlayView();
27291         
27292         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
27293         
27294         var _this = this;
27295                 
27296         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
27297             _this.setPosition(_this.gMapContext.marker.position);
27298         });
27299         
27300         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
27301             _this.fireEvent('mapClick', this, event);
27302             
27303         });
27304
27305         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
27306             _this.fireEvent('mapRightClick', this, event);
27307             
27308         });
27309         
27310         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
27311             _this.fireEvent('markerClick', this, event);
27312             
27313         });
27314
27315         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
27316             _this.fireEvent('markerRightClick', this, event);
27317             
27318         });
27319         
27320         this.setPosition(this.gMapContext.location);
27321         
27322         this.fireEvent('initial', this, this.gMapContext.location);
27323     },
27324     
27325     initOverlayView: function()
27326     {
27327         var _this = this;
27328         
27329         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
27330             
27331             draw: function()
27332             {
27333                 _this.fireEvent('OverlayViewDraw', _this);
27334             },
27335             
27336             onAdd: function()
27337             {
27338                 _this.fireEvent('OverlayViewOnAdd', _this);
27339             },
27340             
27341             onRemove: function()
27342             {
27343                 _this.fireEvent('OverlayViewOnRemove', _this);
27344             },
27345             
27346             show: function(cpx)
27347             {
27348                 _this.fireEvent('OverlayViewShow', _this, cpx);
27349             },
27350             
27351             hide: function()
27352             {
27353                 _this.fireEvent('OverlayViewHide', _this);
27354             }
27355             
27356         });
27357     },
27358     
27359     fromLatLngToContainerPixel: function(event)
27360     {
27361         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27362     },
27363     
27364     isApplied: function() 
27365     {
27366         return this.getGmapContext() == false ? false : true;
27367     },
27368     
27369     getGmapContext: function() 
27370     {
27371         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27372     },
27373     
27374     GMapContext: function() 
27375     {
27376         var position = new google.maps.LatLng(this.latitude, this.longitude);
27377         
27378         var _map = new google.maps.Map(this.el.dom, {
27379             center: position,
27380             zoom: this.zoom,
27381             mapTypeId: this.mapTypeId,
27382             mapTypeControl: this.mapTypeControl,
27383             disableDoubleClickZoom: this.disableDoubleClickZoom,
27384             scrollwheel: this.scrollwheel,
27385             streetViewControl: this.streetViewControl,
27386             locationName: this.locationName,
27387             draggable: this.draggable,
27388             enableAutocomplete: this.enableAutocomplete,
27389             enableReverseGeocode: this.enableReverseGeocode
27390         });
27391         
27392         var _marker = new google.maps.Marker({
27393             position: position,
27394             map: _map,
27395             title: this.markerTitle,
27396             draggable: this.draggable
27397         });
27398         
27399         return {
27400             map: _map,
27401             marker: _marker,
27402             circle: null,
27403             location: position,
27404             radius: this.radius,
27405             locationName: this.locationName,
27406             addressComponents: {
27407                 formatted_address: null,
27408                 addressLine1: null,
27409                 addressLine2: null,
27410                 streetName: null,
27411                 streetNumber: null,
27412                 city: null,
27413                 district: null,
27414                 state: null,
27415                 stateOrProvince: null
27416             },
27417             settings: this,
27418             domContainer: this.el.dom,
27419             geodecoder: new google.maps.Geocoder()
27420         };
27421     },
27422     
27423     drawCircle: function(center, radius, options) 
27424     {
27425         if (this.gMapContext.circle != null) {
27426             this.gMapContext.circle.setMap(null);
27427         }
27428         if (radius > 0) {
27429             radius *= 1;
27430             options = Roo.apply({}, options, {
27431                 strokeColor: "#0000FF",
27432                 strokeOpacity: .35,
27433                 strokeWeight: 2,
27434                 fillColor: "#0000FF",
27435                 fillOpacity: .2
27436             });
27437             
27438             options.map = this.gMapContext.map;
27439             options.radius = radius;
27440             options.center = center;
27441             this.gMapContext.circle = new google.maps.Circle(options);
27442             return this.gMapContext.circle;
27443         }
27444         
27445         return null;
27446     },
27447     
27448     setPosition: function(location) 
27449     {
27450         this.gMapContext.location = location;
27451         this.gMapContext.marker.setPosition(location);
27452         this.gMapContext.map.panTo(location);
27453         this.drawCircle(location, this.gMapContext.radius, {});
27454         
27455         var _this = this;
27456         
27457         if (this.gMapContext.settings.enableReverseGeocode) {
27458             this.gMapContext.geodecoder.geocode({
27459                 latLng: this.gMapContext.location
27460             }, function(results, status) {
27461                 
27462                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27463                     _this.gMapContext.locationName = results[0].formatted_address;
27464                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27465                     
27466                     _this.fireEvent('positionchanged', this, location);
27467                 }
27468             });
27469             
27470             return;
27471         }
27472         
27473         this.fireEvent('positionchanged', this, location);
27474     },
27475     
27476     resize: function()
27477     {
27478         google.maps.event.trigger(this.gMapContext.map, "resize");
27479         
27480         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27481         
27482         this.fireEvent('resize', this);
27483     },
27484     
27485     setPositionByLatLng: function(latitude, longitude)
27486     {
27487         this.setPosition(new google.maps.LatLng(latitude, longitude));
27488     },
27489     
27490     getCurrentPosition: function() 
27491     {
27492         return {
27493             latitude: this.gMapContext.location.lat(),
27494             longitude: this.gMapContext.location.lng()
27495         };
27496     },
27497     
27498     getAddressName: function() 
27499     {
27500         return this.gMapContext.locationName;
27501     },
27502     
27503     getAddressComponents: function() 
27504     {
27505         return this.gMapContext.addressComponents;
27506     },
27507     
27508     address_component_from_google_geocode: function(address_components) 
27509     {
27510         var result = {};
27511         
27512         for (var i = 0; i < address_components.length; i++) {
27513             var component = address_components[i];
27514             if (component.types.indexOf("postal_code") >= 0) {
27515                 result.postalCode = component.short_name;
27516             } else if (component.types.indexOf("street_number") >= 0) {
27517                 result.streetNumber = component.short_name;
27518             } else if (component.types.indexOf("route") >= 0) {
27519                 result.streetName = component.short_name;
27520             } else if (component.types.indexOf("neighborhood") >= 0) {
27521                 result.city = component.short_name;
27522             } else if (component.types.indexOf("locality") >= 0) {
27523                 result.city = component.short_name;
27524             } else if (component.types.indexOf("sublocality") >= 0) {
27525                 result.district = component.short_name;
27526             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27527                 result.stateOrProvince = component.short_name;
27528             } else if (component.types.indexOf("country") >= 0) {
27529                 result.country = component.short_name;
27530             }
27531         }
27532         
27533         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27534         result.addressLine2 = "";
27535         return result;
27536     },
27537     
27538     setZoomLevel: function(zoom)
27539     {
27540         this.gMapContext.map.setZoom(zoom);
27541     },
27542     
27543     show: function()
27544     {
27545         if(!this.el){
27546             return;
27547         }
27548         
27549         this.el.show();
27550         
27551         this.resize();
27552         
27553         this.fireEvent('show', this);
27554     },
27555     
27556     hide: function()
27557     {
27558         if(!this.el){
27559             return;
27560         }
27561         
27562         this.el.hide();
27563         
27564         this.fireEvent('hide', this);
27565     }
27566     
27567 });
27568
27569 Roo.apply(Roo.bootstrap.LocationPicker, {
27570     
27571     OverlayView : function(map, options)
27572     {
27573         options = options || {};
27574         
27575         this.setMap(map);
27576     }
27577     
27578     
27579 });/**
27580  * @class Roo.bootstrap.Alert
27581  * @extends Roo.bootstrap.Component
27582  * Bootstrap Alert class - shows an alert area box
27583  * eg
27584  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27585   Enter a valid email address
27586 </div>
27587  * @licence LGPL
27588  * @cfg {String} title The title of alert
27589  * @cfg {String} html The content of alert
27590  * @cfg {String} weight (  success | info | warning | danger )
27591  * @cfg {String} faicon font-awesomeicon
27592  * 
27593  * @constructor
27594  * Create a new alert
27595  * @param {Object} config The config object
27596  */
27597
27598
27599 Roo.bootstrap.Alert = function(config){
27600     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27601     
27602 };
27603
27604 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27605     
27606     title: '',
27607     html: '',
27608     weight: false,
27609     faicon: false,
27610     
27611     getAutoCreate : function()
27612     {
27613         
27614         var cfg = {
27615             tag : 'div',
27616             cls : 'alert',
27617             cn : [
27618                 {
27619                     tag : 'i',
27620                     cls : 'roo-alert-icon'
27621                     
27622                 },
27623                 {
27624                     tag : 'b',
27625                     cls : 'roo-alert-title',
27626                     html : this.title
27627                 },
27628                 {
27629                     tag : 'span',
27630                     cls : 'roo-alert-text',
27631                     html : this.html
27632                 }
27633             ]
27634         };
27635         
27636         if(this.faicon){
27637             cfg.cn[0].cls += ' fa ' + this.faicon;
27638         }
27639         
27640         if(this.weight){
27641             cfg.cls += ' alert-' + this.weight;
27642         }
27643         
27644         return cfg;
27645     },
27646     
27647     initEvents: function() 
27648     {
27649         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27650     },
27651     
27652     setTitle : function(str)
27653     {
27654         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27655     },
27656     
27657     setText : function(str)
27658     {
27659         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27660     },
27661     
27662     setWeight : function(weight)
27663     {
27664         if(this.weight){
27665             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27666         }
27667         
27668         this.weight = weight;
27669         
27670         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27671     },
27672     
27673     setIcon : function(icon)
27674     {
27675         if(this.faicon){
27676             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27677         }
27678         
27679         this.faicon = icon;
27680         
27681         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27682     },
27683     
27684     hide: function() 
27685     {
27686         this.el.hide();   
27687     },
27688     
27689     show: function() 
27690     {  
27691         this.el.show();   
27692     }
27693     
27694 });
27695
27696  
27697 /*
27698 * Licence: LGPL
27699 */
27700
27701 /**
27702  * @class Roo.bootstrap.UploadCropbox
27703  * @extends Roo.bootstrap.Component
27704  * Bootstrap UploadCropbox class
27705  * @cfg {String} emptyText show when image has been loaded
27706  * @cfg {String} rotateNotify show when image too small to rotate
27707  * @cfg {Number} errorTimeout default 3000
27708  * @cfg {Number} minWidth default 300
27709  * @cfg {Number} minHeight default 300
27710  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27711  * @cfg {Boolean} isDocument (true|false) default false
27712  * @cfg {String} url action url
27713  * @cfg {String} paramName default 'imageUpload'
27714  * @cfg {String} method default POST
27715  * @cfg {Boolean} loadMask (true|false) default true
27716  * @cfg {Boolean} loadingText default 'Loading...'
27717  * 
27718  * @constructor
27719  * Create a new UploadCropbox
27720  * @param {Object} config The config object
27721  */
27722
27723 Roo.bootstrap.UploadCropbox = function(config){
27724     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27725     
27726     this.addEvents({
27727         /**
27728          * @event beforeselectfile
27729          * Fire before select file
27730          * @param {Roo.bootstrap.UploadCropbox} this
27731          */
27732         "beforeselectfile" : true,
27733         /**
27734          * @event initial
27735          * Fire after initEvent
27736          * @param {Roo.bootstrap.UploadCropbox} this
27737          */
27738         "initial" : true,
27739         /**
27740          * @event crop
27741          * Fire after initEvent
27742          * @param {Roo.bootstrap.UploadCropbox} this
27743          * @param {String} data
27744          */
27745         "crop" : true,
27746         /**
27747          * @event prepare
27748          * Fire when preparing the file data
27749          * @param {Roo.bootstrap.UploadCropbox} this
27750          * @param {Object} file
27751          */
27752         "prepare" : true,
27753         /**
27754          * @event exception
27755          * Fire when get exception
27756          * @param {Roo.bootstrap.UploadCropbox} this
27757          * @param {XMLHttpRequest} xhr
27758          */
27759         "exception" : true,
27760         /**
27761          * @event beforeloadcanvas
27762          * Fire before load the canvas
27763          * @param {Roo.bootstrap.UploadCropbox} this
27764          * @param {String} src
27765          */
27766         "beforeloadcanvas" : true,
27767         /**
27768          * @event trash
27769          * Fire when trash image
27770          * @param {Roo.bootstrap.UploadCropbox} this
27771          */
27772         "trash" : true,
27773         /**
27774          * @event download
27775          * Fire when download the image
27776          * @param {Roo.bootstrap.UploadCropbox} this
27777          */
27778         "download" : true,
27779         /**
27780          * @event footerbuttonclick
27781          * Fire when footerbuttonclick
27782          * @param {Roo.bootstrap.UploadCropbox} this
27783          * @param {String} type
27784          */
27785         "footerbuttonclick" : true,
27786         /**
27787          * @event resize
27788          * Fire when resize
27789          * @param {Roo.bootstrap.UploadCropbox} this
27790          */
27791         "resize" : true,
27792         /**
27793          * @event rotate
27794          * Fire when rotate the image
27795          * @param {Roo.bootstrap.UploadCropbox} this
27796          * @param {String} pos
27797          */
27798         "rotate" : true,
27799         /**
27800          * @event inspect
27801          * Fire when inspect the file
27802          * @param {Roo.bootstrap.UploadCropbox} this
27803          * @param {Object} file
27804          */
27805         "inspect" : true,
27806         /**
27807          * @event upload
27808          * Fire when xhr upload the file
27809          * @param {Roo.bootstrap.UploadCropbox} this
27810          * @param {Object} data
27811          */
27812         "upload" : true,
27813         /**
27814          * @event arrange
27815          * Fire when arrange the file data
27816          * @param {Roo.bootstrap.UploadCropbox} this
27817          * @param {Object} formData
27818          */
27819         "arrange" : true
27820     });
27821     
27822     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27823 };
27824
27825 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27826     
27827     emptyText : 'Click to upload image',
27828     rotateNotify : 'Image is too small to rotate',
27829     errorTimeout : 3000,
27830     scale : 0,
27831     baseScale : 1,
27832     rotate : 0,
27833     dragable : false,
27834     pinching : false,
27835     mouseX : 0,
27836     mouseY : 0,
27837     cropData : false,
27838     minWidth : 300,
27839     minHeight : 300,
27840     file : false,
27841     exif : {},
27842     baseRotate : 1,
27843     cropType : 'image/jpeg',
27844     buttons : false,
27845     canvasLoaded : false,
27846     isDocument : false,
27847     method : 'POST',
27848     paramName : 'imageUpload',
27849     loadMask : true,
27850     loadingText : 'Loading...',
27851     maskEl : false,
27852     
27853     getAutoCreate : function()
27854     {
27855         var cfg = {
27856             tag : 'div',
27857             cls : 'roo-upload-cropbox',
27858             cn : [
27859                 {
27860                     tag : 'input',
27861                     cls : 'roo-upload-cropbox-selector',
27862                     type : 'file'
27863                 },
27864                 {
27865                     tag : 'div',
27866                     cls : 'roo-upload-cropbox-body',
27867                     style : 'cursor:pointer',
27868                     cn : [
27869                         {
27870                             tag : 'div',
27871                             cls : 'roo-upload-cropbox-preview'
27872                         },
27873                         {
27874                             tag : 'div',
27875                             cls : 'roo-upload-cropbox-thumb'
27876                         },
27877                         {
27878                             tag : 'div',
27879                             cls : 'roo-upload-cropbox-empty-notify',
27880                             html : this.emptyText
27881                         },
27882                         {
27883                             tag : 'div',
27884                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27885                             html : this.rotateNotify
27886                         }
27887                     ]
27888                 },
27889                 {
27890                     tag : 'div',
27891                     cls : 'roo-upload-cropbox-footer',
27892                     cn : {
27893                         tag : 'div',
27894                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27895                         cn : []
27896                     }
27897                 }
27898             ]
27899         };
27900         
27901         return cfg;
27902     },
27903     
27904     onRender : function(ct, position)
27905     {
27906         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27907         
27908         if (this.buttons.length) {
27909             
27910             Roo.each(this.buttons, function(bb) {
27911                 
27912                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27913                 
27914                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27915                 
27916             }, this);
27917         }
27918         
27919         if(this.loadMask){
27920             this.maskEl = this.el;
27921         }
27922     },
27923     
27924     initEvents : function()
27925     {
27926         this.urlAPI = (window.createObjectURL && window) || 
27927                                 (window.URL && URL.revokeObjectURL && URL) || 
27928                                 (window.webkitURL && webkitURL);
27929                         
27930         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27931         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27932         
27933         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27934         this.selectorEl.hide();
27935         
27936         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27937         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27938         
27939         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27940         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27941         this.thumbEl.hide();
27942         
27943         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27944         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27945         
27946         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27947         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27948         this.errorEl.hide();
27949         
27950         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27951         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27952         this.footerEl.hide();
27953         
27954         this.setThumbBoxSize();
27955         
27956         this.bind();
27957         
27958         this.resize();
27959         
27960         this.fireEvent('initial', this);
27961     },
27962
27963     bind : function()
27964     {
27965         var _this = this;
27966         
27967         window.addEventListener("resize", function() { _this.resize(); } );
27968         
27969         this.bodyEl.on('click', this.beforeSelectFile, this);
27970         
27971         if(Roo.isTouch){
27972             this.bodyEl.on('touchstart', this.onTouchStart, this);
27973             this.bodyEl.on('touchmove', this.onTouchMove, this);
27974             this.bodyEl.on('touchend', this.onTouchEnd, this);
27975         }
27976         
27977         if(!Roo.isTouch){
27978             this.bodyEl.on('mousedown', this.onMouseDown, this);
27979             this.bodyEl.on('mousemove', this.onMouseMove, this);
27980             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27981             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27982             Roo.get(document).on('mouseup', this.onMouseUp, this);
27983         }
27984         
27985         this.selectorEl.on('change', this.onFileSelected, this);
27986     },
27987     
27988     reset : function()
27989     {    
27990         this.scale = 0;
27991         this.baseScale = 1;
27992         this.rotate = 0;
27993         this.baseRotate = 1;
27994         this.dragable = false;
27995         this.pinching = false;
27996         this.mouseX = 0;
27997         this.mouseY = 0;
27998         this.cropData = false;
27999         this.notifyEl.dom.innerHTML = this.emptyText;
28000         
28001         this.selectorEl.dom.value = '';
28002         
28003     },
28004     
28005     resize : function()
28006     {
28007         if(this.fireEvent('resize', this) != false){
28008             this.setThumbBoxPosition();
28009             this.setCanvasPosition();
28010         }
28011     },
28012     
28013     onFooterButtonClick : function(e, el, o, type)
28014     {
28015         switch (type) {
28016             case 'rotate-left' :
28017                 this.onRotateLeft(e);
28018                 break;
28019             case 'rotate-right' :
28020                 this.onRotateRight(e);
28021                 break;
28022             case 'picture' :
28023                 this.beforeSelectFile(e);
28024                 break;
28025             case 'trash' :
28026                 this.trash(e);
28027                 break;
28028             case 'crop' :
28029                 this.crop(e);
28030                 break;
28031             case 'download' :
28032                 this.download(e);
28033                 break;
28034             default :
28035                 break;
28036         }
28037         
28038         this.fireEvent('footerbuttonclick', this, type);
28039     },
28040     
28041     beforeSelectFile : function(e)
28042     {
28043         e.preventDefault();
28044         
28045         if(this.fireEvent('beforeselectfile', this) != false){
28046             this.selectorEl.dom.click();
28047         }
28048     },
28049     
28050     onFileSelected : function(e)
28051     {
28052         e.preventDefault();
28053         
28054         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28055             return;
28056         }
28057         
28058         var file = this.selectorEl.dom.files[0];
28059         
28060         if(this.fireEvent('inspect', this, file) != false){
28061             this.prepare(file);
28062         }
28063         
28064     },
28065     
28066     trash : function(e)
28067     {
28068         this.fireEvent('trash', this);
28069     },
28070     
28071     download : function(e)
28072     {
28073         this.fireEvent('download', this);
28074     },
28075     
28076     loadCanvas : function(src)
28077     {   
28078         if(this.fireEvent('beforeloadcanvas', this, src) != false){
28079             
28080             this.reset();
28081             
28082             this.imageEl = document.createElement('img');
28083             
28084             var _this = this;
28085             
28086             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
28087             
28088             this.imageEl.src = src;
28089         }
28090     },
28091     
28092     onLoadCanvas : function()
28093     {   
28094         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
28095         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
28096         
28097         this.bodyEl.un('click', this.beforeSelectFile, this);
28098         
28099         this.notifyEl.hide();
28100         this.thumbEl.show();
28101         this.footerEl.show();
28102         
28103         this.baseRotateLevel();
28104         
28105         if(this.isDocument){
28106             this.setThumbBoxSize();
28107         }
28108         
28109         this.setThumbBoxPosition();
28110         
28111         this.baseScaleLevel();
28112         
28113         this.draw();
28114         
28115         this.resize();
28116         
28117         this.canvasLoaded = true;
28118         
28119         if(this.loadMask){
28120             this.maskEl.unmask();
28121         }
28122         
28123     },
28124     
28125     setCanvasPosition : function()
28126     {   
28127         if(!this.canvasEl){
28128             return;
28129         }
28130         
28131         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
28132         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
28133         
28134         this.previewEl.setLeft(pw);
28135         this.previewEl.setTop(ph);
28136         
28137     },
28138     
28139     onMouseDown : function(e)
28140     {   
28141         e.stopEvent();
28142         
28143         this.dragable = true;
28144         this.pinching = false;
28145         
28146         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
28147             this.dragable = false;
28148             return;
28149         }
28150         
28151         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28152         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28153         
28154     },
28155     
28156     onMouseMove : function(e)
28157     {   
28158         e.stopEvent();
28159         
28160         if(!this.canvasLoaded){
28161             return;
28162         }
28163         
28164         if (!this.dragable){
28165             return;
28166         }
28167         
28168         var minX = Math.ceil(this.thumbEl.getLeft(true));
28169         var minY = Math.ceil(this.thumbEl.getTop(true));
28170         
28171         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
28172         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
28173         
28174         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28175         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28176         
28177         x = x - this.mouseX;
28178         y = y - this.mouseY;
28179         
28180         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
28181         var bgY = Math.ceil(y + this.previewEl.getTop(true));
28182         
28183         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
28184         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
28185         
28186         this.previewEl.setLeft(bgX);
28187         this.previewEl.setTop(bgY);
28188         
28189         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28190         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28191     },
28192     
28193     onMouseUp : function(e)
28194     {   
28195         e.stopEvent();
28196         
28197         this.dragable = false;
28198     },
28199     
28200     onMouseWheel : function(e)
28201     {   
28202         e.stopEvent();
28203         
28204         this.startScale = this.scale;
28205         
28206         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
28207         
28208         if(!this.zoomable()){
28209             this.scale = this.startScale;
28210             return;
28211         }
28212         
28213         this.draw();
28214         
28215         return;
28216     },
28217     
28218     zoomable : function()
28219     {
28220         var minScale = this.thumbEl.getWidth() / this.minWidth;
28221         
28222         if(this.minWidth < this.minHeight){
28223             minScale = this.thumbEl.getHeight() / this.minHeight;
28224         }
28225         
28226         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
28227         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
28228         
28229         if(
28230                 this.isDocument &&
28231                 (this.rotate == 0 || this.rotate == 180) && 
28232                 (
28233                     width > this.imageEl.OriginWidth || 
28234                     height > this.imageEl.OriginHeight ||
28235                     (width < this.minWidth && height < this.minHeight)
28236                 )
28237         ){
28238             return false;
28239         }
28240         
28241         if(
28242                 this.isDocument &&
28243                 (this.rotate == 90 || this.rotate == 270) && 
28244                 (
28245                     width > this.imageEl.OriginWidth || 
28246                     height > this.imageEl.OriginHeight ||
28247                     (width < this.minHeight && height < this.minWidth)
28248                 )
28249         ){
28250             return false;
28251         }
28252         
28253         if(
28254                 !this.isDocument &&
28255                 (this.rotate == 0 || this.rotate == 180) && 
28256                 (
28257                     width < this.minWidth || 
28258                     width > this.imageEl.OriginWidth || 
28259                     height < this.minHeight || 
28260                     height > this.imageEl.OriginHeight
28261                 )
28262         ){
28263             return false;
28264         }
28265         
28266         if(
28267                 !this.isDocument &&
28268                 (this.rotate == 90 || this.rotate == 270) && 
28269                 (
28270                     width < this.minHeight || 
28271                     width > this.imageEl.OriginWidth || 
28272                     height < this.minWidth || 
28273                     height > this.imageEl.OriginHeight
28274                 )
28275         ){
28276             return false;
28277         }
28278         
28279         return true;
28280         
28281     },
28282     
28283     onRotateLeft : function(e)
28284     {   
28285         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28286             
28287             var minScale = this.thumbEl.getWidth() / this.minWidth;
28288             
28289             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28290             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28291             
28292             this.startScale = this.scale;
28293             
28294             while (this.getScaleLevel() < minScale){
28295             
28296                 this.scale = this.scale + 1;
28297                 
28298                 if(!this.zoomable()){
28299                     break;
28300                 }
28301                 
28302                 if(
28303                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28304                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28305                 ){
28306                     continue;
28307                 }
28308                 
28309                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28310
28311                 this.draw();
28312                 
28313                 return;
28314             }
28315             
28316             this.scale = this.startScale;
28317             
28318             this.onRotateFail();
28319             
28320             return false;
28321         }
28322         
28323         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28324
28325         if(this.isDocument){
28326             this.setThumbBoxSize();
28327             this.setThumbBoxPosition();
28328             this.setCanvasPosition();
28329         }
28330         
28331         this.draw();
28332         
28333         this.fireEvent('rotate', this, 'left');
28334         
28335     },
28336     
28337     onRotateRight : function(e)
28338     {
28339         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28340             
28341             var minScale = this.thumbEl.getWidth() / this.minWidth;
28342         
28343             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28344             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28345             
28346             this.startScale = this.scale;
28347             
28348             while (this.getScaleLevel() < minScale){
28349             
28350                 this.scale = this.scale + 1;
28351                 
28352                 if(!this.zoomable()){
28353                     break;
28354                 }
28355                 
28356                 if(
28357                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28358                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28359                 ){
28360                     continue;
28361                 }
28362                 
28363                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28364
28365                 this.draw();
28366                 
28367                 return;
28368             }
28369             
28370             this.scale = this.startScale;
28371             
28372             this.onRotateFail();
28373             
28374             return false;
28375         }
28376         
28377         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28378
28379         if(this.isDocument){
28380             this.setThumbBoxSize();
28381             this.setThumbBoxPosition();
28382             this.setCanvasPosition();
28383         }
28384         
28385         this.draw();
28386         
28387         this.fireEvent('rotate', this, 'right');
28388     },
28389     
28390     onRotateFail : function()
28391     {
28392         this.errorEl.show(true);
28393         
28394         var _this = this;
28395         
28396         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28397     },
28398     
28399     draw : function()
28400     {
28401         this.previewEl.dom.innerHTML = '';
28402         
28403         var canvasEl = document.createElement("canvas");
28404         
28405         var contextEl = canvasEl.getContext("2d");
28406         
28407         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28408         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28409         var center = this.imageEl.OriginWidth / 2;
28410         
28411         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28412             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28413             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28414             center = this.imageEl.OriginHeight / 2;
28415         }
28416         
28417         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28418         
28419         contextEl.translate(center, center);
28420         contextEl.rotate(this.rotate * Math.PI / 180);
28421
28422         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28423         
28424         this.canvasEl = document.createElement("canvas");
28425         
28426         this.contextEl = this.canvasEl.getContext("2d");
28427         
28428         switch (this.rotate) {
28429             case 0 :
28430                 
28431                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28432                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28433                 
28434                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28435                 
28436                 break;
28437             case 90 : 
28438                 
28439                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28440                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28441                 
28442                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28443                     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);
28444                     break;
28445                 }
28446                 
28447                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28448                 
28449                 break;
28450             case 180 :
28451                 
28452                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28453                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28454                 
28455                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28456                     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);
28457                     break;
28458                 }
28459                 
28460                 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);
28461                 
28462                 break;
28463             case 270 :
28464                 
28465                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28466                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28467         
28468                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28469                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28470                     break;
28471                 }
28472                 
28473                 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);
28474                 
28475                 break;
28476             default : 
28477                 break;
28478         }
28479         
28480         this.previewEl.appendChild(this.canvasEl);
28481         
28482         this.setCanvasPosition();
28483     },
28484     
28485     crop : function()
28486     {
28487         if(!this.canvasLoaded){
28488             return;
28489         }
28490         
28491         var imageCanvas = document.createElement("canvas");
28492         
28493         var imageContext = imageCanvas.getContext("2d");
28494         
28495         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28496         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28497         
28498         var center = imageCanvas.width / 2;
28499         
28500         imageContext.translate(center, center);
28501         
28502         imageContext.rotate(this.rotate * Math.PI / 180);
28503         
28504         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28505         
28506         var canvas = document.createElement("canvas");
28507         
28508         var context = canvas.getContext("2d");
28509                 
28510         canvas.width = this.minWidth;
28511         canvas.height = this.minHeight;
28512
28513         switch (this.rotate) {
28514             case 0 :
28515                 
28516                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28517                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28518                 
28519                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28520                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28521                 
28522                 var targetWidth = this.minWidth - 2 * x;
28523                 var targetHeight = this.minHeight - 2 * y;
28524                 
28525                 var scale = 1;
28526                 
28527                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28528                     scale = targetWidth / width;
28529                 }
28530                 
28531                 if(x > 0 && y == 0){
28532                     scale = targetHeight / height;
28533                 }
28534                 
28535                 if(x > 0 && y > 0){
28536                     scale = targetWidth / width;
28537                     
28538                     if(width < height){
28539                         scale = targetHeight / height;
28540                     }
28541                 }
28542                 
28543                 context.scale(scale, scale);
28544                 
28545                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28546                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28547
28548                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28549                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28550
28551                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28552                 
28553                 break;
28554             case 90 : 
28555                 
28556                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28557                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28558                 
28559                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28560                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28561                 
28562                 var targetWidth = this.minWidth - 2 * x;
28563                 var targetHeight = this.minHeight - 2 * y;
28564                 
28565                 var scale = 1;
28566                 
28567                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28568                     scale = targetWidth / width;
28569                 }
28570                 
28571                 if(x > 0 && y == 0){
28572                     scale = targetHeight / height;
28573                 }
28574                 
28575                 if(x > 0 && y > 0){
28576                     scale = targetWidth / width;
28577                     
28578                     if(width < height){
28579                         scale = targetHeight / height;
28580                     }
28581                 }
28582                 
28583                 context.scale(scale, scale);
28584                 
28585                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28586                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28587
28588                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28589                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28590                 
28591                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28592                 
28593                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28594                 
28595                 break;
28596             case 180 :
28597                 
28598                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28599                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28600                 
28601                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28602                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28603                 
28604                 var targetWidth = this.minWidth - 2 * x;
28605                 var targetHeight = this.minHeight - 2 * y;
28606                 
28607                 var scale = 1;
28608                 
28609                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28610                     scale = targetWidth / width;
28611                 }
28612                 
28613                 if(x > 0 && y == 0){
28614                     scale = targetHeight / height;
28615                 }
28616                 
28617                 if(x > 0 && y > 0){
28618                     scale = targetWidth / width;
28619                     
28620                     if(width < height){
28621                         scale = targetHeight / height;
28622                     }
28623                 }
28624                 
28625                 context.scale(scale, scale);
28626                 
28627                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28628                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28629
28630                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28631                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28632
28633                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28634                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28635                 
28636                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28637                 
28638                 break;
28639             case 270 :
28640                 
28641                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28642                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28643                 
28644                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28645                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28646                 
28647                 var targetWidth = this.minWidth - 2 * x;
28648                 var targetHeight = this.minHeight - 2 * y;
28649                 
28650                 var scale = 1;
28651                 
28652                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28653                     scale = targetWidth / width;
28654                 }
28655                 
28656                 if(x > 0 && y == 0){
28657                     scale = targetHeight / height;
28658                 }
28659                 
28660                 if(x > 0 && y > 0){
28661                     scale = targetWidth / width;
28662                     
28663                     if(width < height){
28664                         scale = targetHeight / height;
28665                     }
28666                 }
28667                 
28668                 context.scale(scale, scale);
28669                 
28670                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28671                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28672
28673                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28674                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28675                 
28676                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28677                 
28678                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28679                 
28680                 break;
28681             default : 
28682                 break;
28683         }
28684         
28685         this.cropData = canvas.toDataURL(this.cropType);
28686         
28687         if(this.fireEvent('crop', this, this.cropData) !== false){
28688             this.process(this.file, this.cropData);
28689         }
28690         
28691         return;
28692         
28693     },
28694     
28695     setThumbBoxSize : function()
28696     {
28697         var width, height;
28698         
28699         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28700             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28701             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28702             
28703             this.minWidth = width;
28704             this.minHeight = height;
28705             
28706             if(this.rotate == 90 || this.rotate == 270){
28707                 this.minWidth = height;
28708                 this.minHeight = width;
28709             }
28710         }
28711         
28712         height = 300;
28713         width = Math.ceil(this.minWidth * height / this.minHeight);
28714         
28715         if(this.minWidth > this.minHeight){
28716             width = 300;
28717             height = Math.ceil(this.minHeight * width / this.minWidth);
28718         }
28719         
28720         this.thumbEl.setStyle({
28721             width : width + 'px',
28722             height : height + 'px'
28723         });
28724
28725         return;
28726             
28727     },
28728     
28729     setThumbBoxPosition : function()
28730     {
28731         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28732         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28733         
28734         this.thumbEl.setLeft(x);
28735         this.thumbEl.setTop(y);
28736         
28737     },
28738     
28739     baseRotateLevel : function()
28740     {
28741         this.baseRotate = 1;
28742         
28743         if(
28744                 typeof(this.exif) != 'undefined' &&
28745                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28746                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28747         ){
28748             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28749         }
28750         
28751         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28752         
28753     },
28754     
28755     baseScaleLevel : function()
28756     {
28757         var width, height;
28758         
28759         if(this.isDocument){
28760             
28761             if(this.baseRotate == 6 || this.baseRotate == 8){
28762             
28763                 height = this.thumbEl.getHeight();
28764                 this.baseScale = height / this.imageEl.OriginWidth;
28765
28766                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28767                     width = this.thumbEl.getWidth();
28768                     this.baseScale = width / this.imageEl.OriginHeight;
28769                 }
28770
28771                 return;
28772             }
28773
28774             height = this.thumbEl.getHeight();
28775             this.baseScale = height / this.imageEl.OriginHeight;
28776
28777             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28778                 width = this.thumbEl.getWidth();
28779                 this.baseScale = width / this.imageEl.OriginWidth;
28780             }
28781
28782             return;
28783         }
28784         
28785         if(this.baseRotate == 6 || this.baseRotate == 8){
28786             
28787             width = this.thumbEl.getHeight();
28788             this.baseScale = width / this.imageEl.OriginHeight;
28789             
28790             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28791                 height = this.thumbEl.getWidth();
28792                 this.baseScale = height / this.imageEl.OriginHeight;
28793             }
28794             
28795             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28796                 height = this.thumbEl.getWidth();
28797                 this.baseScale = height / this.imageEl.OriginHeight;
28798                 
28799                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28800                     width = this.thumbEl.getHeight();
28801                     this.baseScale = width / this.imageEl.OriginWidth;
28802                 }
28803             }
28804             
28805             return;
28806         }
28807         
28808         width = this.thumbEl.getWidth();
28809         this.baseScale = width / this.imageEl.OriginWidth;
28810         
28811         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28812             height = this.thumbEl.getHeight();
28813             this.baseScale = height / this.imageEl.OriginHeight;
28814         }
28815         
28816         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28817             
28818             height = this.thumbEl.getHeight();
28819             this.baseScale = height / this.imageEl.OriginHeight;
28820             
28821             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28822                 width = this.thumbEl.getWidth();
28823                 this.baseScale = width / this.imageEl.OriginWidth;
28824             }
28825             
28826         }
28827         
28828         return;
28829     },
28830     
28831     getScaleLevel : function()
28832     {
28833         return this.baseScale * Math.pow(1.1, this.scale);
28834     },
28835     
28836     onTouchStart : function(e)
28837     {
28838         if(!this.canvasLoaded){
28839             this.beforeSelectFile(e);
28840             return;
28841         }
28842         
28843         var touches = e.browserEvent.touches;
28844         
28845         if(!touches){
28846             return;
28847         }
28848         
28849         if(touches.length == 1){
28850             this.onMouseDown(e);
28851             return;
28852         }
28853         
28854         if(touches.length != 2){
28855             return;
28856         }
28857         
28858         var coords = [];
28859         
28860         for(var i = 0, finger; finger = touches[i]; i++){
28861             coords.push(finger.pageX, finger.pageY);
28862         }
28863         
28864         var x = Math.pow(coords[0] - coords[2], 2);
28865         var y = Math.pow(coords[1] - coords[3], 2);
28866         
28867         this.startDistance = Math.sqrt(x + y);
28868         
28869         this.startScale = this.scale;
28870         
28871         this.pinching = true;
28872         this.dragable = false;
28873         
28874     },
28875     
28876     onTouchMove : function(e)
28877     {
28878         if(!this.pinching && !this.dragable){
28879             return;
28880         }
28881         
28882         var touches = e.browserEvent.touches;
28883         
28884         if(!touches){
28885             return;
28886         }
28887         
28888         if(this.dragable){
28889             this.onMouseMove(e);
28890             return;
28891         }
28892         
28893         var coords = [];
28894         
28895         for(var i = 0, finger; finger = touches[i]; i++){
28896             coords.push(finger.pageX, finger.pageY);
28897         }
28898         
28899         var x = Math.pow(coords[0] - coords[2], 2);
28900         var y = Math.pow(coords[1] - coords[3], 2);
28901         
28902         this.endDistance = Math.sqrt(x + y);
28903         
28904         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28905         
28906         if(!this.zoomable()){
28907             this.scale = this.startScale;
28908             return;
28909         }
28910         
28911         this.draw();
28912         
28913     },
28914     
28915     onTouchEnd : function(e)
28916     {
28917         this.pinching = false;
28918         this.dragable = false;
28919         
28920     },
28921     
28922     process : function(file, crop)
28923     {
28924         if(this.loadMask){
28925             this.maskEl.mask(this.loadingText);
28926         }
28927         
28928         this.xhr = new XMLHttpRequest();
28929         
28930         file.xhr = this.xhr;
28931
28932         this.xhr.open(this.method, this.url, true);
28933         
28934         var headers = {
28935             "Accept": "application/json",
28936             "Cache-Control": "no-cache",
28937             "X-Requested-With": "XMLHttpRequest"
28938         };
28939         
28940         for (var headerName in headers) {
28941             var headerValue = headers[headerName];
28942             if (headerValue) {
28943                 this.xhr.setRequestHeader(headerName, headerValue);
28944             }
28945         }
28946         
28947         var _this = this;
28948         
28949         this.xhr.onload = function()
28950         {
28951             _this.xhrOnLoad(_this.xhr);
28952         }
28953         
28954         this.xhr.onerror = function()
28955         {
28956             _this.xhrOnError(_this.xhr);
28957         }
28958         
28959         var formData = new FormData();
28960
28961         formData.append('returnHTML', 'NO');
28962         
28963         if(crop){
28964             formData.append('crop', crop);
28965         }
28966         
28967         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28968             formData.append(this.paramName, file, file.name);
28969         }
28970         
28971         if(typeof(file.filename) != 'undefined'){
28972             formData.append('filename', file.filename);
28973         }
28974         
28975         if(typeof(file.mimetype) != 'undefined'){
28976             formData.append('mimetype', file.mimetype);
28977         }
28978         
28979         if(this.fireEvent('arrange', this, formData) != false){
28980             this.xhr.send(formData);
28981         };
28982     },
28983     
28984     xhrOnLoad : function(xhr)
28985     {
28986         if(this.loadMask){
28987             this.maskEl.unmask();
28988         }
28989         
28990         if (xhr.readyState !== 4) {
28991             this.fireEvent('exception', this, xhr);
28992             return;
28993         }
28994
28995         var response = Roo.decode(xhr.responseText);
28996         
28997         if(!response.success){
28998             this.fireEvent('exception', this, xhr);
28999             return;
29000         }
29001         
29002         var response = Roo.decode(xhr.responseText);
29003         
29004         this.fireEvent('upload', this, response);
29005         
29006     },
29007     
29008     xhrOnError : function()
29009     {
29010         if(this.loadMask){
29011             this.maskEl.unmask();
29012         }
29013         
29014         Roo.log('xhr on error');
29015         
29016         var response = Roo.decode(xhr.responseText);
29017           
29018         Roo.log(response);
29019         
29020     },
29021     
29022     prepare : function(file)
29023     {   
29024         if(this.loadMask){
29025             this.maskEl.mask(this.loadingText);
29026         }
29027         
29028         this.file = false;
29029         this.exif = {};
29030         
29031         if(typeof(file) === 'string'){
29032             this.loadCanvas(file);
29033             return;
29034         }
29035         
29036         if(!file || !this.urlAPI){
29037             return;
29038         }
29039         
29040         this.file = file;
29041         this.cropType = file.type;
29042         
29043         var _this = this;
29044         
29045         if(this.fireEvent('prepare', this, this.file) != false){
29046             
29047             var reader = new FileReader();
29048             
29049             reader.onload = function (e) {
29050                 if (e.target.error) {
29051                     Roo.log(e.target.error);
29052                     return;
29053                 }
29054                 
29055                 var buffer = e.target.result,
29056                     dataView = new DataView(buffer),
29057                     offset = 2,
29058                     maxOffset = dataView.byteLength - 4,
29059                     markerBytes,
29060                     markerLength;
29061                 
29062                 if (dataView.getUint16(0) === 0xffd8) {
29063                     while (offset < maxOffset) {
29064                         markerBytes = dataView.getUint16(offset);
29065                         
29066                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
29067                             markerLength = dataView.getUint16(offset + 2) + 2;
29068                             if (offset + markerLength > dataView.byteLength) {
29069                                 Roo.log('Invalid meta data: Invalid segment size.');
29070                                 break;
29071                             }
29072                             
29073                             if(markerBytes == 0xffe1){
29074                                 _this.parseExifData(
29075                                     dataView,
29076                                     offset,
29077                                     markerLength
29078                                 );
29079                             }
29080                             
29081                             offset += markerLength;
29082                             
29083                             continue;
29084                         }
29085                         
29086                         break;
29087                     }
29088                     
29089                 }
29090                 
29091                 var url = _this.urlAPI.createObjectURL(_this.file);
29092                 
29093                 _this.loadCanvas(url);
29094                 
29095                 return;
29096             }
29097             
29098             reader.readAsArrayBuffer(this.file);
29099             
29100         }
29101         
29102     },
29103     
29104     parseExifData : function(dataView, offset, length)
29105     {
29106         var tiffOffset = offset + 10,
29107             littleEndian,
29108             dirOffset;
29109     
29110         if (dataView.getUint32(offset + 4) !== 0x45786966) {
29111             // No Exif data, might be XMP data instead
29112             return;
29113         }
29114         
29115         // Check for the ASCII code for "Exif" (0x45786966):
29116         if (dataView.getUint32(offset + 4) !== 0x45786966) {
29117             // No Exif data, might be XMP data instead
29118             return;
29119         }
29120         if (tiffOffset + 8 > dataView.byteLength) {
29121             Roo.log('Invalid Exif data: Invalid segment size.');
29122             return;
29123         }
29124         // Check for the two null bytes:
29125         if (dataView.getUint16(offset + 8) !== 0x0000) {
29126             Roo.log('Invalid Exif data: Missing byte alignment offset.');
29127             return;
29128         }
29129         // Check the byte alignment:
29130         switch (dataView.getUint16(tiffOffset)) {
29131         case 0x4949:
29132             littleEndian = true;
29133             break;
29134         case 0x4D4D:
29135             littleEndian = false;
29136             break;
29137         default:
29138             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
29139             return;
29140         }
29141         // Check for the TIFF tag marker (0x002A):
29142         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
29143             Roo.log('Invalid Exif data: Missing TIFF marker.');
29144             return;
29145         }
29146         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
29147         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
29148         
29149         this.parseExifTags(
29150             dataView,
29151             tiffOffset,
29152             tiffOffset + dirOffset,
29153             littleEndian
29154         );
29155     },
29156     
29157     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
29158     {
29159         var tagsNumber,
29160             dirEndOffset,
29161             i;
29162         if (dirOffset + 6 > dataView.byteLength) {
29163             Roo.log('Invalid Exif data: Invalid directory offset.');
29164             return;
29165         }
29166         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
29167         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
29168         if (dirEndOffset + 4 > dataView.byteLength) {
29169             Roo.log('Invalid Exif data: Invalid directory size.');
29170             return;
29171         }
29172         for (i = 0; i < tagsNumber; i += 1) {
29173             this.parseExifTag(
29174                 dataView,
29175                 tiffOffset,
29176                 dirOffset + 2 + 12 * i, // tag offset
29177                 littleEndian
29178             );
29179         }
29180         // Return the offset to the next directory:
29181         return dataView.getUint32(dirEndOffset, littleEndian);
29182     },
29183     
29184     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
29185     {
29186         var tag = dataView.getUint16(offset, littleEndian);
29187         
29188         this.exif[tag] = this.getExifValue(
29189             dataView,
29190             tiffOffset,
29191             offset,
29192             dataView.getUint16(offset + 2, littleEndian), // tag type
29193             dataView.getUint32(offset + 4, littleEndian), // tag length
29194             littleEndian
29195         );
29196     },
29197     
29198     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
29199     {
29200         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
29201             tagSize,
29202             dataOffset,
29203             values,
29204             i,
29205             str,
29206             c;
29207     
29208         if (!tagType) {
29209             Roo.log('Invalid Exif data: Invalid tag type.');
29210             return;
29211         }
29212         
29213         tagSize = tagType.size * length;
29214         // Determine if the value is contained in the dataOffset bytes,
29215         // or if the value at the dataOffset is a pointer to the actual data:
29216         dataOffset = tagSize > 4 ?
29217                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
29218         if (dataOffset + tagSize > dataView.byteLength) {
29219             Roo.log('Invalid Exif data: Invalid data offset.');
29220             return;
29221         }
29222         if (length === 1) {
29223             return tagType.getValue(dataView, dataOffset, littleEndian);
29224         }
29225         values = [];
29226         for (i = 0; i < length; i += 1) {
29227             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
29228         }
29229         
29230         if (tagType.ascii) {
29231             str = '';
29232             // Concatenate the chars:
29233             for (i = 0; i < values.length; i += 1) {
29234                 c = values[i];
29235                 // Ignore the terminating NULL byte(s):
29236                 if (c === '\u0000') {
29237                     break;
29238                 }
29239                 str += c;
29240             }
29241             return str;
29242         }
29243         return values;
29244     }
29245     
29246 });
29247
29248 Roo.apply(Roo.bootstrap.UploadCropbox, {
29249     tags : {
29250         'Orientation': 0x0112
29251     },
29252     
29253     Orientation: {
29254             1: 0, //'top-left',
29255 //            2: 'top-right',
29256             3: 180, //'bottom-right',
29257 //            4: 'bottom-left',
29258 //            5: 'left-top',
29259             6: 90, //'right-top',
29260 //            7: 'right-bottom',
29261             8: 270 //'left-bottom'
29262     },
29263     
29264     exifTagTypes : {
29265         // byte, 8-bit unsigned int:
29266         1: {
29267             getValue: function (dataView, dataOffset) {
29268                 return dataView.getUint8(dataOffset);
29269             },
29270             size: 1
29271         },
29272         // ascii, 8-bit byte:
29273         2: {
29274             getValue: function (dataView, dataOffset) {
29275                 return String.fromCharCode(dataView.getUint8(dataOffset));
29276             },
29277             size: 1,
29278             ascii: true
29279         },
29280         // short, 16 bit int:
29281         3: {
29282             getValue: function (dataView, dataOffset, littleEndian) {
29283                 return dataView.getUint16(dataOffset, littleEndian);
29284             },
29285             size: 2
29286         },
29287         // long, 32 bit int:
29288         4: {
29289             getValue: function (dataView, dataOffset, littleEndian) {
29290                 return dataView.getUint32(dataOffset, littleEndian);
29291             },
29292             size: 4
29293         },
29294         // rational = two long values, first is numerator, second is denominator:
29295         5: {
29296             getValue: function (dataView, dataOffset, littleEndian) {
29297                 return dataView.getUint32(dataOffset, littleEndian) /
29298                     dataView.getUint32(dataOffset + 4, littleEndian);
29299             },
29300             size: 8
29301         },
29302         // slong, 32 bit signed int:
29303         9: {
29304             getValue: function (dataView, dataOffset, littleEndian) {
29305                 return dataView.getInt32(dataOffset, littleEndian);
29306             },
29307             size: 4
29308         },
29309         // srational, two slongs, first is numerator, second is denominator:
29310         10: {
29311             getValue: function (dataView, dataOffset, littleEndian) {
29312                 return dataView.getInt32(dataOffset, littleEndian) /
29313                     dataView.getInt32(dataOffset + 4, littleEndian);
29314             },
29315             size: 8
29316         }
29317     },
29318     
29319     footer : {
29320         STANDARD : [
29321             {
29322                 tag : 'div',
29323                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29324                 action : 'rotate-left',
29325                 cn : [
29326                     {
29327                         tag : 'button',
29328                         cls : 'btn btn-default',
29329                         html : '<i class="fa fa-undo"></i>'
29330                     }
29331                 ]
29332             },
29333             {
29334                 tag : 'div',
29335                 cls : 'btn-group roo-upload-cropbox-picture',
29336                 action : 'picture',
29337                 cn : [
29338                     {
29339                         tag : 'button',
29340                         cls : 'btn btn-default',
29341                         html : '<i class="fa fa-picture-o"></i>'
29342                     }
29343                 ]
29344             },
29345             {
29346                 tag : 'div',
29347                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29348                 action : 'rotate-right',
29349                 cn : [
29350                     {
29351                         tag : 'button',
29352                         cls : 'btn btn-default',
29353                         html : '<i class="fa fa-repeat"></i>'
29354                     }
29355                 ]
29356             }
29357         ],
29358         DOCUMENT : [
29359             {
29360                 tag : 'div',
29361                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29362                 action : 'rotate-left',
29363                 cn : [
29364                     {
29365                         tag : 'button',
29366                         cls : 'btn btn-default',
29367                         html : '<i class="fa fa-undo"></i>'
29368                     }
29369                 ]
29370             },
29371             {
29372                 tag : 'div',
29373                 cls : 'btn-group roo-upload-cropbox-download',
29374                 action : 'download',
29375                 cn : [
29376                     {
29377                         tag : 'button',
29378                         cls : 'btn btn-default',
29379                         html : '<i class="fa fa-download"></i>'
29380                     }
29381                 ]
29382             },
29383             {
29384                 tag : 'div',
29385                 cls : 'btn-group roo-upload-cropbox-crop',
29386                 action : 'crop',
29387                 cn : [
29388                     {
29389                         tag : 'button',
29390                         cls : 'btn btn-default',
29391                         html : '<i class="fa fa-crop"></i>'
29392                     }
29393                 ]
29394             },
29395             {
29396                 tag : 'div',
29397                 cls : 'btn-group roo-upload-cropbox-trash',
29398                 action : 'trash',
29399                 cn : [
29400                     {
29401                         tag : 'button',
29402                         cls : 'btn btn-default',
29403                         html : '<i class="fa fa-trash"></i>'
29404                     }
29405                 ]
29406             },
29407             {
29408                 tag : 'div',
29409                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29410                 action : 'rotate-right',
29411                 cn : [
29412                     {
29413                         tag : 'button',
29414                         cls : 'btn btn-default',
29415                         html : '<i class="fa fa-repeat"></i>'
29416                     }
29417                 ]
29418             }
29419         ],
29420         ROTATOR : [
29421             {
29422                 tag : 'div',
29423                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29424                 action : 'rotate-left',
29425                 cn : [
29426                     {
29427                         tag : 'button',
29428                         cls : 'btn btn-default',
29429                         html : '<i class="fa fa-undo"></i>'
29430                     }
29431                 ]
29432             },
29433             {
29434                 tag : 'div',
29435                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29436                 action : 'rotate-right',
29437                 cn : [
29438                     {
29439                         tag : 'button',
29440                         cls : 'btn btn-default',
29441                         html : '<i class="fa fa-repeat"></i>'
29442                     }
29443                 ]
29444             }
29445         ]
29446     }
29447 });
29448
29449 /*
29450 * Licence: LGPL
29451 */
29452
29453 /**
29454  * @class Roo.bootstrap.DocumentManager
29455  * @extends Roo.bootstrap.Component
29456  * Bootstrap DocumentManager class
29457  * @cfg {String} paramName default 'imageUpload'
29458  * @cfg {String} toolTipName default 'filename'
29459  * @cfg {String} method default POST
29460  * @cfg {String} url action url
29461  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29462  * @cfg {Boolean} multiple multiple upload default true
29463  * @cfg {Number} thumbSize default 300
29464  * @cfg {String} fieldLabel
29465  * @cfg {Number} labelWidth default 4
29466  * @cfg {String} labelAlign (left|top) default left
29467  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29468 * @cfg {Number} labellg set the width of label (1-12)
29469  * @cfg {Number} labelmd set the width of label (1-12)
29470  * @cfg {Number} labelsm set the width of label (1-12)
29471  * @cfg {Number} labelxs set the width of label (1-12)
29472  * 
29473  * @constructor
29474  * Create a new DocumentManager
29475  * @param {Object} config The config object
29476  */
29477
29478 Roo.bootstrap.DocumentManager = function(config){
29479     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29480     
29481     this.files = [];
29482     this.delegates = [];
29483     
29484     this.addEvents({
29485         /**
29486          * @event initial
29487          * Fire when initial the DocumentManager
29488          * @param {Roo.bootstrap.DocumentManager} this
29489          */
29490         "initial" : true,
29491         /**
29492          * @event inspect
29493          * inspect selected file
29494          * @param {Roo.bootstrap.DocumentManager} this
29495          * @param {File} file
29496          */
29497         "inspect" : true,
29498         /**
29499          * @event exception
29500          * Fire when xhr load exception
29501          * @param {Roo.bootstrap.DocumentManager} this
29502          * @param {XMLHttpRequest} xhr
29503          */
29504         "exception" : true,
29505         /**
29506          * @event afterupload
29507          * Fire when xhr load exception
29508          * @param {Roo.bootstrap.DocumentManager} this
29509          * @param {XMLHttpRequest} xhr
29510          */
29511         "afterupload" : true,
29512         /**
29513          * @event prepare
29514          * prepare the form data
29515          * @param {Roo.bootstrap.DocumentManager} this
29516          * @param {Object} formData
29517          */
29518         "prepare" : true,
29519         /**
29520          * @event remove
29521          * Fire when remove the file
29522          * @param {Roo.bootstrap.DocumentManager} this
29523          * @param {Object} file
29524          */
29525         "remove" : true,
29526         /**
29527          * @event refresh
29528          * Fire after refresh the file
29529          * @param {Roo.bootstrap.DocumentManager} this
29530          */
29531         "refresh" : true,
29532         /**
29533          * @event click
29534          * Fire after click the image
29535          * @param {Roo.bootstrap.DocumentManager} this
29536          * @param {Object} file
29537          */
29538         "click" : true,
29539         /**
29540          * @event edit
29541          * Fire when upload a image and editable set to true
29542          * @param {Roo.bootstrap.DocumentManager} this
29543          * @param {Object} file
29544          */
29545         "edit" : true,
29546         /**
29547          * @event beforeselectfile
29548          * Fire before select file
29549          * @param {Roo.bootstrap.DocumentManager} this
29550          */
29551         "beforeselectfile" : true,
29552         /**
29553          * @event process
29554          * Fire before process file
29555          * @param {Roo.bootstrap.DocumentManager} this
29556          * @param {Object} file
29557          */
29558         "process" : true,
29559         /**
29560          * @event previewrendered
29561          * Fire when preview rendered
29562          * @param {Roo.bootstrap.DocumentManager} this
29563          * @param {Object} file
29564          */
29565         "previewrendered" : true,
29566         /**
29567          */
29568         "previewResize" : true
29569         
29570     });
29571 };
29572
29573 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29574     
29575     boxes : 0,
29576     inputName : '',
29577     thumbSize : 300,
29578     multiple : true,
29579     files : false,
29580     method : 'POST',
29581     url : '',
29582     paramName : 'imageUpload',
29583     toolTipName : 'filename',
29584     fieldLabel : '',
29585     labelWidth : 4,
29586     labelAlign : 'left',
29587     editable : true,
29588     delegates : false,
29589     xhr : false, 
29590     
29591     labellg : 0,
29592     labelmd : 0,
29593     labelsm : 0,
29594     labelxs : 0,
29595     
29596     getAutoCreate : function()
29597     {   
29598         var managerWidget = {
29599             tag : 'div',
29600             cls : 'roo-document-manager',
29601             cn : [
29602                 {
29603                     tag : 'input',
29604                     cls : 'roo-document-manager-selector',
29605                     type : 'file'
29606                 },
29607                 {
29608                     tag : 'div',
29609                     cls : 'roo-document-manager-uploader',
29610                     cn : [
29611                         {
29612                             tag : 'div',
29613                             cls : 'roo-document-manager-upload-btn',
29614                             html : '<i class="fa fa-plus"></i>'
29615                         }
29616                     ]
29617                     
29618                 }
29619             ]
29620         };
29621         
29622         var content = [
29623             {
29624                 tag : 'div',
29625                 cls : 'column col-md-12',
29626                 cn : managerWidget
29627             }
29628         ];
29629         
29630         if(this.fieldLabel.length){
29631             
29632             content = [
29633                 {
29634                     tag : 'div',
29635                     cls : 'column col-md-12',
29636                     html : this.fieldLabel
29637                 },
29638                 {
29639                     tag : 'div',
29640                     cls : 'column col-md-12',
29641                     cn : managerWidget
29642                 }
29643             ];
29644
29645             if(this.labelAlign == 'left'){
29646                 content = [
29647                     {
29648                         tag : 'div',
29649                         cls : 'column',
29650                         html : this.fieldLabel
29651                     },
29652                     {
29653                         tag : 'div',
29654                         cls : 'column',
29655                         cn : managerWidget
29656                     }
29657                 ];
29658                 
29659                 if(this.labelWidth > 12){
29660                     content[0].style = "width: " + this.labelWidth + 'px';
29661                 }
29662
29663                 if(this.labelWidth < 13 && this.labelmd == 0){
29664                     this.labelmd = this.labelWidth;
29665                 }
29666
29667                 if(this.labellg > 0){
29668                     content[0].cls += ' col-lg-' + this.labellg;
29669                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29670                 }
29671
29672                 if(this.labelmd > 0){
29673                     content[0].cls += ' col-md-' + this.labelmd;
29674                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29675                 }
29676
29677                 if(this.labelsm > 0){
29678                     content[0].cls += ' col-sm-' + this.labelsm;
29679                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29680                 }
29681
29682                 if(this.labelxs > 0){
29683                     content[0].cls += ' col-xs-' + this.labelxs;
29684                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29685                 }
29686                 
29687             }
29688         }
29689         
29690         var cfg = {
29691             tag : 'div',
29692             cls : 'row clearfix',
29693             cn : content
29694         };
29695         
29696         return cfg;
29697         
29698     },
29699     
29700     initEvents : function()
29701     {
29702         this.managerEl = this.el.select('.roo-document-manager', true).first();
29703         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29704         
29705         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29706         this.selectorEl.hide();
29707         
29708         if(this.multiple){
29709             this.selectorEl.attr('multiple', 'multiple');
29710         }
29711         
29712         this.selectorEl.on('change', this.onFileSelected, this);
29713         
29714         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29715         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29716         
29717         this.uploader.on('click', this.onUploaderClick, this);
29718         
29719         this.renderProgressDialog();
29720         
29721         var _this = this;
29722         
29723         window.addEventListener("resize", function() { _this.refresh(); } );
29724         
29725         this.fireEvent('initial', this);
29726     },
29727     
29728     renderProgressDialog : function()
29729     {
29730         var _this = this;
29731         
29732         this.progressDialog = new Roo.bootstrap.Modal({
29733             cls : 'roo-document-manager-progress-dialog',
29734             allow_close : false,
29735             animate : false,
29736             title : '',
29737             buttons : [
29738                 {
29739                     name  :'cancel',
29740                     weight : 'danger',
29741                     html : 'Cancel'
29742                 }
29743             ], 
29744             listeners : { 
29745                 btnclick : function() {
29746                     _this.uploadCancel();
29747                     this.hide();
29748                 }
29749             }
29750         });
29751          
29752         this.progressDialog.render(Roo.get(document.body));
29753          
29754         this.progress = new Roo.bootstrap.Progress({
29755             cls : 'roo-document-manager-progress',
29756             active : true,
29757             striped : true
29758         });
29759         
29760         this.progress.render(this.progressDialog.getChildContainer());
29761         
29762         this.progressBar = new Roo.bootstrap.ProgressBar({
29763             cls : 'roo-document-manager-progress-bar',
29764             aria_valuenow : 0,
29765             aria_valuemin : 0,
29766             aria_valuemax : 12,
29767             panel : 'success'
29768         });
29769         
29770         this.progressBar.render(this.progress.getChildContainer());
29771     },
29772     
29773     onUploaderClick : function(e)
29774     {
29775         e.preventDefault();
29776      
29777         if(this.fireEvent('beforeselectfile', this) != false){
29778             this.selectorEl.dom.click();
29779         }
29780         
29781     },
29782     
29783     onFileSelected : function(e)
29784     {
29785         e.preventDefault();
29786         
29787         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29788             return;
29789         }
29790         
29791         Roo.each(this.selectorEl.dom.files, function(file){
29792             if(this.fireEvent('inspect', this, file) != false){
29793                 this.files.push(file);
29794             }
29795         }, this);
29796         
29797         this.queue();
29798         
29799     },
29800     
29801     queue : function()
29802     {
29803         this.selectorEl.dom.value = '';
29804         
29805         if(!this.files || !this.files.length){
29806             return;
29807         }
29808         
29809         if(this.boxes > 0 && this.files.length > this.boxes){
29810             this.files = this.files.slice(0, this.boxes);
29811         }
29812         
29813         this.uploader.show();
29814         
29815         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29816             this.uploader.hide();
29817         }
29818         
29819         var _this = this;
29820         
29821         var files = [];
29822         
29823         var docs = [];
29824         
29825         Roo.each(this.files, function(file){
29826             
29827             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29828                 var f = this.renderPreview(file);
29829                 files.push(f);
29830                 return;
29831             }
29832             
29833             if(file.type.indexOf('image') != -1){
29834                 this.delegates.push(
29835                     (function(){
29836                         _this.process(file);
29837                     }).createDelegate(this)
29838                 );
29839         
29840                 return;
29841             }
29842             
29843             docs.push(
29844                 (function(){
29845                     _this.process(file);
29846                 }).createDelegate(this)
29847             );
29848             
29849         }, this);
29850         
29851         this.files = files;
29852         
29853         this.delegates = this.delegates.concat(docs);
29854         
29855         if(!this.delegates.length){
29856             this.refresh();
29857             return;
29858         }
29859         
29860         this.progressBar.aria_valuemax = this.delegates.length;
29861         
29862         this.arrange();
29863         
29864         return;
29865     },
29866     
29867     arrange : function()
29868     {
29869         if(!this.delegates.length){
29870             this.progressDialog.hide();
29871             this.refresh();
29872             return;
29873         }
29874         
29875         var delegate = this.delegates.shift();
29876         
29877         this.progressDialog.show();
29878         
29879         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29880         
29881         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29882         
29883         delegate();
29884     },
29885     
29886     refresh : function()
29887     {
29888         this.uploader.show();
29889         
29890         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29891             this.uploader.hide();
29892         }
29893         
29894         Roo.isTouch ? this.closable(false) : this.closable(true);
29895         
29896         this.fireEvent('refresh', this);
29897     },
29898     
29899     onRemove : function(e, el, o)
29900     {
29901         e.preventDefault();
29902         
29903         this.fireEvent('remove', this, o);
29904         
29905     },
29906     
29907     remove : function(o)
29908     {
29909         var files = [];
29910         
29911         Roo.each(this.files, function(file){
29912             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29913                 files.push(file);
29914                 return;
29915             }
29916
29917             o.target.remove();
29918
29919         }, this);
29920         
29921         this.files = files;
29922         
29923         this.refresh();
29924     },
29925     
29926     clear : function()
29927     {
29928         Roo.each(this.files, function(file){
29929             if(!file.target){
29930                 return;
29931             }
29932             
29933             file.target.remove();
29934
29935         }, this);
29936         
29937         this.files = [];
29938         
29939         this.refresh();
29940     },
29941     
29942     onClick : function(e, el, o)
29943     {
29944         e.preventDefault();
29945         
29946         this.fireEvent('click', this, o);
29947         
29948     },
29949     
29950     closable : function(closable)
29951     {
29952         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29953             
29954             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29955             
29956             if(closable){
29957                 el.show();
29958                 return;
29959             }
29960             
29961             el.hide();
29962             
29963         }, this);
29964     },
29965     
29966     xhrOnLoad : function(xhr)
29967     {
29968         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29969             el.remove();
29970         }, this);
29971         
29972         if (xhr.readyState !== 4) {
29973             this.arrange();
29974             this.fireEvent('exception', this, xhr);
29975             return;
29976         }
29977
29978         var response = Roo.decode(xhr.responseText);
29979         
29980         if(!response.success){
29981             this.arrange();
29982             this.fireEvent('exception', this, xhr);
29983             return;
29984         }
29985         
29986         var file = this.renderPreview(response.data);
29987         
29988         this.files.push(file);
29989         
29990         this.arrange();
29991         
29992         this.fireEvent('afterupload', this, xhr);
29993         
29994     },
29995     
29996     xhrOnError : function(xhr)
29997     {
29998         Roo.log('xhr on error');
29999         
30000         var response = Roo.decode(xhr.responseText);
30001           
30002         Roo.log(response);
30003         
30004         this.arrange();
30005     },
30006     
30007     process : function(file)
30008     {
30009         if(this.fireEvent('process', this, file) !== false){
30010             if(this.editable && file.type.indexOf('image') != -1){
30011                 this.fireEvent('edit', this, file);
30012                 return;
30013             }
30014
30015             this.uploadStart(file, false);
30016
30017             return;
30018         }
30019         
30020     },
30021     
30022     uploadStart : function(file, crop)
30023     {
30024         this.xhr = new XMLHttpRequest();
30025         
30026         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
30027             this.arrange();
30028             return;
30029         }
30030         
30031         file.xhr = this.xhr;
30032             
30033         this.managerEl.createChild({
30034             tag : 'div',
30035             cls : 'roo-document-manager-loading',
30036             cn : [
30037                 {
30038                     tag : 'div',
30039                     tooltip : file.name,
30040                     cls : 'roo-document-manager-thumb',
30041                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30042                 }
30043             ]
30044
30045         });
30046
30047         this.xhr.open(this.method, this.url, true);
30048         
30049         var headers = {
30050             "Accept": "application/json",
30051             "Cache-Control": "no-cache",
30052             "X-Requested-With": "XMLHttpRequest"
30053         };
30054         
30055         for (var headerName in headers) {
30056             var headerValue = headers[headerName];
30057             if (headerValue) {
30058                 this.xhr.setRequestHeader(headerName, headerValue);
30059             }
30060         }
30061         
30062         var _this = this;
30063         
30064         this.xhr.onload = function()
30065         {
30066             _this.xhrOnLoad(_this.xhr);
30067         }
30068         
30069         this.xhr.onerror = function()
30070         {
30071             _this.xhrOnError(_this.xhr);
30072         }
30073         
30074         var formData = new FormData();
30075
30076         formData.append('returnHTML', 'NO');
30077         
30078         if(crop){
30079             formData.append('crop', crop);
30080         }
30081         
30082         formData.append(this.paramName, file, file.name);
30083         
30084         var options = {
30085             file : file, 
30086             manually : false
30087         };
30088         
30089         if(this.fireEvent('prepare', this, formData, options) != false){
30090             
30091             if(options.manually){
30092                 return;
30093             }
30094             
30095             this.xhr.send(formData);
30096             return;
30097         };
30098         
30099         this.uploadCancel();
30100     },
30101     
30102     uploadCancel : function()
30103     {
30104         if (this.xhr) {
30105             this.xhr.abort();
30106         }
30107         
30108         this.delegates = [];
30109         
30110         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30111             el.remove();
30112         }, this);
30113         
30114         this.arrange();
30115     },
30116     
30117     renderPreview : function(file)
30118     {
30119         if(typeof(file.target) != 'undefined' && file.target){
30120             return file;
30121         }
30122         
30123         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
30124         
30125         var previewEl = this.managerEl.createChild({
30126             tag : 'div',
30127             cls : 'roo-document-manager-preview',
30128             cn : [
30129                 {
30130                     tag : 'div',
30131                     tooltip : file[this.toolTipName],
30132                     cls : 'roo-document-manager-thumb',
30133                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
30134                 },
30135                 {
30136                     tag : 'button',
30137                     cls : 'close',
30138                     html : '<i class="fa fa-times-circle"></i>'
30139                 }
30140             ]
30141         });
30142
30143         var close = previewEl.select('button.close', true).first();
30144
30145         close.on('click', this.onRemove, this, file);
30146
30147         file.target = previewEl;
30148
30149         var image = previewEl.select('img', true).first();
30150         
30151         var _this = this;
30152         
30153         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
30154         
30155         image.on('click', this.onClick, this, file);
30156         
30157         this.fireEvent('previewrendered', this, file);
30158         
30159         return file;
30160         
30161     },
30162     
30163     onPreviewLoad : function(file, image)
30164     {
30165         if(typeof(file.target) == 'undefined' || !file.target){
30166             return;
30167         }
30168         
30169         var width = image.dom.naturalWidth || image.dom.width;
30170         var height = image.dom.naturalHeight || image.dom.height;
30171         
30172         if(!this.previewResize) {
30173             return;
30174         }
30175         
30176         if(width > height){
30177             file.target.addClass('wide');
30178             return;
30179         }
30180         
30181         file.target.addClass('tall');
30182         return;
30183         
30184     },
30185     
30186     uploadFromSource : function(file, crop)
30187     {
30188         this.xhr = new XMLHttpRequest();
30189         
30190         this.managerEl.createChild({
30191             tag : 'div',
30192             cls : 'roo-document-manager-loading',
30193             cn : [
30194                 {
30195                     tag : 'div',
30196                     tooltip : file.name,
30197                     cls : 'roo-document-manager-thumb',
30198                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30199                 }
30200             ]
30201
30202         });
30203
30204         this.xhr.open(this.method, this.url, true);
30205         
30206         var headers = {
30207             "Accept": "application/json",
30208             "Cache-Control": "no-cache",
30209             "X-Requested-With": "XMLHttpRequest"
30210         };
30211         
30212         for (var headerName in headers) {
30213             var headerValue = headers[headerName];
30214             if (headerValue) {
30215                 this.xhr.setRequestHeader(headerName, headerValue);
30216             }
30217         }
30218         
30219         var _this = this;
30220         
30221         this.xhr.onload = function()
30222         {
30223             _this.xhrOnLoad(_this.xhr);
30224         }
30225         
30226         this.xhr.onerror = function()
30227         {
30228             _this.xhrOnError(_this.xhr);
30229         }
30230         
30231         var formData = new FormData();
30232
30233         formData.append('returnHTML', 'NO');
30234         
30235         formData.append('crop', crop);
30236         
30237         if(typeof(file.filename) != 'undefined'){
30238             formData.append('filename', file.filename);
30239         }
30240         
30241         if(typeof(file.mimetype) != 'undefined'){
30242             formData.append('mimetype', file.mimetype);
30243         }
30244         
30245         Roo.log(formData);
30246         
30247         if(this.fireEvent('prepare', this, formData) != false){
30248             this.xhr.send(formData);
30249         };
30250     }
30251 });
30252
30253 /*
30254 * Licence: LGPL
30255 */
30256
30257 /**
30258  * @class Roo.bootstrap.DocumentViewer
30259  * @extends Roo.bootstrap.Component
30260  * Bootstrap DocumentViewer class
30261  * @cfg {Boolean} showDownload (true|false) show download button (default true)
30262  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
30263  * 
30264  * @constructor
30265  * Create a new DocumentViewer
30266  * @param {Object} config The config object
30267  */
30268
30269 Roo.bootstrap.DocumentViewer = function(config){
30270     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
30271     
30272     this.addEvents({
30273         /**
30274          * @event initial
30275          * Fire after initEvent
30276          * @param {Roo.bootstrap.DocumentViewer} this
30277          */
30278         "initial" : true,
30279         /**
30280          * @event click
30281          * Fire after click
30282          * @param {Roo.bootstrap.DocumentViewer} this
30283          */
30284         "click" : true,
30285         /**
30286          * @event download
30287          * Fire after download button
30288          * @param {Roo.bootstrap.DocumentViewer} this
30289          */
30290         "download" : true,
30291         /**
30292          * @event trash
30293          * Fire after trash button
30294          * @param {Roo.bootstrap.DocumentViewer} this
30295          */
30296         "trash" : true
30297         
30298     });
30299 };
30300
30301 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
30302     
30303     showDownload : true,
30304     
30305     showTrash : true,
30306     
30307     getAutoCreate : function()
30308     {
30309         var cfg = {
30310             tag : 'div',
30311             cls : 'roo-document-viewer',
30312             cn : [
30313                 {
30314                     tag : 'div',
30315                     cls : 'roo-document-viewer-body',
30316                     cn : [
30317                         {
30318                             tag : 'div',
30319                             cls : 'roo-document-viewer-thumb',
30320                             cn : [
30321                                 {
30322                                     tag : 'img',
30323                                     cls : 'roo-document-viewer-image'
30324                                 }
30325                             ]
30326                         }
30327                     ]
30328                 },
30329                 {
30330                     tag : 'div',
30331                     cls : 'roo-document-viewer-footer',
30332                     cn : {
30333                         tag : 'div',
30334                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
30335                         cn : [
30336                             {
30337                                 tag : 'div',
30338                                 cls : 'btn-group roo-document-viewer-download',
30339                                 cn : [
30340                                     {
30341                                         tag : 'button',
30342                                         cls : 'btn btn-default',
30343                                         html : '<i class="fa fa-download"></i>'
30344                                     }
30345                                 ]
30346                             },
30347                             {
30348                                 tag : 'div',
30349                                 cls : 'btn-group roo-document-viewer-trash',
30350                                 cn : [
30351                                     {
30352                                         tag : 'button',
30353                                         cls : 'btn btn-default',
30354                                         html : '<i class="fa fa-trash"></i>'
30355                                     }
30356                                 ]
30357                             }
30358                         ]
30359                     }
30360                 }
30361             ]
30362         };
30363         
30364         return cfg;
30365     },
30366     
30367     initEvents : function()
30368     {
30369         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30370         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30371         
30372         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30373         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30374         
30375         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30376         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30377         
30378         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30379         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30380         
30381         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30382         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30383         
30384         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30385         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30386         
30387         this.bodyEl.on('click', this.onClick, this);
30388         this.downloadBtn.on('click', this.onDownload, this);
30389         this.trashBtn.on('click', this.onTrash, this);
30390         
30391         this.downloadBtn.hide();
30392         this.trashBtn.hide();
30393         
30394         if(this.showDownload){
30395             this.downloadBtn.show();
30396         }
30397         
30398         if(this.showTrash){
30399             this.trashBtn.show();
30400         }
30401         
30402         if(!this.showDownload && !this.showTrash) {
30403             this.footerEl.hide();
30404         }
30405         
30406     },
30407     
30408     initial : function()
30409     {
30410         this.fireEvent('initial', this);
30411         
30412     },
30413     
30414     onClick : function(e)
30415     {
30416         e.preventDefault();
30417         
30418         this.fireEvent('click', this);
30419     },
30420     
30421     onDownload : function(e)
30422     {
30423         e.preventDefault();
30424         
30425         this.fireEvent('download', this);
30426     },
30427     
30428     onTrash : function(e)
30429     {
30430         e.preventDefault();
30431         
30432         this.fireEvent('trash', this);
30433     }
30434     
30435 });
30436 /*
30437  * - LGPL
30438  *
30439  * nav progress bar
30440  * 
30441  */
30442
30443 /**
30444  * @class Roo.bootstrap.NavProgressBar
30445  * @extends Roo.bootstrap.Component
30446  * Bootstrap NavProgressBar class
30447  * 
30448  * @constructor
30449  * Create a new nav progress bar
30450  * @param {Object} config The config object
30451  */
30452
30453 Roo.bootstrap.NavProgressBar = function(config){
30454     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30455
30456     this.bullets = this.bullets || [];
30457    
30458 //    Roo.bootstrap.NavProgressBar.register(this);
30459      this.addEvents({
30460         /**
30461              * @event changed
30462              * Fires when the active item changes
30463              * @param {Roo.bootstrap.NavProgressBar} this
30464              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30465              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
30466          */
30467         'changed': true
30468      });
30469     
30470 };
30471
30472 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
30473     
30474     bullets : [],
30475     barItems : [],
30476     
30477     getAutoCreate : function()
30478     {
30479         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30480         
30481         cfg = {
30482             tag : 'div',
30483             cls : 'roo-navigation-bar-group',
30484             cn : [
30485                 {
30486                     tag : 'div',
30487                     cls : 'roo-navigation-top-bar'
30488                 },
30489                 {
30490                     tag : 'div',
30491                     cls : 'roo-navigation-bullets-bar',
30492                     cn : [
30493                         {
30494                             tag : 'ul',
30495                             cls : 'roo-navigation-bar'
30496                         }
30497                     ]
30498                 },
30499                 
30500                 {
30501                     tag : 'div',
30502                     cls : 'roo-navigation-bottom-bar'
30503                 }
30504             ]
30505             
30506         };
30507         
30508         return cfg;
30509         
30510     },
30511     
30512     initEvents: function() 
30513     {
30514         
30515     },
30516     
30517     onRender : function(ct, position) 
30518     {
30519         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30520         
30521         if(this.bullets.length){
30522             Roo.each(this.bullets, function(b){
30523                this.addItem(b);
30524             }, this);
30525         }
30526         
30527         this.format();
30528         
30529     },
30530     
30531     addItem : function(cfg)
30532     {
30533         var item = new Roo.bootstrap.NavProgressItem(cfg);
30534         
30535         item.parentId = this.id;
30536         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30537         
30538         if(cfg.html){
30539             var top = new Roo.bootstrap.Element({
30540                 tag : 'div',
30541                 cls : 'roo-navigation-bar-text'
30542             });
30543             
30544             var bottom = new Roo.bootstrap.Element({
30545                 tag : 'div',
30546                 cls : 'roo-navigation-bar-text'
30547             });
30548             
30549             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30550             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30551             
30552             var topText = new Roo.bootstrap.Element({
30553                 tag : 'span',
30554                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30555             });
30556             
30557             var bottomText = new Roo.bootstrap.Element({
30558                 tag : 'span',
30559                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30560             });
30561             
30562             topText.onRender(top.el, null);
30563             bottomText.onRender(bottom.el, null);
30564             
30565             item.topEl = top;
30566             item.bottomEl = bottom;
30567         }
30568         
30569         this.barItems.push(item);
30570         
30571         return item;
30572     },
30573     
30574     getActive : function()
30575     {
30576         var active = false;
30577         
30578         Roo.each(this.barItems, function(v){
30579             
30580             if (!v.isActive()) {
30581                 return;
30582             }
30583             
30584             active = v;
30585             return false;
30586             
30587         });
30588         
30589         return active;
30590     },
30591     
30592     setActiveItem : function(item)
30593     {
30594         var prev = false;
30595         
30596         Roo.each(this.barItems, function(v){
30597             if (v.rid == item.rid) {
30598                 return ;
30599             }
30600             
30601             if (v.isActive()) {
30602                 v.setActive(false);
30603                 prev = v;
30604             }
30605         });
30606
30607         item.setActive(true);
30608         
30609         this.fireEvent('changed', this, item, prev);
30610     },
30611     
30612     getBarItem: function(rid)
30613     {
30614         var ret = false;
30615         
30616         Roo.each(this.barItems, function(e) {
30617             if (e.rid != rid) {
30618                 return;
30619             }
30620             
30621             ret =  e;
30622             return false;
30623         });
30624         
30625         return ret;
30626     },
30627     
30628     indexOfItem : function(item)
30629     {
30630         var index = false;
30631         
30632         Roo.each(this.barItems, function(v, i){
30633             
30634             if (v.rid != item.rid) {
30635                 return;
30636             }
30637             
30638             index = i;
30639             return false
30640         });
30641         
30642         return index;
30643     },
30644     
30645     setActiveNext : function()
30646     {
30647         var i = this.indexOfItem(this.getActive());
30648         
30649         if (i > this.barItems.length) {
30650             return;
30651         }
30652         
30653         this.setActiveItem(this.barItems[i+1]);
30654     },
30655     
30656     setActivePrev : function()
30657     {
30658         var i = this.indexOfItem(this.getActive());
30659         
30660         if (i  < 1) {
30661             return;
30662         }
30663         
30664         this.setActiveItem(this.barItems[i-1]);
30665     },
30666     
30667     format : function()
30668     {
30669         if(!this.barItems.length){
30670             return;
30671         }
30672      
30673         var width = 100 / this.barItems.length;
30674         
30675         Roo.each(this.barItems, function(i){
30676             i.el.setStyle('width', width + '%');
30677             i.topEl.el.setStyle('width', width + '%');
30678             i.bottomEl.el.setStyle('width', width + '%');
30679         }, this);
30680         
30681     }
30682     
30683 });
30684 /*
30685  * - LGPL
30686  *
30687  * Nav Progress Item
30688  * 
30689  */
30690
30691 /**
30692  * @class Roo.bootstrap.NavProgressItem
30693  * @extends Roo.bootstrap.Component
30694  * Bootstrap NavProgressItem class
30695  * @cfg {String} rid the reference id
30696  * @cfg {Boolean} active (true|false) Is item active default false
30697  * @cfg {Boolean} disabled (true|false) Is item active default false
30698  * @cfg {String} html
30699  * @cfg {String} position (top|bottom) text position default bottom
30700  * @cfg {String} icon show icon instead of number
30701  * 
30702  * @constructor
30703  * Create a new NavProgressItem
30704  * @param {Object} config The config object
30705  */
30706 Roo.bootstrap.NavProgressItem = function(config){
30707     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30708     this.addEvents({
30709         // raw events
30710         /**
30711          * @event click
30712          * The raw click event for the entire grid.
30713          * @param {Roo.bootstrap.NavProgressItem} this
30714          * @param {Roo.EventObject} e
30715          */
30716         "click" : true
30717     });
30718    
30719 };
30720
30721 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30722     
30723     rid : '',
30724     active : false,
30725     disabled : false,
30726     html : '',
30727     position : 'bottom',
30728     icon : false,
30729     
30730     getAutoCreate : function()
30731     {
30732         var iconCls = 'roo-navigation-bar-item-icon';
30733         
30734         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30735         
30736         var cfg = {
30737             tag: 'li',
30738             cls: 'roo-navigation-bar-item',
30739             cn : [
30740                 {
30741                     tag : 'i',
30742                     cls : iconCls
30743                 }
30744             ]
30745         };
30746         
30747         if(this.active){
30748             cfg.cls += ' active';
30749         }
30750         if(this.disabled){
30751             cfg.cls += ' disabled';
30752         }
30753         
30754         return cfg;
30755     },
30756     
30757     disable : function()
30758     {
30759         this.setDisabled(true);
30760     },
30761     
30762     enable : function()
30763     {
30764         this.setDisabled(false);
30765     },
30766     
30767     initEvents: function() 
30768     {
30769         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30770         
30771         this.iconEl.on('click', this.onClick, this);
30772     },
30773     
30774     onClick : function(e)
30775     {
30776         e.preventDefault();
30777         
30778         if(this.disabled){
30779             return;
30780         }
30781         
30782         if(this.fireEvent('click', this, e) === false){
30783             return;
30784         };
30785         
30786         this.parent().setActiveItem(this);
30787     },
30788     
30789     isActive: function () 
30790     {
30791         return this.active;
30792     },
30793     
30794     setActive : function(state)
30795     {
30796         if(this.active == state){
30797             return;
30798         }
30799         
30800         this.active = state;
30801         
30802         if (state) {
30803             this.el.addClass('active');
30804             return;
30805         }
30806         
30807         this.el.removeClass('active');
30808         
30809         return;
30810     },
30811     
30812     setDisabled : function(state)
30813     {
30814         if(this.disabled == state){
30815             return;
30816         }
30817         
30818         this.disabled = state;
30819         
30820         if (state) {
30821             this.el.addClass('disabled');
30822             return;
30823         }
30824         
30825         this.el.removeClass('disabled');
30826     },
30827     
30828     tooltipEl : function()
30829     {
30830         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30831     }
30832 });
30833  
30834
30835  /*
30836  * - LGPL
30837  *
30838  * FieldLabel
30839  * 
30840  */
30841
30842 /**
30843  * @class Roo.bootstrap.FieldLabel
30844  * @extends Roo.bootstrap.Component
30845  * Bootstrap FieldLabel class
30846  * @cfg {String} html contents of the element
30847  * @cfg {String} tag tag of the element default label
30848  * @cfg {String} cls class of the element
30849  * @cfg {String} target label target 
30850  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30851  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30852  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30853  * @cfg {String} iconTooltip default "This field is required"
30854  * @cfg {String} indicatorpos (left|right) default left
30855  * 
30856  * @constructor
30857  * Create a new FieldLabel
30858  * @param {Object} config The config object
30859  */
30860
30861 Roo.bootstrap.FieldLabel = function(config){
30862     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30863     
30864     this.addEvents({
30865             /**
30866              * @event invalid
30867              * Fires after the field has been marked as invalid.
30868              * @param {Roo.form.FieldLabel} this
30869              * @param {String} msg The validation message
30870              */
30871             invalid : true,
30872             /**
30873              * @event valid
30874              * Fires after the field has been validated with no errors.
30875              * @param {Roo.form.FieldLabel} this
30876              */
30877             valid : true
30878         });
30879 };
30880
30881 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30882     
30883     tag: 'label',
30884     cls: '',
30885     html: '',
30886     target: '',
30887     allowBlank : true,
30888     invalidClass : 'has-warning',
30889     validClass : 'has-success',
30890     iconTooltip : 'This field is required',
30891     indicatorpos : 'left',
30892     
30893     getAutoCreate : function(){
30894         
30895         var cls = "";
30896         if (!this.allowBlank) {
30897             cls  = "visible";
30898         }
30899         
30900         var cfg = {
30901             tag : this.tag,
30902             cls : 'roo-bootstrap-field-label ' + this.cls,
30903             for : this.target,
30904             cn : [
30905                 {
30906                     tag : 'i',
30907                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30908                     tooltip : this.iconTooltip
30909                 },
30910                 {
30911                     tag : 'span',
30912                     html : this.html
30913                 }
30914             ] 
30915         };
30916         
30917         if(this.indicatorpos == 'right'){
30918             var cfg = {
30919                 tag : this.tag,
30920                 cls : 'roo-bootstrap-field-label ' + this.cls,
30921                 for : this.target,
30922                 cn : [
30923                     {
30924                         tag : 'span',
30925                         html : this.html
30926                     },
30927                     {
30928                         tag : 'i',
30929                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30930                         tooltip : this.iconTooltip
30931                     }
30932                 ] 
30933             };
30934         }
30935         
30936         return cfg;
30937     },
30938     
30939     initEvents: function() 
30940     {
30941         Roo.bootstrap.Element.superclass.initEvents.call(this);
30942         
30943         this.indicator = this.indicatorEl();
30944         
30945         if(this.indicator){
30946             this.indicator.removeClass('visible');
30947             this.indicator.addClass('invisible');
30948         }
30949         
30950         Roo.bootstrap.FieldLabel.register(this);
30951     },
30952     
30953     indicatorEl : function()
30954     {
30955         var indicator = this.el.select('i.roo-required-indicator',true).first();
30956         
30957         if(!indicator){
30958             return false;
30959         }
30960         
30961         return indicator;
30962         
30963     },
30964     
30965     /**
30966      * Mark this field as valid
30967      */
30968     markValid : function()
30969     {
30970         if(this.indicator){
30971             this.indicator.removeClass('visible');
30972             this.indicator.addClass('invisible');
30973         }
30974         if (Roo.bootstrap.version == 3) {
30975             this.el.removeClass(this.invalidClass);
30976             this.el.addClass(this.validClass);
30977         } else {
30978             this.el.removeClass('is-invalid');
30979             this.el.addClass('is-valid');
30980         }
30981         
30982         
30983         this.fireEvent('valid', this);
30984     },
30985     
30986     /**
30987      * Mark this field as invalid
30988      * @param {String} msg The validation message
30989      */
30990     markInvalid : function(msg)
30991     {
30992         if(this.indicator){
30993             this.indicator.removeClass('invisible');
30994             this.indicator.addClass('visible');
30995         }
30996           if (Roo.bootstrap.version == 3) {
30997             this.el.removeClass(this.validClass);
30998             this.el.addClass(this.invalidClass);
30999         } else {
31000             this.el.removeClass('is-valid');
31001             this.el.addClass('is-invalid');
31002         }
31003         
31004         
31005         this.fireEvent('invalid', this, msg);
31006     }
31007     
31008    
31009 });
31010
31011 Roo.apply(Roo.bootstrap.FieldLabel, {
31012     
31013     groups: {},
31014     
31015      /**
31016     * register a FieldLabel Group
31017     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
31018     */
31019     register : function(label)
31020     {
31021         if(this.groups.hasOwnProperty(label.target)){
31022             return;
31023         }
31024      
31025         this.groups[label.target] = label;
31026         
31027     },
31028     /**
31029     * fetch a FieldLabel Group based on the target
31030     * @param {string} target
31031     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
31032     */
31033     get: function(target) {
31034         if (typeof(this.groups[target]) == 'undefined') {
31035             return false;
31036         }
31037         
31038         return this.groups[target] ;
31039     }
31040 });
31041
31042  
31043
31044  /*
31045  * - LGPL
31046  *
31047  * page DateSplitField.
31048  * 
31049  */
31050
31051
31052 /**
31053  * @class Roo.bootstrap.DateSplitField
31054  * @extends Roo.bootstrap.Component
31055  * Bootstrap DateSplitField class
31056  * @cfg {string} fieldLabel - the label associated
31057  * @cfg {Number} labelWidth set the width of label (0-12)
31058  * @cfg {String} labelAlign (top|left)
31059  * @cfg {Boolean} dayAllowBlank (true|false) default false
31060  * @cfg {Boolean} monthAllowBlank (true|false) default false
31061  * @cfg {Boolean} yearAllowBlank (true|false) default false
31062  * @cfg {string} dayPlaceholder 
31063  * @cfg {string} monthPlaceholder
31064  * @cfg {string} yearPlaceholder
31065  * @cfg {string} dayFormat default 'd'
31066  * @cfg {string} monthFormat default 'm'
31067  * @cfg {string} yearFormat default 'Y'
31068  * @cfg {Number} labellg set the width of label (1-12)
31069  * @cfg {Number} labelmd set the width of label (1-12)
31070  * @cfg {Number} labelsm set the width of label (1-12)
31071  * @cfg {Number} labelxs set the width of label (1-12)
31072
31073  *     
31074  * @constructor
31075  * Create a new DateSplitField
31076  * @param {Object} config The config object
31077  */
31078
31079 Roo.bootstrap.DateSplitField = function(config){
31080     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
31081     
31082     this.addEvents({
31083         // raw events
31084          /**
31085          * @event years
31086          * getting the data of years
31087          * @param {Roo.bootstrap.DateSplitField} this
31088          * @param {Object} years
31089          */
31090         "years" : true,
31091         /**
31092          * @event days
31093          * getting the data of days
31094          * @param {Roo.bootstrap.DateSplitField} this
31095          * @param {Object} days
31096          */
31097         "days" : true,
31098         /**
31099          * @event invalid
31100          * Fires after the field has been marked as invalid.
31101          * @param {Roo.form.Field} this
31102          * @param {String} msg The validation message
31103          */
31104         invalid : true,
31105        /**
31106          * @event valid
31107          * Fires after the field has been validated with no errors.
31108          * @param {Roo.form.Field} this
31109          */
31110         valid : true
31111     });
31112 };
31113
31114 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
31115     
31116     fieldLabel : '',
31117     labelAlign : 'top',
31118     labelWidth : 3,
31119     dayAllowBlank : false,
31120     monthAllowBlank : false,
31121     yearAllowBlank : false,
31122     dayPlaceholder : '',
31123     monthPlaceholder : '',
31124     yearPlaceholder : '',
31125     dayFormat : 'd',
31126     monthFormat : 'm',
31127     yearFormat : 'Y',
31128     isFormField : true,
31129     labellg : 0,
31130     labelmd : 0,
31131     labelsm : 0,
31132     labelxs : 0,
31133     
31134     getAutoCreate : function()
31135     {
31136         var cfg = {
31137             tag : 'div',
31138             cls : 'row roo-date-split-field-group',
31139             cn : [
31140                 {
31141                     tag : 'input',
31142                     type : 'hidden',
31143                     cls : 'form-hidden-field roo-date-split-field-group-value',
31144                     name : this.name
31145                 }
31146             ]
31147         };
31148         
31149         var labelCls = 'col-md-12';
31150         var contentCls = 'col-md-4';
31151         
31152         if(this.fieldLabel){
31153             
31154             var label = {
31155                 tag : 'div',
31156                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
31157                 cn : [
31158                     {
31159                         tag : 'label',
31160                         html : this.fieldLabel
31161                     }
31162                 ]
31163             };
31164             
31165             if(this.labelAlign == 'left'){
31166             
31167                 if(this.labelWidth > 12){
31168                     label.style = "width: " + this.labelWidth + 'px';
31169                 }
31170
31171                 if(this.labelWidth < 13 && this.labelmd == 0){
31172                     this.labelmd = this.labelWidth;
31173                 }
31174
31175                 if(this.labellg > 0){
31176                     labelCls = ' col-lg-' + this.labellg;
31177                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
31178                 }
31179
31180                 if(this.labelmd > 0){
31181                     labelCls = ' col-md-' + this.labelmd;
31182                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
31183                 }
31184
31185                 if(this.labelsm > 0){
31186                     labelCls = ' col-sm-' + this.labelsm;
31187                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
31188                 }
31189
31190                 if(this.labelxs > 0){
31191                     labelCls = ' col-xs-' + this.labelxs;
31192                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
31193                 }
31194             }
31195             
31196             label.cls += ' ' + labelCls;
31197             
31198             cfg.cn.push(label);
31199         }
31200         
31201         Roo.each(['day', 'month', 'year'], function(t){
31202             cfg.cn.push({
31203                 tag : 'div',
31204                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
31205             });
31206         }, this);
31207         
31208         return cfg;
31209     },
31210     
31211     inputEl: function ()
31212     {
31213         return this.el.select('.roo-date-split-field-group-value', true).first();
31214     },
31215     
31216     onRender : function(ct, position) 
31217     {
31218         var _this = this;
31219         
31220         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31221         
31222         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
31223         
31224         this.dayField = new Roo.bootstrap.ComboBox({
31225             allowBlank : this.dayAllowBlank,
31226             alwaysQuery : true,
31227             displayField : 'value',
31228             editable : false,
31229             fieldLabel : '',
31230             forceSelection : true,
31231             mode : 'local',
31232             placeholder : this.dayPlaceholder,
31233             selectOnFocus : true,
31234             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31235             triggerAction : 'all',
31236             typeAhead : true,
31237             valueField : 'value',
31238             store : new Roo.data.SimpleStore({
31239                 data : (function() {    
31240                     var days = [];
31241                     _this.fireEvent('days', _this, days);
31242                     return days;
31243                 })(),
31244                 fields : [ 'value' ]
31245             }),
31246             listeners : {
31247                 select : function (_self, record, index)
31248                 {
31249                     _this.setValue(_this.getValue());
31250                 }
31251             }
31252         });
31253
31254         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
31255         
31256         this.monthField = new Roo.bootstrap.MonthField({
31257             after : '<i class=\"fa fa-calendar\"></i>',
31258             allowBlank : this.monthAllowBlank,
31259             placeholder : this.monthPlaceholder,
31260             readOnly : true,
31261             listeners : {
31262                 render : function (_self)
31263                 {
31264                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
31265                         e.preventDefault();
31266                         _self.focus();
31267                     });
31268                 },
31269                 select : function (_self, oldvalue, newvalue)
31270                 {
31271                     _this.setValue(_this.getValue());
31272                 }
31273             }
31274         });
31275         
31276         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
31277         
31278         this.yearField = new Roo.bootstrap.ComboBox({
31279             allowBlank : this.yearAllowBlank,
31280             alwaysQuery : true,
31281             displayField : 'value',
31282             editable : false,
31283             fieldLabel : '',
31284             forceSelection : true,
31285             mode : 'local',
31286             placeholder : this.yearPlaceholder,
31287             selectOnFocus : true,
31288             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31289             triggerAction : 'all',
31290             typeAhead : true,
31291             valueField : 'value',
31292             store : new Roo.data.SimpleStore({
31293                 data : (function() {
31294                     var years = [];
31295                     _this.fireEvent('years', _this, years);
31296                     return years;
31297                 })(),
31298                 fields : [ 'value' ]
31299             }),
31300             listeners : {
31301                 select : function (_self, record, index)
31302                 {
31303                     _this.setValue(_this.getValue());
31304                 }
31305             }
31306         });
31307
31308         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
31309     },
31310     
31311     setValue : function(v, format)
31312     {
31313         this.inputEl.dom.value = v;
31314         
31315         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
31316         
31317         var d = Date.parseDate(v, f);
31318         
31319         if(!d){
31320             this.validate();
31321             return;
31322         }
31323         
31324         this.setDay(d.format(this.dayFormat));
31325         this.setMonth(d.format(this.monthFormat));
31326         this.setYear(d.format(this.yearFormat));
31327         
31328         this.validate();
31329         
31330         return;
31331     },
31332     
31333     setDay : function(v)
31334     {
31335         this.dayField.setValue(v);
31336         this.inputEl.dom.value = this.getValue();
31337         this.validate();
31338         return;
31339     },
31340     
31341     setMonth : function(v)
31342     {
31343         this.monthField.setValue(v, true);
31344         this.inputEl.dom.value = this.getValue();
31345         this.validate();
31346         return;
31347     },
31348     
31349     setYear : function(v)
31350     {
31351         this.yearField.setValue(v);
31352         this.inputEl.dom.value = this.getValue();
31353         this.validate();
31354         return;
31355     },
31356     
31357     getDay : function()
31358     {
31359         return this.dayField.getValue();
31360     },
31361     
31362     getMonth : function()
31363     {
31364         return this.monthField.getValue();
31365     },
31366     
31367     getYear : function()
31368     {
31369         return this.yearField.getValue();
31370     },
31371     
31372     getValue : function()
31373     {
31374         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31375         
31376         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31377         
31378         return date;
31379     },
31380     
31381     reset : function()
31382     {
31383         this.setDay('');
31384         this.setMonth('');
31385         this.setYear('');
31386         this.inputEl.dom.value = '';
31387         this.validate();
31388         return;
31389     },
31390     
31391     validate : function()
31392     {
31393         var d = this.dayField.validate();
31394         var m = this.monthField.validate();
31395         var y = this.yearField.validate();
31396         
31397         var valid = true;
31398         
31399         if(
31400                 (!this.dayAllowBlank && !d) ||
31401                 (!this.monthAllowBlank && !m) ||
31402                 (!this.yearAllowBlank && !y)
31403         ){
31404             valid = false;
31405         }
31406         
31407         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31408             return valid;
31409         }
31410         
31411         if(valid){
31412             this.markValid();
31413             return valid;
31414         }
31415         
31416         this.markInvalid();
31417         
31418         return valid;
31419     },
31420     
31421     markValid : function()
31422     {
31423         
31424         var label = this.el.select('label', true).first();
31425         var icon = this.el.select('i.fa-star', true).first();
31426
31427         if(label && icon){
31428             icon.remove();
31429         }
31430         
31431         this.fireEvent('valid', this);
31432     },
31433     
31434      /**
31435      * Mark this field as invalid
31436      * @param {String} msg The validation message
31437      */
31438     markInvalid : function(msg)
31439     {
31440         
31441         var label = this.el.select('label', true).first();
31442         var icon = this.el.select('i.fa-star', true).first();
31443
31444         if(label && !icon){
31445             this.el.select('.roo-date-split-field-label', true).createChild({
31446                 tag : 'i',
31447                 cls : 'text-danger fa fa-lg fa-star',
31448                 tooltip : 'This field is required',
31449                 style : 'margin-right:5px;'
31450             }, label, true);
31451         }
31452         
31453         this.fireEvent('invalid', this, msg);
31454     },
31455     
31456     clearInvalid : function()
31457     {
31458         var label = this.el.select('label', true).first();
31459         var icon = this.el.select('i.fa-star', true).first();
31460
31461         if(label && icon){
31462             icon.remove();
31463         }
31464         
31465         this.fireEvent('valid', this);
31466     },
31467     
31468     getName: function()
31469     {
31470         return this.name;
31471     }
31472     
31473 });
31474
31475  /**
31476  *
31477  * This is based on 
31478  * http://masonry.desandro.com
31479  *
31480  * The idea is to render all the bricks based on vertical width...
31481  *
31482  * The original code extends 'outlayer' - we might need to use that....
31483  * 
31484  */
31485
31486
31487 /**
31488  * @class Roo.bootstrap.LayoutMasonry
31489  * @extends Roo.bootstrap.Component
31490  * Bootstrap Layout Masonry class
31491  * 
31492  * @constructor
31493  * Create a new Element
31494  * @param {Object} config The config object
31495  */
31496
31497 Roo.bootstrap.LayoutMasonry = function(config){
31498     
31499     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31500     
31501     this.bricks = [];
31502     
31503     Roo.bootstrap.LayoutMasonry.register(this);
31504     
31505     this.addEvents({
31506         // raw events
31507         /**
31508          * @event layout
31509          * Fire after layout the items
31510          * @param {Roo.bootstrap.LayoutMasonry} this
31511          * @param {Roo.EventObject} e
31512          */
31513         "layout" : true
31514     });
31515     
31516 };
31517
31518 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31519     
31520     /**
31521      * @cfg {Boolean} isLayoutInstant = no animation?
31522      */   
31523     isLayoutInstant : false, // needed?
31524    
31525     /**
31526      * @cfg {Number} boxWidth  width of the columns
31527      */   
31528     boxWidth : 450,
31529     
31530       /**
31531      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31532      */   
31533     boxHeight : 0,
31534     
31535     /**
31536      * @cfg {Number} padWidth padding below box..
31537      */   
31538     padWidth : 10, 
31539     
31540     /**
31541      * @cfg {Number} gutter gutter width..
31542      */   
31543     gutter : 10,
31544     
31545      /**
31546      * @cfg {Number} maxCols maximum number of columns
31547      */   
31548     
31549     maxCols: 0,
31550     
31551     /**
31552      * @cfg {Boolean} isAutoInitial defalut true
31553      */   
31554     isAutoInitial : true, 
31555     
31556     containerWidth: 0,
31557     
31558     /**
31559      * @cfg {Boolean} isHorizontal defalut false
31560      */   
31561     isHorizontal : false, 
31562
31563     currentSize : null,
31564     
31565     tag: 'div',
31566     
31567     cls: '',
31568     
31569     bricks: null, //CompositeElement
31570     
31571     cols : 1,
31572     
31573     _isLayoutInited : false,
31574     
31575 //    isAlternative : false, // only use for vertical layout...
31576     
31577     /**
31578      * @cfg {Number} alternativePadWidth padding below box..
31579      */   
31580     alternativePadWidth : 50,
31581     
31582     selectedBrick : [],
31583     
31584     getAutoCreate : function(){
31585         
31586         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31587         
31588         var cfg = {
31589             tag: this.tag,
31590             cls: 'blog-masonary-wrapper ' + this.cls,
31591             cn : {
31592                 cls : 'mas-boxes masonary'
31593             }
31594         };
31595         
31596         return cfg;
31597     },
31598     
31599     getChildContainer: function( )
31600     {
31601         if (this.boxesEl) {
31602             return this.boxesEl;
31603         }
31604         
31605         this.boxesEl = this.el.select('.mas-boxes').first();
31606         
31607         return this.boxesEl;
31608     },
31609     
31610     
31611     initEvents : function()
31612     {
31613         var _this = this;
31614         
31615         if(this.isAutoInitial){
31616             Roo.log('hook children rendered');
31617             this.on('childrenrendered', function() {
31618                 Roo.log('children rendered');
31619                 _this.initial();
31620             } ,this);
31621         }
31622     },
31623     
31624     initial : function()
31625     {
31626         this.selectedBrick = [];
31627         
31628         this.currentSize = this.el.getBox(true);
31629         
31630         Roo.EventManager.onWindowResize(this.resize, this); 
31631
31632         if(!this.isAutoInitial){
31633             this.layout();
31634             return;
31635         }
31636         
31637         this.layout();
31638         
31639         return;
31640         //this.layout.defer(500,this);
31641         
31642     },
31643     
31644     resize : function()
31645     {
31646         var cs = this.el.getBox(true);
31647         
31648         if (
31649                 this.currentSize.width == cs.width && 
31650                 this.currentSize.x == cs.x && 
31651                 this.currentSize.height == cs.height && 
31652                 this.currentSize.y == cs.y 
31653         ) {
31654             Roo.log("no change in with or X or Y");
31655             return;
31656         }
31657         
31658         this.currentSize = cs;
31659         
31660         this.layout();
31661         
31662     },
31663     
31664     layout : function()
31665     {   
31666         this._resetLayout();
31667         
31668         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31669         
31670         this.layoutItems( isInstant );
31671       
31672         this._isLayoutInited = true;
31673         
31674         this.fireEvent('layout', this);
31675         
31676     },
31677     
31678     _resetLayout : function()
31679     {
31680         if(this.isHorizontal){
31681             this.horizontalMeasureColumns();
31682             return;
31683         }
31684         
31685         this.verticalMeasureColumns();
31686         
31687     },
31688     
31689     verticalMeasureColumns : function()
31690     {
31691         this.getContainerWidth();
31692         
31693 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31694 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31695 //            return;
31696 //        }
31697         
31698         var boxWidth = this.boxWidth + this.padWidth;
31699         
31700         if(this.containerWidth < this.boxWidth){
31701             boxWidth = this.containerWidth
31702         }
31703         
31704         var containerWidth = this.containerWidth;
31705         
31706         var cols = Math.floor(containerWidth / boxWidth);
31707         
31708         this.cols = Math.max( cols, 1 );
31709         
31710         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31711         
31712         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31713         
31714         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31715         
31716         this.colWidth = boxWidth + avail - this.padWidth;
31717         
31718         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31719         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31720     },
31721     
31722     horizontalMeasureColumns : function()
31723     {
31724         this.getContainerWidth();
31725         
31726         var boxWidth = this.boxWidth;
31727         
31728         if(this.containerWidth < boxWidth){
31729             boxWidth = this.containerWidth;
31730         }
31731         
31732         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31733         
31734         this.el.setHeight(boxWidth);
31735         
31736     },
31737     
31738     getContainerWidth : function()
31739     {
31740         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31741     },
31742     
31743     layoutItems : function( isInstant )
31744     {
31745         Roo.log(this.bricks);
31746         
31747         var items = Roo.apply([], this.bricks);
31748         
31749         if(this.isHorizontal){
31750             this._horizontalLayoutItems( items , isInstant );
31751             return;
31752         }
31753         
31754 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31755 //            this._verticalAlternativeLayoutItems( items , isInstant );
31756 //            return;
31757 //        }
31758         
31759         this._verticalLayoutItems( items , isInstant );
31760         
31761     },
31762     
31763     _verticalLayoutItems : function ( items , isInstant)
31764     {
31765         if ( !items || !items.length ) {
31766             return;
31767         }
31768         
31769         var standard = [
31770             ['xs', 'xs', 'xs', 'tall'],
31771             ['xs', 'xs', 'tall'],
31772             ['xs', 'xs', 'sm'],
31773             ['xs', 'xs', 'xs'],
31774             ['xs', 'tall'],
31775             ['xs', 'sm'],
31776             ['xs', 'xs'],
31777             ['xs'],
31778             
31779             ['sm', 'xs', 'xs'],
31780             ['sm', 'xs'],
31781             ['sm'],
31782             
31783             ['tall', 'xs', 'xs', 'xs'],
31784             ['tall', 'xs', 'xs'],
31785             ['tall', 'xs'],
31786             ['tall']
31787             
31788         ];
31789         
31790         var queue = [];
31791         
31792         var boxes = [];
31793         
31794         var box = [];
31795         
31796         Roo.each(items, function(item, k){
31797             
31798             switch (item.size) {
31799                 // these layouts take up a full box,
31800                 case 'md' :
31801                 case 'md-left' :
31802                 case 'md-right' :
31803                 case 'wide' :
31804                     
31805                     if(box.length){
31806                         boxes.push(box);
31807                         box = [];
31808                     }
31809                     
31810                     boxes.push([item]);
31811                     
31812                     break;
31813                     
31814                 case 'xs' :
31815                 case 'sm' :
31816                 case 'tall' :
31817                     
31818                     box.push(item);
31819                     
31820                     break;
31821                 default :
31822                     break;
31823                     
31824             }
31825             
31826         }, this);
31827         
31828         if(box.length){
31829             boxes.push(box);
31830             box = [];
31831         }
31832         
31833         var filterPattern = function(box, length)
31834         {
31835             if(!box.length){
31836                 return;
31837             }
31838             
31839             var match = false;
31840             
31841             var pattern = box.slice(0, length);
31842             
31843             var format = [];
31844             
31845             Roo.each(pattern, function(i){
31846                 format.push(i.size);
31847             }, this);
31848             
31849             Roo.each(standard, function(s){
31850                 
31851                 if(String(s) != String(format)){
31852                     return;
31853                 }
31854                 
31855                 match = true;
31856                 return false;
31857                 
31858             }, this);
31859             
31860             if(!match && length == 1){
31861                 return;
31862             }
31863             
31864             if(!match){
31865                 filterPattern(box, length - 1);
31866                 return;
31867             }
31868                 
31869             queue.push(pattern);
31870
31871             box = box.slice(length, box.length);
31872
31873             filterPattern(box, 4);
31874
31875             return;
31876             
31877         }
31878         
31879         Roo.each(boxes, function(box, k){
31880             
31881             if(!box.length){
31882                 return;
31883             }
31884             
31885             if(box.length == 1){
31886                 queue.push(box);
31887                 return;
31888             }
31889             
31890             filterPattern(box, 4);
31891             
31892         }, this);
31893         
31894         this._processVerticalLayoutQueue( queue, isInstant );
31895         
31896     },
31897     
31898 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31899 //    {
31900 //        if ( !items || !items.length ) {
31901 //            return;
31902 //        }
31903 //
31904 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31905 //        
31906 //    },
31907     
31908     _horizontalLayoutItems : function ( items , isInstant)
31909     {
31910         if ( !items || !items.length || items.length < 3) {
31911             return;
31912         }
31913         
31914         items.reverse();
31915         
31916         var eItems = items.slice(0, 3);
31917         
31918         items = items.slice(3, items.length);
31919         
31920         var standard = [
31921             ['xs', 'xs', 'xs', 'wide'],
31922             ['xs', 'xs', 'wide'],
31923             ['xs', 'xs', 'sm'],
31924             ['xs', 'xs', 'xs'],
31925             ['xs', 'wide'],
31926             ['xs', 'sm'],
31927             ['xs', 'xs'],
31928             ['xs'],
31929             
31930             ['sm', 'xs', 'xs'],
31931             ['sm', 'xs'],
31932             ['sm'],
31933             
31934             ['wide', 'xs', 'xs', 'xs'],
31935             ['wide', 'xs', 'xs'],
31936             ['wide', 'xs'],
31937             ['wide'],
31938             
31939             ['wide-thin']
31940         ];
31941         
31942         var queue = [];
31943         
31944         var boxes = [];
31945         
31946         var box = [];
31947         
31948         Roo.each(items, function(item, k){
31949             
31950             switch (item.size) {
31951                 case 'md' :
31952                 case 'md-left' :
31953                 case 'md-right' :
31954                 case 'tall' :
31955                     
31956                     if(box.length){
31957                         boxes.push(box);
31958                         box = [];
31959                     }
31960                     
31961                     boxes.push([item]);
31962                     
31963                     break;
31964                     
31965                 case 'xs' :
31966                 case 'sm' :
31967                 case 'wide' :
31968                 case 'wide-thin' :
31969                     
31970                     box.push(item);
31971                     
31972                     break;
31973                 default :
31974                     break;
31975                     
31976             }
31977             
31978         }, this);
31979         
31980         if(box.length){
31981             boxes.push(box);
31982             box = [];
31983         }
31984         
31985         var filterPattern = function(box, length)
31986         {
31987             if(!box.length){
31988                 return;
31989             }
31990             
31991             var match = false;
31992             
31993             var pattern = box.slice(0, length);
31994             
31995             var format = [];
31996             
31997             Roo.each(pattern, function(i){
31998                 format.push(i.size);
31999             }, this);
32000             
32001             Roo.each(standard, function(s){
32002                 
32003                 if(String(s) != String(format)){
32004                     return;
32005                 }
32006                 
32007                 match = true;
32008                 return false;
32009                 
32010             }, this);
32011             
32012             if(!match && length == 1){
32013                 return;
32014             }
32015             
32016             if(!match){
32017                 filterPattern(box, length - 1);
32018                 return;
32019             }
32020                 
32021             queue.push(pattern);
32022
32023             box = box.slice(length, box.length);
32024
32025             filterPattern(box, 4);
32026
32027             return;
32028             
32029         }
32030         
32031         Roo.each(boxes, function(box, k){
32032             
32033             if(!box.length){
32034                 return;
32035             }
32036             
32037             if(box.length == 1){
32038                 queue.push(box);
32039                 return;
32040             }
32041             
32042             filterPattern(box, 4);
32043             
32044         }, this);
32045         
32046         
32047         var prune = [];
32048         
32049         var pos = this.el.getBox(true);
32050         
32051         var minX = pos.x;
32052         
32053         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32054         
32055         var hit_end = false;
32056         
32057         Roo.each(queue, function(box){
32058             
32059             if(hit_end){
32060                 
32061                 Roo.each(box, function(b){
32062                 
32063                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
32064                     b.el.hide();
32065
32066                 }, this);
32067
32068                 return;
32069             }
32070             
32071             var mx = 0;
32072             
32073             Roo.each(box, function(b){
32074                 
32075                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32076                 b.el.show();
32077
32078                 mx = Math.max(mx, b.x);
32079                 
32080             }, this);
32081             
32082             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
32083             
32084             if(maxX < minX){
32085                 
32086                 Roo.each(box, function(b){
32087                 
32088                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
32089                     b.el.hide();
32090                     
32091                 }, this);
32092                 
32093                 hit_end = true;
32094                 
32095                 return;
32096             }
32097             
32098             prune.push(box);
32099             
32100         }, this);
32101         
32102         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
32103     },
32104     
32105     /** Sets position of item in DOM
32106     * @param {Element} item
32107     * @param {Number} x - horizontal position
32108     * @param {Number} y - vertical position
32109     * @param {Boolean} isInstant - disables transitions
32110     */
32111     _processVerticalLayoutQueue : function( queue, isInstant )
32112     {
32113         var pos = this.el.getBox(true);
32114         var x = pos.x;
32115         var y = pos.y;
32116         var maxY = [];
32117         
32118         for (var i = 0; i < this.cols; i++){
32119             maxY[i] = pos.y;
32120         }
32121         
32122         Roo.each(queue, function(box, k){
32123             
32124             var col = k % this.cols;
32125             
32126             Roo.each(box, function(b,kk){
32127                 
32128                 b.el.position('absolute');
32129                 
32130                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32131                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32132                 
32133                 if(b.size == 'md-left' || b.size == 'md-right'){
32134                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32135                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32136                 }
32137                 
32138                 b.el.setWidth(width);
32139                 b.el.setHeight(height);
32140                 // iframe?
32141                 b.el.select('iframe',true).setSize(width,height);
32142                 
32143             }, this);
32144             
32145             for (var i = 0; i < this.cols; i++){
32146                 
32147                 if(maxY[i] < maxY[col]){
32148                     col = i;
32149                     continue;
32150                 }
32151                 
32152                 col = Math.min(col, i);
32153                 
32154             }
32155             
32156             x = pos.x + col * (this.colWidth + this.padWidth);
32157             
32158             y = maxY[col];
32159             
32160             var positions = [];
32161             
32162             switch (box.length){
32163                 case 1 :
32164                     positions = this.getVerticalOneBoxColPositions(x, y, box);
32165                     break;
32166                 case 2 :
32167                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
32168                     break;
32169                 case 3 :
32170                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
32171                     break;
32172                 case 4 :
32173                     positions = this.getVerticalFourBoxColPositions(x, y, box);
32174                     break;
32175                 default :
32176                     break;
32177             }
32178             
32179             Roo.each(box, function(b,kk){
32180                 
32181                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32182                 
32183                 var sz = b.el.getSize();
32184                 
32185                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
32186                 
32187             }, this);
32188             
32189         }, this);
32190         
32191         var mY = 0;
32192         
32193         for (var i = 0; i < this.cols; i++){
32194             mY = Math.max(mY, maxY[i]);
32195         }
32196         
32197         this.el.setHeight(mY - pos.y);
32198         
32199     },
32200     
32201 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
32202 //    {
32203 //        var pos = this.el.getBox(true);
32204 //        var x = pos.x;
32205 //        var y = pos.y;
32206 //        var maxX = pos.right;
32207 //        
32208 //        var maxHeight = 0;
32209 //        
32210 //        Roo.each(items, function(item, k){
32211 //            
32212 //            var c = k % 2;
32213 //            
32214 //            item.el.position('absolute');
32215 //                
32216 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
32217 //
32218 //            item.el.setWidth(width);
32219 //
32220 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
32221 //
32222 //            item.el.setHeight(height);
32223 //            
32224 //            if(c == 0){
32225 //                item.el.setXY([x, y], isInstant ? false : true);
32226 //            } else {
32227 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
32228 //            }
32229 //            
32230 //            y = y + height + this.alternativePadWidth;
32231 //            
32232 //            maxHeight = maxHeight + height + this.alternativePadWidth;
32233 //            
32234 //        }, this);
32235 //        
32236 //        this.el.setHeight(maxHeight);
32237 //        
32238 //    },
32239     
32240     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
32241     {
32242         var pos = this.el.getBox(true);
32243         
32244         var minX = pos.x;
32245         var minY = pos.y;
32246         
32247         var maxX = pos.right;
32248         
32249         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
32250         
32251         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32252         
32253         Roo.each(queue, function(box, k){
32254             
32255             Roo.each(box, function(b, kk){
32256                 
32257                 b.el.position('absolute');
32258                 
32259                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32260                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32261                 
32262                 if(b.size == 'md-left' || b.size == 'md-right'){
32263                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32264                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32265                 }
32266                 
32267                 b.el.setWidth(width);
32268                 b.el.setHeight(height);
32269                 
32270             }, this);
32271             
32272             if(!box.length){
32273                 return;
32274             }
32275             
32276             var positions = [];
32277             
32278             switch (box.length){
32279                 case 1 :
32280                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
32281                     break;
32282                 case 2 :
32283                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
32284                     break;
32285                 case 3 :
32286                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
32287                     break;
32288                 case 4 :
32289                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
32290                     break;
32291                 default :
32292                     break;
32293             }
32294             
32295             Roo.each(box, function(b,kk){
32296                 
32297                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32298                 
32299                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
32300                 
32301             }, this);
32302             
32303         }, this);
32304         
32305     },
32306     
32307     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
32308     {
32309         Roo.each(eItems, function(b,k){
32310             
32311             b.size = (k == 0) ? 'sm' : 'xs';
32312             b.x = (k == 0) ? 2 : 1;
32313             b.y = (k == 0) ? 2 : 1;
32314             
32315             b.el.position('absolute');
32316             
32317             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32318                 
32319             b.el.setWidth(width);
32320             
32321             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32322             
32323             b.el.setHeight(height);
32324             
32325         }, this);
32326
32327         var positions = [];
32328         
32329         positions.push({
32330             x : maxX - this.unitWidth * 2 - this.gutter,
32331             y : minY
32332         });
32333         
32334         positions.push({
32335             x : maxX - this.unitWidth,
32336             y : minY + (this.unitWidth + this.gutter) * 2
32337         });
32338         
32339         positions.push({
32340             x : maxX - this.unitWidth * 3 - this.gutter * 2,
32341             y : minY
32342         });
32343         
32344         Roo.each(eItems, function(b,k){
32345             
32346             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
32347
32348         }, this);
32349         
32350     },
32351     
32352     getVerticalOneBoxColPositions : function(x, y, box)
32353     {
32354         var pos = [];
32355         
32356         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32357         
32358         if(box[0].size == 'md-left'){
32359             rand = 0;
32360         }
32361         
32362         if(box[0].size == 'md-right'){
32363             rand = 1;
32364         }
32365         
32366         pos.push({
32367             x : x + (this.unitWidth + this.gutter) * rand,
32368             y : y
32369         });
32370         
32371         return pos;
32372     },
32373     
32374     getVerticalTwoBoxColPositions : function(x, y, box)
32375     {
32376         var pos = [];
32377         
32378         if(box[0].size == 'xs'){
32379             
32380             pos.push({
32381                 x : x,
32382                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32383             });
32384
32385             pos.push({
32386                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32387                 y : y
32388             });
32389             
32390             return pos;
32391             
32392         }
32393         
32394         pos.push({
32395             x : x,
32396             y : y
32397         });
32398
32399         pos.push({
32400             x : x + (this.unitWidth + this.gutter) * 2,
32401             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32402         });
32403         
32404         return pos;
32405         
32406     },
32407     
32408     getVerticalThreeBoxColPositions : function(x, y, box)
32409     {
32410         var pos = [];
32411         
32412         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32413             
32414             pos.push({
32415                 x : x,
32416                 y : y
32417             });
32418
32419             pos.push({
32420                 x : x + (this.unitWidth + this.gutter) * 1,
32421                 y : y
32422             });
32423             
32424             pos.push({
32425                 x : x + (this.unitWidth + this.gutter) * 2,
32426                 y : y
32427             });
32428             
32429             return pos;
32430             
32431         }
32432         
32433         if(box[0].size == 'xs' && box[1].size == 'xs'){
32434             
32435             pos.push({
32436                 x : x,
32437                 y : y
32438             });
32439
32440             pos.push({
32441                 x : x,
32442                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32443             });
32444             
32445             pos.push({
32446                 x : x + (this.unitWidth + this.gutter) * 1,
32447                 y : y
32448             });
32449             
32450             return pos;
32451             
32452         }
32453         
32454         pos.push({
32455             x : x,
32456             y : y
32457         });
32458
32459         pos.push({
32460             x : x + (this.unitWidth + this.gutter) * 2,
32461             y : y
32462         });
32463
32464         pos.push({
32465             x : x + (this.unitWidth + this.gutter) * 2,
32466             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32467         });
32468             
32469         return pos;
32470         
32471     },
32472     
32473     getVerticalFourBoxColPositions : function(x, y, box)
32474     {
32475         var pos = [];
32476         
32477         if(box[0].size == 'xs'){
32478             
32479             pos.push({
32480                 x : x,
32481                 y : y
32482             });
32483
32484             pos.push({
32485                 x : x,
32486                 y : y + (this.unitHeight + this.gutter) * 1
32487             });
32488             
32489             pos.push({
32490                 x : x,
32491                 y : y + (this.unitHeight + this.gutter) * 2
32492             });
32493             
32494             pos.push({
32495                 x : x + (this.unitWidth + this.gutter) * 1,
32496                 y : y
32497             });
32498             
32499             return pos;
32500             
32501         }
32502         
32503         pos.push({
32504             x : x,
32505             y : y
32506         });
32507
32508         pos.push({
32509             x : x + (this.unitWidth + this.gutter) * 2,
32510             y : y
32511         });
32512
32513         pos.push({
32514             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32515             y : y + (this.unitHeight + this.gutter) * 1
32516         });
32517
32518         pos.push({
32519             x : x + (this.unitWidth + this.gutter) * 2,
32520             y : y + (this.unitWidth + this.gutter) * 2
32521         });
32522
32523         return pos;
32524         
32525     },
32526     
32527     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32528     {
32529         var pos = [];
32530         
32531         if(box[0].size == 'md-left'){
32532             pos.push({
32533                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32534                 y : minY
32535             });
32536             
32537             return pos;
32538         }
32539         
32540         if(box[0].size == 'md-right'){
32541             pos.push({
32542                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32543                 y : minY + (this.unitWidth + this.gutter) * 1
32544             });
32545             
32546             return pos;
32547         }
32548         
32549         var rand = Math.floor(Math.random() * (4 - box[0].y));
32550         
32551         pos.push({
32552             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32553             y : minY + (this.unitWidth + this.gutter) * rand
32554         });
32555         
32556         return pos;
32557         
32558     },
32559     
32560     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32561     {
32562         var pos = [];
32563         
32564         if(box[0].size == 'xs'){
32565             
32566             pos.push({
32567                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32568                 y : minY
32569             });
32570
32571             pos.push({
32572                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32573                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32574             });
32575             
32576             return pos;
32577             
32578         }
32579         
32580         pos.push({
32581             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32582             y : minY
32583         });
32584
32585         pos.push({
32586             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32587             y : minY + (this.unitWidth + this.gutter) * 2
32588         });
32589         
32590         return pos;
32591         
32592     },
32593     
32594     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32595     {
32596         var pos = [];
32597         
32598         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32599             
32600             pos.push({
32601                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32602                 y : minY
32603             });
32604
32605             pos.push({
32606                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32607                 y : minY + (this.unitWidth + this.gutter) * 1
32608             });
32609             
32610             pos.push({
32611                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32612                 y : minY + (this.unitWidth + this.gutter) * 2
32613             });
32614             
32615             return pos;
32616             
32617         }
32618         
32619         if(box[0].size == 'xs' && box[1].size == 'xs'){
32620             
32621             pos.push({
32622                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32623                 y : minY
32624             });
32625
32626             pos.push({
32627                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32628                 y : minY
32629             });
32630             
32631             pos.push({
32632                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32633                 y : minY + (this.unitWidth + this.gutter) * 1
32634             });
32635             
32636             return pos;
32637             
32638         }
32639         
32640         pos.push({
32641             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32642             y : minY
32643         });
32644
32645         pos.push({
32646             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32647             y : minY + (this.unitWidth + this.gutter) * 2
32648         });
32649
32650         pos.push({
32651             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32652             y : minY + (this.unitWidth + this.gutter) * 2
32653         });
32654             
32655         return pos;
32656         
32657     },
32658     
32659     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32660     {
32661         var pos = [];
32662         
32663         if(box[0].size == 'xs'){
32664             
32665             pos.push({
32666                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32667                 y : minY
32668             });
32669
32670             pos.push({
32671                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32672                 y : minY
32673             });
32674             
32675             pos.push({
32676                 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),
32677                 y : minY
32678             });
32679             
32680             pos.push({
32681                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32682                 y : minY + (this.unitWidth + this.gutter) * 1
32683             });
32684             
32685             return pos;
32686             
32687         }
32688         
32689         pos.push({
32690             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32691             y : minY
32692         });
32693         
32694         pos.push({
32695             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32696             y : minY + (this.unitWidth + this.gutter) * 2
32697         });
32698         
32699         pos.push({
32700             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32701             y : minY + (this.unitWidth + this.gutter) * 2
32702         });
32703         
32704         pos.push({
32705             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),
32706             y : minY + (this.unitWidth + this.gutter) * 2
32707         });
32708
32709         return pos;
32710         
32711     },
32712     
32713     /**
32714     * remove a Masonry Brick
32715     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32716     */
32717     removeBrick : function(brick_id)
32718     {
32719         if (!brick_id) {
32720             return;
32721         }
32722         
32723         for (var i = 0; i<this.bricks.length; i++) {
32724             if (this.bricks[i].id == brick_id) {
32725                 this.bricks.splice(i,1);
32726                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32727                 this.initial();
32728             }
32729         }
32730     },
32731     
32732     /**
32733     * adds a Masonry Brick
32734     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32735     */
32736     addBrick : function(cfg)
32737     {
32738         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32739         //this.register(cn);
32740         cn.parentId = this.id;
32741         cn.render(this.el);
32742         return cn;
32743     },
32744     
32745     /**
32746     * register a Masonry Brick
32747     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32748     */
32749     
32750     register : function(brick)
32751     {
32752         this.bricks.push(brick);
32753         brick.masonryId = this.id;
32754     },
32755     
32756     /**
32757     * clear all the Masonry Brick
32758     */
32759     clearAll : function()
32760     {
32761         this.bricks = [];
32762         //this.getChildContainer().dom.innerHTML = "";
32763         this.el.dom.innerHTML = '';
32764     },
32765     
32766     getSelected : function()
32767     {
32768         if (!this.selectedBrick) {
32769             return false;
32770         }
32771         
32772         return this.selectedBrick;
32773     }
32774 });
32775
32776 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32777     
32778     groups: {},
32779      /**
32780     * register a Masonry Layout
32781     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32782     */
32783     
32784     register : function(layout)
32785     {
32786         this.groups[layout.id] = layout;
32787     },
32788     /**
32789     * fetch a  Masonry Layout based on the masonry layout ID
32790     * @param {string} the masonry layout to add
32791     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32792     */
32793     
32794     get: function(layout_id) {
32795         if (typeof(this.groups[layout_id]) == 'undefined') {
32796             return false;
32797         }
32798         return this.groups[layout_id] ;
32799     }
32800     
32801     
32802     
32803 });
32804
32805  
32806
32807  /**
32808  *
32809  * This is based on 
32810  * http://masonry.desandro.com
32811  *
32812  * The idea is to render all the bricks based on vertical width...
32813  *
32814  * The original code extends 'outlayer' - we might need to use that....
32815  * 
32816  */
32817
32818
32819 /**
32820  * @class Roo.bootstrap.LayoutMasonryAuto
32821  * @extends Roo.bootstrap.Component
32822  * Bootstrap Layout Masonry class
32823  * 
32824  * @constructor
32825  * Create a new Element
32826  * @param {Object} config The config object
32827  */
32828
32829 Roo.bootstrap.LayoutMasonryAuto = function(config){
32830     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32831 };
32832
32833 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32834     
32835       /**
32836      * @cfg {Boolean} isFitWidth  - resize the width..
32837      */   
32838     isFitWidth : false,  // options..
32839     /**
32840      * @cfg {Boolean} isOriginLeft = left align?
32841      */   
32842     isOriginLeft : true,
32843     /**
32844      * @cfg {Boolean} isOriginTop = top align?
32845      */   
32846     isOriginTop : false,
32847     /**
32848      * @cfg {Boolean} isLayoutInstant = no animation?
32849      */   
32850     isLayoutInstant : false, // needed?
32851     /**
32852      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32853      */   
32854     isResizingContainer : true,
32855     /**
32856      * @cfg {Number} columnWidth  width of the columns 
32857      */   
32858     
32859     columnWidth : 0,
32860     
32861     /**
32862      * @cfg {Number} maxCols maximum number of columns
32863      */   
32864     
32865     maxCols: 0,
32866     /**
32867      * @cfg {Number} padHeight padding below box..
32868      */   
32869     
32870     padHeight : 10, 
32871     
32872     /**
32873      * @cfg {Boolean} isAutoInitial defalut true
32874      */   
32875     
32876     isAutoInitial : true, 
32877     
32878     // private?
32879     gutter : 0,
32880     
32881     containerWidth: 0,
32882     initialColumnWidth : 0,
32883     currentSize : null,
32884     
32885     colYs : null, // array.
32886     maxY : 0,
32887     padWidth: 10,
32888     
32889     
32890     tag: 'div',
32891     cls: '',
32892     bricks: null, //CompositeElement
32893     cols : 0, // array?
32894     // element : null, // wrapped now this.el
32895     _isLayoutInited : null, 
32896     
32897     
32898     getAutoCreate : function(){
32899         
32900         var cfg = {
32901             tag: this.tag,
32902             cls: 'blog-masonary-wrapper ' + this.cls,
32903             cn : {
32904                 cls : 'mas-boxes masonary'
32905             }
32906         };
32907         
32908         return cfg;
32909     },
32910     
32911     getChildContainer: function( )
32912     {
32913         if (this.boxesEl) {
32914             return this.boxesEl;
32915         }
32916         
32917         this.boxesEl = this.el.select('.mas-boxes').first();
32918         
32919         return this.boxesEl;
32920     },
32921     
32922     
32923     initEvents : function()
32924     {
32925         var _this = this;
32926         
32927         if(this.isAutoInitial){
32928             Roo.log('hook children rendered');
32929             this.on('childrenrendered', function() {
32930                 Roo.log('children rendered');
32931                 _this.initial();
32932             } ,this);
32933         }
32934         
32935     },
32936     
32937     initial : function()
32938     {
32939         this.reloadItems();
32940
32941         this.currentSize = this.el.getBox(true);
32942
32943         /// was window resize... - let's see if this works..
32944         Roo.EventManager.onWindowResize(this.resize, this); 
32945
32946         if(!this.isAutoInitial){
32947             this.layout();
32948             return;
32949         }
32950         
32951         this.layout.defer(500,this);
32952     },
32953     
32954     reloadItems: function()
32955     {
32956         this.bricks = this.el.select('.masonry-brick', true);
32957         
32958         this.bricks.each(function(b) {
32959             //Roo.log(b.getSize());
32960             if (!b.attr('originalwidth')) {
32961                 b.attr('originalwidth',  b.getSize().width);
32962             }
32963             
32964         });
32965         
32966         Roo.log(this.bricks.elements.length);
32967     },
32968     
32969     resize : function()
32970     {
32971         Roo.log('resize');
32972         var cs = this.el.getBox(true);
32973         
32974         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32975             Roo.log("no change in with or X");
32976             return;
32977         }
32978         this.currentSize = cs;
32979         this.layout();
32980     },
32981     
32982     layout : function()
32983     {
32984          Roo.log('layout');
32985         this._resetLayout();
32986         //this._manageStamps();
32987       
32988         // don't animate first layout
32989         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32990         this.layoutItems( isInstant );
32991       
32992         // flag for initalized
32993         this._isLayoutInited = true;
32994     },
32995     
32996     layoutItems : function( isInstant )
32997     {
32998         //var items = this._getItemsForLayout( this.items );
32999         // original code supports filtering layout items.. we just ignore it..
33000         
33001         this._layoutItems( this.bricks , isInstant );
33002       
33003         this._postLayout();
33004     },
33005     _layoutItems : function ( items , isInstant)
33006     {
33007        //this.fireEvent( 'layout', this, items );
33008     
33009
33010         if ( !items || !items.elements.length ) {
33011           // no items, emit event with empty array
33012             return;
33013         }
33014
33015         var queue = [];
33016         items.each(function(item) {
33017             Roo.log("layout item");
33018             Roo.log(item);
33019             // get x/y object from method
33020             var position = this._getItemLayoutPosition( item );
33021             // enqueue
33022             position.item = item;
33023             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
33024             queue.push( position );
33025         }, this);
33026       
33027         this._processLayoutQueue( queue );
33028     },
33029     /** Sets position of item in DOM
33030     * @param {Element} item
33031     * @param {Number} x - horizontal position
33032     * @param {Number} y - vertical position
33033     * @param {Boolean} isInstant - disables transitions
33034     */
33035     _processLayoutQueue : function( queue )
33036     {
33037         for ( var i=0, len = queue.length; i < len; i++ ) {
33038             var obj = queue[i];
33039             obj.item.position('absolute');
33040             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
33041         }
33042     },
33043       
33044     
33045     /**
33046     * Any logic you want to do after each layout,
33047     * i.e. size the container
33048     */
33049     _postLayout : function()
33050     {
33051         this.resizeContainer();
33052     },
33053     
33054     resizeContainer : function()
33055     {
33056         if ( !this.isResizingContainer ) {
33057             return;
33058         }
33059         var size = this._getContainerSize();
33060         if ( size ) {
33061             this.el.setSize(size.width,size.height);
33062             this.boxesEl.setSize(size.width,size.height);
33063         }
33064     },
33065     
33066     
33067     
33068     _resetLayout : function()
33069     {
33070         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
33071         this.colWidth = this.el.getWidth();
33072         //this.gutter = this.el.getWidth(); 
33073         
33074         this.measureColumns();
33075
33076         // reset column Y
33077         var i = this.cols;
33078         this.colYs = [];
33079         while (i--) {
33080             this.colYs.push( 0 );
33081         }
33082     
33083         this.maxY = 0;
33084     },
33085
33086     measureColumns : function()
33087     {
33088         this.getContainerWidth();
33089       // if columnWidth is 0, default to outerWidth of first item
33090         if ( !this.columnWidth ) {
33091             var firstItem = this.bricks.first();
33092             Roo.log(firstItem);
33093             this.columnWidth  = this.containerWidth;
33094             if (firstItem && firstItem.attr('originalwidth') ) {
33095                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
33096             }
33097             // columnWidth fall back to item of first element
33098             Roo.log("set column width?");
33099                         this.initialColumnWidth = this.columnWidth  ;
33100
33101             // if first elem has no width, default to size of container
33102             
33103         }
33104         
33105         
33106         if (this.initialColumnWidth) {
33107             this.columnWidth = this.initialColumnWidth;
33108         }
33109         
33110         
33111             
33112         // column width is fixed at the top - however if container width get's smaller we should
33113         // reduce it...
33114         
33115         // this bit calcs how man columns..
33116             
33117         var columnWidth = this.columnWidth += this.gutter;
33118       
33119         // calculate columns
33120         var containerWidth = this.containerWidth + this.gutter;
33121         
33122         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
33123         // fix rounding errors, typically with gutters
33124         var excess = columnWidth - containerWidth % columnWidth;
33125         
33126         
33127         // if overshoot is less than a pixel, round up, otherwise floor it
33128         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
33129         cols = Math[ mathMethod ]( cols );
33130         this.cols = Math.max( cols, 1 );
33131         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33132         
33133          // padding positioning..
33134         var totalColWidth = this.cols * this.columnWidth;
33135         var padavail = this.containerWidth - totalColWidth;
33136         // so for 2 columns - we need 3 'pads'
33137         
33138         var padNeeded = (1+this.cols) * this.padWidth;
33139         
33140         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
33141         
33142         this.columnWidth += padExtra
33143         //this.padWidth = Math.floor(padavail /  ( this.cols));
33144         
33145         // adjust colum width so that padding is fixed??
33146         
33147         // we have 3 columns ... total = width * 3
33148         // we have X left over... that should be used by 
33149         
33150         //if (this.expandC) {
33151             
33152         //}
33153         
33154         
33155         
33156     },
33157     
33158     getContainerWidth : function()
33159     {
33160        /* // container is parent if fit width
33161         var container = this.isFitWidth ? this.element.parentNode : this.element;
33162         // check that this.size and size are there
33163         // IE8 triggers resize on body size change, so they might not be
33164         
33165         var size = getSize( container );  //FIXME
33166         this.containerWidth = size && size.innerWidth; //FIXME
33167         */
33168          
33169         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33170         
33171     },
33172     
33173     _getItemLayoutPosition : function( item )  // what is item?
33174     {
33175         // we resize the item to our columnWidth..
33176       
33177         item.setWidth(this.columnWidth);
33178         item.autoBoxAdjust  = false;
33179         
33180         var sz = item.getSize();
33181  
33182         // how many columns does this brick span
33183         var remainder = this.containerWidth % this.columnWidth;
33184         
33185         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
33186         // round if off by 1 pixel, otherwise use ceil
33187         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
33188         colSpan = Math.min( colSpan, this.cols );
33189         
33190         // normally this should be '1' as we dont' currently allow multi width columns..
33191         
33192         var colGroup = this._getColGroup( colSpan );
33193         // get the minimum Y value from the columns
33194         var minimumY = Math.min.apply( Math, colGroup );
33195         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
33196         
33197         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
33198          
33199         // position the brick
33200         var position = {
33201             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
33202             y: this.currentSize.y + minimumY + this.padHeight
33203         };
33204         
33205         Roo.log(position);
33206         // apply setHeight to necessary columns
33207         var setHeight = minimumY + sz.height + this.padHeight;
33208         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
33209         
33210         var setSpan = this.cols + 1 - colGroup.length;
33211         for ( var i = 0; i < setSpan; i++ ) {
33212           this.colYs[ shortColIndex + i ] = setHeight ;
33213         }
33214       
33215         return position;
33216     },
33217     
33218     /**
33219      * @param {Number} colSpan - number of columns the element spans
33220      * @returns {Array} colGroup
33221      */
33222     _getColGroup : function( colSpan )
33223     {
33224         if ( colSpan < 2 ) {
33225           // if brick spans only one column, use all the column Ys
33226           return this.colYs;
33227         }
33228       
33229         var colGroup = [];
33230         // how many different places could this brick fit horizontally
33231         var groupCount = this.cols + 1 - colSpan;
33232         // for each group potential horizontal position
33233         for ( var i = 0; i < groupCount; i++ ) {
33234           // make an array of colY values for that one group
33235           var groupColYs = this.colYs.slice( i, i + colSpan );
33236           // and get the max value of the array
33237           colGroup[i] = Math.max.apply( Math, groupColYs );
33238         }
33239         return colGroup;
33240     },
33241     /*
33242     _manageStamp : function( stamp )
33243     {
33244         var stampSize =  stamp.getSize();
33245         var offset = stamp.getBox();
33246         // get the columns that this stamp affects
33247         var firstX = this.isOriginLeft ? offset.x : offset.right;
33248         var lastX = firstX + stampSize.width;
33249         var firstCol = Math.floor( firstX / this.columnWidth );
33250         firstCol = Math.max( 0, firstCol );
33251         
33252         var lastCol = Math.floor( lastX / this.columnWidth );
33253         // lastCol should not go over if multiple of columnWidth #425
33254         lastCol -= lastX % this.columnWidth ? 0 : 1;
33255         lastCol = Math.min( this.cols - 1, lastCol );
33256         
33257         // set colYs to bottom of the stamp
33258         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
33259             stampSize.height;
33260             
33261         for ( var i = firstCol; i <= lastCol; i++ ) {
33262           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
33263         }
33264     },
33265     */
33266     
33267     _getContainerSize : function()
33268     {
33269         this.maxY = Math.max.apply( Math, this.colYs );
33270         var size = {
33271             height: this.maxY
33272         };
33273       
33274         if ( this.isFitWidth ) {
33275             size.width = this._getContainerFitWidth();
33276         }
33277       
33278         return size;
33279     },
33280     
33281     _getContainerFitWidth : function()
33282     {
33283         var unusedCols = 0;
33284         // count unused columns
33285         var i = this.cols;
33286         while ( --i ) {
33287           if ( this.colYs[i] !== 0 ) {
33288             break;
33289           }
33290           unusedCols++;
33291         }
33292         // fit container to columns that have been used
33293         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
33294     },
33295     
33296     needsResizeLayout : function()
33297     {
33298         var previousWidth = this.containerWidth;
33299         this.getContainerWidth();
33300         return previousWidth !== this.containerWidth;
33301     }
33302  
33303 });
33304
33305  
33306
33307  /*
33308  * - LGPL
33309  *
33310  * element
33311  * 
33312  */
33313
33314 /**
33315  * @class Roo.bootstrap.MasonryBrick
33316  * @extends Roo.bootstrap.Component
33317  * Bootstrap MasonryBrick class
33318  * 
33319  * @constructor
33320  * Create a new MasonryBrick
33321  * @param {Object} config The config object
33322  */
33323
33324 Roo.bootstrap.MasonryBrick = function(config){
33325     
33326     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
33327     
33328     Roo.bootstrap.MasonryBrick.register(this);
33329     
33330     this.addEvents({
33331         // raw events
33332         /**
33333          * @event click
33334          * When a MasonryBrick is clcik
33335          * @param {Roo.bootstrap.MasonryBrick} this
33336          * @param {Roo.EventObject} e
33337          */
33338         "click" : true
33339     });
33340 };
33341
33342 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
33343     
33344     /**
33345      * @cfg {String} title
33346      */   
33347     title : '',
33348     /**
33349      * @cfg {String} html
33350      */   
33351     html : '',
33352     /**
33353      * @cfg {String} bgimage
33354      */   
33355     bgimage : '',
33356     /**
33357      * @cfg {String} videourl
33358      */   
33359     videourl : '',
33360     /**
33361      * @cfg {String} cls
33362      */   
33363     cls : '',
33364     /**
33365      * @cfg {String} href
33366      */   
33367     href : '',
33368     /**
33369      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33370      */   
33371     size : 'xs',
33372     
33373     /**
33374      * @cfg {String} placetitle (center|bottom)
33375      */   
33376     placetitle : '',
33377     
33378     /**
33379      * @cfg {Boolean} isFitContainer defalut true
33380      */   
33381     isFitContainer : true, 
33382     
33383     /**
33384      * @cfg {Boolean} preventDefault defalut false
33385      */   
33386     preventDefault : false, 
33387     
33388     /**
33389      * @cfg {Boolean} inverse defalut false
33390      */   
33391     maskInverse : false, 
33392     
33393     getAutoCreate : function()
33394     {
33395         if(!this.isFitContainer){
33396             return this.getSplitAutoCreate();
33397         }
33398         
33399         var cls = 'masonry-brick masonry-brick-full';
33400         
33401         if(this.href.length){
33402             cls += ' masonry-brick-link';
33403         }
33404         
33405         if(this.bgimage.length){
33406             cls += ' masonry-brick-image';
33407         }
33408         
33409         if(this.maskInverse){
33410             cls += ' mask-inverse';
33411         }
33412         
33413         if(!this.html.length && !this.maskInverse && !this.videourl.length){
33414             cls += ' enable-mask';
33415         }
33416         
33417         if(this.size){
33418             cls += ' masonry-' + this.size + '-brick';
33419         }
33420         
33421         if(this.placetitle.length){
33422             
33423             switch (this.placetitle) {
33424                 case 'center' :
33425                     cls += ' masonry-center-title';
33426                     break;
33427                 case 'bottom' :
33428                     cls += ' masonry-bottom-title';
33429                     break;
33430                 default:
33431                     break;
33432             }
33433             
33434         } else {
33435             if(!this.html.length && !this.bgimage.length){
33436                 cls += ' masonry-center-title';
33437             }
33438
33439             if(!this.html.length && this.bgimage.length){
33440                 cls += ' masonry-bottom-title';
33441             }
33442         }
33443         
33444         if(this.cls){
33445             cls += ' ' + this.cls;
33446         }
33447         
33448         var cfg = {
33449             tag: (this.href.length) ? 'a' : 'div',
33450             cls: cls,
33451             cn: [
33452                 {
33453                     tag: 'div',
33454                     cls: 'masonry-brick-mask'
33455                 },
33456                 {
33457                     tag: 'div',
33458                     cls: 'masonry-brick-paragraph',
33459                     cn: []
33460                 }
33461             ]
33462         };
33463         
33464         if(this.href.length){
33465             cfg.href = this.href;
33466         }
33467         
33468         var cn = cfg.cn[1].cn;
33469         
33470         if(this.title.length){
33471             cn.push({
33472                 tag: 'h4',
33473                 cls: 'masonry-brick-title',
33474                 html: this.title
33475             });
33476         }
33477         
33478         if(this.html.length){
33479             cn.push({
33480                 tag: 'p',
33481                 cls: 'masonry-brick-text',
33482                 html: this.html
33483             });
33484         }
33485         
33486         if (!this.title.length && !this.html.length) {
33487             cfg.cn[1].cls += ' hide';
33488         }
33489         
33490         if(this.bgimage.length){
33491             cfg.cn.push({
33492                 tag: 'img',
33493                 cls: 'masonry-brick-image-view',
33494                 src: this.bgimage
33495             });
33496         }
33497         
33498         if(this.videourl.length){
33499             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33500             // youtube support only?
33501             cfg.cn.push({
33502                 tag: 'iframe',
33503                 cls: 'masonry-brick-image-view',
33504                 src: vurl,
33505                 frameborder : 0,
33506                 allowfullscreen : true
33507             });
33508         }
33509         
33510         return cfg;
33511         
33512     },
33513     
33514     getSplitAutoCreate : function()
33515     {
33516         var cls = 'masonry-brick masonry-brick-split';
33517         
33518         if(this.href.length){
33519             cls += ' masonry-brick-link';
33520         }
33521         
33522         if(this.bgimage.length){
33523             cls += ' masonry-brick-image';
33524         }
33525         
33526         if(this.size){
33527             cls += ' masonry-' + this.size + '-brick';
33528         }
33529         
33530         switch (this.placetitle) {
33531             case 'center' :
33532                 cls += ' masonry-center-title';
33533                 break;
33534             case 'bottom' :
33535                 cls += ' masonry-bottom-title';
33536                 break;
33537             default:
33538                 if(!this.bgimage.length){
33539                     cls += ' masonry-center-title';
33540                 }
33541
33542                 if(this.bgimage.length){
33543                     cls += ' masonry-bottom-title';
33544                 }
33545                 break;
33546         }
33547         
33548         if(this.cls){
33549             cls += ' ' + this.cls;
33550         }
33551         
33552         var cfg = {
33553             tag: (this.href.length) ? 'a' : 'div',
33554             cls: cls,
33555             cn: [
33556                 {
33557                     tag: 'div',
33558                     cls: 'masonry-brick-split-head',
33559                     cn: [
33560                         {
33561                             tag: 'div',
33562                             cls: 'masonry-brick-paragraph',
33563                             cn: []
33564                         }
33565                     ]
33566                 },
33567                 {
33568                     tag: 'div',
33569                     cls: 'masonry-brick-split-body',
33570                     cn: []
33571                 }
33572             ]
33573         };
33574         
33575         if(this.href.length){
33576             cfg.href = this.href;
33577         }
33578         
33579         if(this.title.length){
33580             cfg.cn[0].cn[0].cn.push({
33581                 tag: 'h4',
33582                 cls: 'masonry-brick-title',
33583                 html: this.title
33584             });
33585         }
33586         
33587         if(this.html.length){
33588             cfg.cn[1].cn.push({
33589                 tag: 'p',
33590                 cls: 'masonry-brick-text',
33591                 html: this.html
33592             });
33593         }
33594
33595         if(this.bgimage.length){
33596             cfg.cn[0].cn.push({
33597                 tag: 'img',
33598                 cls: 'masonry-brick-image-view',
33599                 src: this.bgimage
33600             });
33601         }
33602         
33603         if(this.videourl.length){
33604             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33605             // youtube support only?
33606             cfg.cn[0].cn.cn.push({
33607                 tag: 'iframe',
33608                 cls: 'masonry-brick-image-view',
33609                 src: vurl,
33610                 frameborder : 0,
33611                 allowfullscreen : true
33612             });
33613         }
33614         
33615         return cfg;
33616     },
33617     
33618     initEvents: function() 
33619     {
33620         switch (this.size) {
33621             case 'xs' :
33622                 this.x = 1;
33623                 this.y = 1;
33624                 break;
33625             case 'sm' :
33626                 this.x = 2;
33627                 this.y = 2;
33628                 break;
33629             case 'md' :
33630             case 'md-left' :
33631             case 'md-right' :
33632                 this.x = 3;
33633                 this.y = 3;
33634                 break;
33635             case 'tall' :
33636                 this.x = 2;
33637                 this.y = 3;
33638                 break;
33639             case 'wide' :
33640                 this.x = 3;
33641                 this.y = 2;
33642                 break;
33643             case 'wide-thin' :
33644                 this.x = 3;
33645                 this.y = 1;
33646                 break;
33647                         
33648             default :
33649                 break;
33650         }
33651         
33652         if(Roo.isTouch){
33653             this.el.on('touchstart', this.onTouchStart, this);
33654             this.el.on('touchmove', this.onTouchMove, this);
33655             this.el.on('touchend', this.onTouchEnd, this);
33656             this.el.on('contextmenu', this.onContextMenu, this);
33657         } else {
33658             this.el.on('mouseenter'  ,this.enter, this);
33659             this.el.on('mouseleave', this.leave, this);
33660             this.el.on('click', this.onClick, this);
33661         }
33662         
33663         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33664             this.parent().bricks.push(this);   
33665         }
33666         
33667     },
33668     
33669     onClick: function(e, el)
33670     {
33671         var time = this.endTimer - this.startTimer;
33672         // Roo.log(e.preventDefault());
33673         if(Roo.isTouch){
33674             if(time > 1000){
33675                 e.preventDefault();
33676                 return;
33677             }
33678         }
33679         
33680         if(!this.preventDefault){
33681             return;
33682         }
33683         
33684         e.preventDefault();
33685         
33686         if (this.activeClass != '') {
33687             this.selectBrick();
33688         }
33689         
33690         this.fireEvent('click', this, e);
33691     },
33692     
33693     enter: function(e, el)
33694     {
33695         e.preventDefault();
33696         
33697         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33698             return;
33699         }
33700         
33701         if(this.bgimage.length && this.html.length){
33702             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33703         }
33704     },
33705     
33706     leave: function(e, el)
33707     {
33708         e.preventDefault();
33709         
33710         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33711             return;
33712         }
33713         
33714         if(this.bgimage.length && this.html.length){
33715             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33716         }
33717     },
33718     
33719     onTouchStart: function(e, el)
33720     {
33721 //        e.preventDefault();
33722         
33723         this.touchmoved = false;
33724         
33725         if(!this.isFitContainer){
33726             return;
33727         }
33728         
33729         if(!this.bgimage.length || !this.html.length){
33730             return;
33731         }
33732         
33733         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33734         
33735         this.timer = new Date().getTime();
33736         
33737     },
33738     
33739     onTouchMove: function(e, el)
33740     {
33741         this.touchmoved = true;
33742     },
33743     
33744     onContextMenu : function(e,el)
33745     {
33746         e.preventDefault();
33747         e.stopPropagation();
33748         return false;
33749     },
33750     
33751     onTouchEnd: function(e, el)
33752     {
33753 //        e.preventDefault();
33754         
33755         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33756         
33757             this.leave(e,el);
33758             
33759             return;
33760         }
33761         
33762         if(!this.bgimage.length || !this.html.length){
33763             
33764             if(this.href.length){
33765                 window.location.href = this.href;
33766             }
33767             
33768             return;
33769         }
33770         
33771         if(!this.isFitContainer){
33772             return;
33773         }
33774         
33775         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33776         
33777         window.location.href = this.href;
33778     },
33779     
33780     //selection on single brick only
33781     selectBrick : function() {
33782         
33783         if (!this.parentId) {
33784             return;
33785         }
33786         
33787         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33788         var index = m.selectedBrick.indexOf(this.id);
33789         
33790         if ( index > -1) {
33791             m.selectedBrick.splice(index,1);
33792             this.el.removeClass(this.activeClass);
33793             return;
33794         }
33795         
33796         for(var i = 0; i < m.selectedBrick.length; i++) {
33797             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33798             b.el.removeClass(b.activeClass);
33799         }
33800         
33801         m.selectedBrick = [];
33802         
33803         m.selectedBrick.push(this.id);
33804         this.el.addClass(this.activeClass);
33805         return;
33806     },
33807     
33808     isSelected : function(){
33809         return this.el.hasClass(this.activeClass);
33810         
33811     }
33812 });
33813
33814 Roo.apply(Roo.bootstrap.MasonryBrick, {
33815     
33816     //groups: {},
33817     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33818      /**
33819     * register a Masonry Brick
33820     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33821     */
33822     
33823     register : function(brick)
33824     {
33825         //this.groups[brick.id] = brick;
33826         this.groups.add(brick.id, brick);
33827     },
33828     /**
33829     * fetch a  masonry brick based on the masonry brick ID
33830     * @param {string} the masonry brick to add
33831     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33832     */
33833     
33834     get: function(brick_id) 
33835     {
33836         // if (typeof(this.groups[brick_id]) == 'undefined') {
33837         //     return false;
33838         // }
33839         // return this.groups[brick_id] ;
33840         
33841         if(this.groups.key(brick_id)) {
33842             return this.groups.key(brick_id);
33843         }
33844         
33845         return false;
33846     }
33847     
33848     
33849     
33850 });
33851
33852  /*
33853  * - LGPL
33854  *
33855  * element
33856  * 
33857  */
33858
33859 /**
33860  * @class Roo.bootstrap.Brick
33861  * @extends Roo.bootstrap.Component
33862  * Bootstrap Brick class
33863  * 
33864  * @constructor
33865  * Create a new Brick
33866  * @param {Object} config The config object
33867  */
33868
33869 Roo.bootstrap.Brick = function(config){
33870     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33871     
33872     this.addEvents({
33873         // raw events
33874         /**
33875          * @event click
33876          * When a Brick is click
33877          * @param {Roo.bootstrap.Brick} this
33878          * @param {Roo.EventObject} e
33879          */
33880         "click" : true
33881     });
33882 };
33883
33884 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33885     
33886     /**
33887      * @cfg {String} title
33888      */   
33889     title : '',
33890     /**
33891      * @cfg {String} html
33892      */   
33893     html : '',
33894     /**
33895      * @cfg {String} bgimage
33896      */   
33897     bgimage : '',
33898     /**
33899      * @cfg {String} cls
33900      */   
33901     cls : '',
33902     /**
33903      * @cfg {String} href
33904      */   
33905     href : '',
33906     /**
33907      * @cfg {String} video
33908      */   
33909     video : '',
33910     /**
33911      * @cfg {Boolean} square
33912      */   
33913     square : true,
33914     
33915     getAutoCreate : function()
33916     {
33917         var cls = 'roo-brick';
33918         
33919         if(this.href.length){
33920             cls += ' roo-brick-link';
33921         }
33922         
33923         if(this.bgimage.length){
33924             cls += ' roo-brick-image';
33925         }
33926         
33927         if(!this.html.length && !this.bgimage.length){
33928             cls += ' roo-brick-center-title';
33929         }
33930         
33931         if(!this.html.length && this.bgimage.length){
33932             cls += ' roo-brick-bottom-title';
33933         }
33934         
33935         if(this.cls){
33936             cls += ' ' + this.cls;
33937         }
33938         
33939         var cfg = {
33940             tag: (this.href.length) ? 'a' : 'div',
33941             cls: cls,
33942             cn: [
33943                 {
33944                     tag: 'div',
33945                     cls: 'roo-brick-paragraph',
33946                     cn: []
33947                 }
33948             ]
33949         };
33950         
33951         if(this.href.length){
33952             cfg.href = this.href;
33953         }
33954         
33955         var cn = cfg.cn[0].cn;
33956         
33957         if(this.title.length){
33958             cn.push({
33959                 tag: 'h4',
33960                 cls: 'roo-brick-title',
33961                 html: this.title
33962             });
33963         }
33964         
33965         if(this.html.length){
33966             cn.push({
33967                 tag: 'p',
33968                 cls: 'roo-brick-text',
33969                 html: this.html
33970             });
33971         } else {
33972             cn.cls += ' hide';
33973         }
33974         
33975         if(this.bgimage.length){
33976             cfg.cn.push({
33977                 tag: 'img',
33978                 cls: 'roo-brick-image-view',
33979                 src: this.bgimage
33980             });
33981         }
33982         
33983         return cfg;
33984     },
33985     
33986     initEvents: function() 
33987     {
33988         if(this.title.length || this.html.length){
33989             this.el.on('mouseenter'  ,this.enter, this);
33990             this.el.on('mouseleave', this.leave, this);
33991         }
33992         
33993         Roo.EventManager.onWindowResize(this.resize, this); 
33994         
33995         if(this.bgimage.length){
33996             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33997             this.imageEl.on('load', this.onImageLoad, this);
33998             return;
33999         }
34000         
34001         this.resize();
34002     },
34003     
34004     onImageLoad : function()
34005     {
34006         this.resize();
34007     },
34008     
34009     resize : function()
34010     {
34011         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
34012         
34013         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
34014         
34015         if(this.bgimage.length){
34016             var image = this.el.select('.roo-brick-image-view', true).first();
34017             
34018             image.setWidth(paragraph.getWidth());
34019             
34020             if(this.square){
34021                 image.setHeight(paragraph.getWidth());
34022             }
34023             
34024             this.el.setHeight(image.getHeight());
34025             paragraph.setHeight(image.getHeight());
34026             
34027         }
34028         
34029     },
34030     
34031     enter: function(e, el)
34032     {
34033         e.preventDefault();
34034         
34035         if(this.bgimage.length){
34036             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
34037             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
34038         }
34039     },
34040     
34041     leave: function(e, el)
34042     {
34043         e.preventDefault();
34044         
34045         if(this.bgimage.length){
34046             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
34047             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
34048         }
34049     }
34050     
34051 });
34052
34053  
34054
34055  /*
34056  * - LGPL
34057  *
34058  * Number field 
34059  */
34060
34061 /**
34062  * @class Roo.bootstrap.NumberField
34063  * @extends Roo.bootstrap.Input
34064  * Bootstrap NumberField class
34065  * 
34066  * 
34067  * 
34068  * 
34069  * @constructor
34070  * Create a new NumberField
34071  * @param {Object} config The config object
34072  */
34073
34074 Roo.bootstrap.NumberField = function(config){
34075     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
34076 };
34077
34078 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
34079     
34080     /**
34081      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
34082      */
34083     allowDecimals : true,
34084     /**
34085      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
34086      */
34087     decimalSeparator : ".",
34088     /**
34089      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
34090      */
34091     decimalPrecision : 2,
34092     /**
34093      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
34094      */
34095     allowNegative : true,
34096     
34097     /**
34098      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
34099      */
34100     allowZero: true,
34101     /**
34102      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
34103      */
34104     minValue : Number.NEGATIVE_INFINITY,
34105     /**
34106      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
34107      */
34108     maxValue : Number.MAX_VALUE,
34109     /**
34110      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
34111      */
34112     minText : "The minimum value for this field is {0}",
34113     /**
34114      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
34115      */
34116     maxText : "The maximum value for this field is {0}",
34117     /**
34118      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
34119      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
34120      */
34121     nanText : "{0} is not a valid number",
34122     /**
34123      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
34124      */
34125     thousandsDelimiter : false,
34126     /**
34127      * @cfg {String} valueAlign alignment of value
34128      */
34129     valueAlign : "left",
34130
34131     getAutoCreate : function()
34132     {
34133         var hiddenInput = {
34134             tag: 'input',
34135             type: 'hidden',
34136             id: Roo.id(),
34137             cls: 'hidden-number-input'
34138         };
34139         
34140         if (this.name) {
34141             hiddenInput.name = this.name;
34142         }
34143         
34144         this.name = '';
34145         
34146         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
34147         
34148         this.name = hiddenInput.name;
34149         
34150         if(cfg.cn.length > 0) {
34151             cfg.cn.push(hiddenInput);
34152         }
34153         
34154         return cfg;
34155     },
34156
34157     // private
34158     initEvents : function()
34159     {   
34160         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
34161         
34162         var allowed = "0123456789";
34163         
34164         if(this.allowDecimals){
34165             allowed += this.decimalSeparator;
34166         }
34167         
34168         if(this.allowNegative){
34169             allowed += "-";
34170         }
34171         
34172         if(this.thousandsDelimiter) {
34173             allowed += ",";
34174         }
34175         
34176         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
34177         
34178         var keyPress = function(e){
34179             
34180             var k = e.getKey();
34181             
34182             var c = e.getCharCode();
34183             
34184             if(
34185                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
34186                     allowed.indexOf(String.fromCharCode(c)) === -1
34187             ){
34188                 e.stopEvent();
34189                 return;
34190             }
34191             
34192             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
34193                 return;
34194             }
34195             
34196             if(allowed.indexOf(String.fromCharCode(c)) === -1){
34197                 e.stopEvent();
34198             }
34199         };
34200         
34201         this.el.on("keypress", keyPress, this);
34202     },
34203     
34204     validateValue : function(value)
34205     {
34206         
34207         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
34208             return false;
34209         }
34210         
34211         var num = this.parseValue(value);
34212         
34213         if(isNaN(num)){
34214             this.markInvalid(String.format(this.nanText, value));
34215             return false;
34216         }
34217         
34218         if(num < this.minValue){
34219             this.markInvalid(String.format(this.minText, this.minValue));
34220             return false;
34221         }
34222         
34223         if(num > this.maxValue){
34224             this.markInvalid(String.format(this.maxText, this.maxValue));
34225             return false;
34226         }
34227         
34228         return true;
34229     },
34230
34231     getValue : function()
34232     {
34233         var v = this.hiddenEl().getValue();
34234         
34235         return this.fixPrecision(this.parseValue(v));
34236     },
34237
34238     parseValue : function(value)
34239     {
34240         if(this.thousandsDelimiter) {
34241             value += "";
34242             r = new RegExp(",", "g");
34243             value = value.replace(r, "");
34244         }
34245         
34246         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
34247         return isNaN(value) ? '' : value;
34248     },
34249
34250     fixPrecision : function(value)
34251     {
34252         if(this.thousandsDelimiter) {
34253             value += "";
34254             r = new RegExp(",", "g");
34255             value = value.replace(r, "");
34256         }
34257         
34258         var nan = isNaN(value);
34259         
34260         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
34261             return nan ? '' : value;
34262         }
34263         return parseFloat(value).toFixed(this.decimalPrecision);
34264     },
34265
34266     setValue : function(v)
34267     {
34268         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
34269         
34270         this.value = v;
34271         
34272         if(this.rendered){
34273             
34274             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
34275             
34276             this.inputEl().dom.value = (v == '') ? '' :
34277                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
34278             
34279             if(!this.allowZero && v === '0') {
34280                 this.hiddenEl().dom.value = '';
34281                 this.inputEl().dom.value = '';
34282             }
34283             
34284             this.validate();
34285         }
34286     },
34287
34288     decimalPrecisionFcn : function(v)
34289     {
34290         return Math.floor(v);
34291     },
34292
34293     beforeBlur : function()
34294     {
34295         var v = this.parseValue(this.getRawValue());
34296         
34297         if(v || v === 0 || v === ''){
34298             this.setValue(v);
34299         }
34300     },
34301     
34302     hiddenEl : function()
34303     {
34304         return this.el.select('input.hidden-number-input',true).first();
34305     }
34306     
34307 });
34308
34309  
34310
34311 /*
34312 * Licence: LGPL
34313 */
34314
34315 /**
34316  * @class Roo.bootstrap.DocumentSlider
34317  * @extends Roo.bootstrap.Component
34318  * Bootstrap DocumentSlider class
34319  * 
34320  * @constructor
34321  * Create a new DocumentViewer
34322  * @param {Object} config The config object
34323  */
34324
34325 Roo.bootstrap.DocumentSlider = function(config){
34326     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
34327     
34328     this.files = [];
34329     
34330     this.addEvents({
34331         /**
34332          * @event initial
34333          * Fire after initEvent
34334          * @param {Roo.bootstrap.DocumentSlider} this
34335          */
34336         "initial" : true,
34337         /**
34338          * @event update
34339          * Fire after update
34340          * @param {Roo.bootstrap.DocumentSlider} this
34341          */
34342         "update" : true,
34343         /**
34344          * @event click
34345          * Fire after click
34346          * @param {Roo.bootstrap.DocumentSlider} this
34347          */
34348         "click" : true
34349     });
34350 };
34351
34352 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
34353     
34354     files : false,
34355     
34356     indicator : 0,
34357     
34358     getAutoCreate : function()
34359     {
34360         var cfg = {
34361             tag : 'div',
34362             cls : 'roo-document-slider',
34363             cn : [
34364                 {
34365                     tag : 'div',
34366                     cls : 'roo-document-slider-header',
34367                     cn : [
34368                         {
34369                             tag : 'div',
34370                             cls : 'roo-document-slider-header-title'
34371                         }
34372                     ]
34373                 },
34374                 {
34375                     tag : 'div',
34376                     cls : 'roo-document-slider-body',
34377                     cn : [
34378                         {
34379                             tag : 'div',
34380                             cls : 'roo-document-slider-prev',
34381                             cn : [
34382                                 {
34383                                     tag : 'i',
34384                                     cls : 'fa fa-chevron-left'
34385                                 }
34386                             ]
34387                         },
34388                         {
34389                             tag : 'div',
34390                             cls : 'roo-document-slider-thumb',
34391                             cn : [
34392                                 {
34393                                     tag : 'img',
34394                                     cls : 'roo-document-slider-image'
34395                                 }
34396                             ]
34397                         },
34398                         {
34399                             tag : 'div',
34400                             cls : 'roo-document-slider-next',
34401                             cn : [
34402                                 {
34403                                     tag : 'i',
34404                                     cls : 'fa fa-chevron-right'
34405                                 }
34406                             ]
34407                         }
34408                     ]
34409                 }
34410             ]
34411         };
34412         
34413         return cfg;
34414     },
34415     
34416     initEvents : function()
34417     {
34418         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34419         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34420         
34421         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34422         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34423         
34424         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34425         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34426         
34427         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34428         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34429         
34430         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34431         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34432         
34433         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34434         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34435         
34436         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34437         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34438         
34439         this.thumbEl.on('click', this.onClick, this);
34440         
34441         this.prevIndicator.on('click', this.prev, this);
34442         
34443         this.nextIndicator.on('click', this.next, this);
34444         
34445     },
34446     
34447     initial : function()
34448     {
34449         if(this.files.length){
34450             this.indicator = 1;
34451             this.update()
34452         }
34453         
34454         this.fireEvent('initial', this);
34455     },
34456     
34457     update : function()
34458     {
34459         this.imageEl.attr('src', this.files[this.indicator - 1]);
34460         
34461         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34462         
34463         this.prevIndicator.show();
34464         
34465         if(this.indicator == 1){
34466             this.prevIndicator.hide();
34467         }
34468         
34469         this.nextIndicator.show();
34470         
34471         if(this.indicator == this.files.length){
34472             this.nextIndicator.hide();
34473         }
34474         
34475         this.thumbEl.scrollTo('top');
34476         
34477         this.fireEvent('update', this);
34478     },
34479     
34480     onClick : function(e)
34481     {
34482         e.preventDefault();
34483         
34484         this.fireEvent('click', this);
34485     },
34486     
34487     prev : function(e)
34488     {
34489         e.preventDefault();
34490         
34491         this.indicator = Math.max(1, this.indicator - 1);
34492         
34493         this.update();
34494     },
34495     
34496     next : function(e)
34497     {
34498         e.preventDefault();
34499         
34500         this.indicator = Math.min(this.files.length, this.indicator + 1);
34501         
34502         this.update();
34503     }
34504 });
34505 /*
34506  * - LGPL
34507  *
34508  * RadioSet
34509  *
34510  *
34511  */
34512
34513 /**
34514  * @class Roo.bootstrap.RadioSet
34515  * @extends Roo.bootstrap.Input
34516  * Bootstrap RadioSet class
34517  * @cfg {String} indicatorpos (left|right) default left
34518  * @cfg {Boolean} inline (true|false) inline the element (default true)
34519  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34520  * @constructor
34521  * Create a new RadioSet
34522  * @param {Object} config The config object
34523  */
34524
34525 Roo.bootstrap.RadioSet = function(config){
34526     
34527     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34528     
34529     this.radioes = [];
34530     
34531     Roo.bootstrap.RadioSet.register(this);
34532     
34533     this.addEvents({
34534         /**
34535         * @event check
34536         * Fires when the element is checked or unchecked.
34537         * @param {Roo.bootstrap.RadioSet} this This radio
34538         * @param {Roo.bootstrap.Radio} item The checked item
34539         */
34540        check : true,
34541        /**
34542         * @event click
34543         * Fires when the element is click.
34544         * @param {Roo.bootstrap.RadioSet} this This radio set
34545         * @param {Roo.bootstrap.Radio} item The checked item
34546         * @param {Roo.EventObject} e The event object
34547         */
34548        click : true
34549     });
34550     
34551 };
34552
34553 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34554
34555     radioes : false,
34556     
34557     inline : true,
34558     
34559     weight : '',
34560     
34561     indicatorpos : 'left',
34562     
34563     getAutoCreate : function()
34564     {
34565         var label = {
34566             tag : 'label',
34567             cls : 'roo-radio-set-label',
34568             cn : [
34569                 {
34570                     tag : 'span',
34571                     html : this.fieldLabel
34572                 }
34573             ]
34574         };
34575         if (Roo.bootstrap.version == 3) {
34576             
34577             
34578             if(this.indicatorpos == 'left'){
34579                 label.cn.unshift({
34580                     tag : 'i',
34581                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34582                     tooltip : 'This field is required'
34583                 });
34584             } else {
34585                 label.cn.push({
34586                     tag : 'i',
34587                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34588                     tooltip : 'This field is required'
34589                 });
34590             }
34591         }
34592         var items = {
34593             tag : 'div',
34594             cls : 'roo-radio-set-items'
34595         };
34596         
34597         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34598         
34599         if (align === 'left' && this.fieldLabel.length) {
34600             
34601             items = {
34602                 cls : "roo-radio-set-right", 
34603                 cn: [
34604                     items
34605                 ]
34606             };
34607             
34608             if(this.labelWidth > 12){
34609                 label.style = "width: " + this.labelWidth + 'px';
34610             }
34611             
34612             if(this.labelWidth < 13 && this.labelmd == 0){
34613                 this.labelmd = this.labelWidth;
34614             }
34615             
34616             if(this.labellg > 0){
34617                 label.cls += ' col-lg-' + this.labellg;
34618                 items.cls += ' col-lg-' + (12 - this.labellg);
34619             }
34620             
34621             if(this.labelmd > 0){
34622                 label.cls += ' col-md-' + this.labelmd;
34623                 items.cls += ' col-md-' + (12 - this.labelmd);
34624             }
34625             
34626             if(this.labelsm > 0){
34627                 label.cls += ' col-sm-' + this.labelsm;
34628                 items.cls += ' col-sm-' + (12 - this.labelsm);
34629             }
34630             
34631             if(this.labelxs > 0){
34632                 label.cls += ' col-xs-' + this.labelxs;
34633                 items.cls += ' col-xs-' + (12 - this.labelxs);
34634             }
34635         }
34636         
34637         var cfg = {
34638             tag : 'div',
34639             cls : 'roo-radio-set',
34640             cn : [
34641                 {
34642                     tag : 'input',
34643                     cls : 'roo-radio-set-input',
34644                     type : 'hidden',
34645                     name : this.name,
34646                     value : this.value ? this.value :  ''
34647                 },
34648                 label,
34649                 items
34650             ]
34651         };
34652         
34653         if(this.weight.length){
34654             cfg.cls += ' roo-radio-' + this.weight;
34655         }
34656         
34657         if(this.inline) {
34658             cfg.cls += ' roo-radio-set-inline';
34659         }
34660         
34661         var settings=this;
34662         ['xs','sm','md','lg'].map(function(size){
34663             if (settings[size]) {
34664                 cfg.cls += ' col-' + size + '-' + settings[size];
34665             }
34666         });
34667         
34668         return cfg;
34669         
34670     },
34671
34672     initEvents : function()
34673     {
34674         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34675         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34676         
34677         if(!this.fieldLabel.length){
34678             this.labelEl.hide();
34679         }
34680         
34681         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34682         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34683         
34684         this.indicator = this.indicatorEl();
34685         
34686         if(this.indicator){
34687             this.indicator.addClass('invisible');
34688         }
34689         
34690         this.originalValue = this.getValue();
34691         
34692     },
34693     
34694     inputEl: function ()
34695     {
34696         return this.el.select('.roo-radio-set-input', true).first();
34697     },
34698     
34699     getChildContainer : function()
34700     {
34701         return this.itemsEl;
34702     },
34703     
34704     register : function(item)
34705     {
34706         this.radioes.push(item);
34707         
34708     },
34709     
34710     validate : function()
34711     {   
34712         if(this.getVisibilityEl().hasClass('hidden')){
34713             return true;
34714         }
34715         
34716         var valid = false;
34717         
34718         Roo.each(this.radioes, function(i){
34719             if(!i.checked){
34720                 return;
34721             }
34722             
34723             valid = true;
34724             return false;
34725         });
34726         
34727         if(this.allowBlank) {
34728             return true;
34729         }
34730         
34731         if(this.disabled || valid){
34732             this.markValid();
34733             return true;
34734         }
34735         
34736         this.markInvalid();
34737         return false;
34738         
34739     },
34740     
34741     markValid : function()
34742     {
34743         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34744             this.indicatorEl().removeClass('visible');
34745             this.indicatorEl().addClass('invisible');
34746         }
34747         
34748         
34749         if (Roo.bootstrap.version == 3) {
34750             this.el.removeClass([this.invalidClass, this.validClass]);
34751             this.el.addClass(this.validClass);
34752         } else {
34753             this.el.removeClass(['is-invalid','is-valid']);
34754             this.el.addClass(['is-valid']);
34755         }
34756         this.fireEvent('valid', this);
34757     },
34758     
34759     markInvalid : function(msg)
34760     {
34761         if(this.allowBlank || this.disabled){
34762             return;
34763         }
34764         
34765         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34766             this.indicatorEl().removeClass('invisible');
34767             this.indicatorEl().addClass('visible');
34768         }
34769         if (Roo.bootstrap.version == 3) {
34770             this.el.removeClass([this.invalidClass, this.validClass]);
34771             this.el.addClass(this.invalidClass);
34772         } else {
34773             this.el.removeClass(['is-invalid','is-valid']);
34774             this.el.addClass(['is-invalid']);
34775         }
34776         
34777         this.fireEvent('invalid', this, msg);
34778         
34779     },
34780     
34781     setValue : function(v, suppressEvent)
34782     {   
34783         if(this.value === v){
34784             return;
34785         }
34786         
34787         this.value = v;
34788         
34789         if(this.rendered){
34790             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34791         }
34792         
34793         Roo.each(this.radioes, function(i){
34794             i.checked = false;
34795             i.el.removeClass('checked');
34796         });
34797         
34798         Roo.each(this.radioes, function(i){
34799             
34800             if(i.value === v || i.value.toString() === v.toString()){
34801                 i.checked = true;
34802                 i.el.addClass('checked');
34803                 
34804                 if(suppressEvent !== true){
34805                     this.fireEvent('check', this, i);
34806                 }
34807                 
34808                 return false;
34809             }
34810             
34811         }, this);
34812         
34813         this.validate();
34814     },
34815     
34816     clearInvalid : function(){
34817         
34818         if(!this.el || this.preventMark){
34819             return;
34820         }
34821         
34822         this.el.removeClass([this.invalidClass]);
34823         
34824         this.fireEvent('valid', this);
34825     }
34826     
34827 });
34828
34829 Roo.apply(Roo.bootstrap.RadioSet, {
34830     
34831     groups: {},
34832     
34833     register : function(set)
34834     {
34835         this.groups[set.name] = set;
34836     },
34837     
34838     get: function(name) 
34839     {
34840         if (typeof(this.groups[name]) == 'undefined') {
34841             return false;
34842         }
34843         
34844         return this.groups[name] ;
34845     }
34846     
34847 });
34848 /*
34849  * Based on:
34850  * Ext JS Library 1.1.1
34851  * Copyright(c) 2006-2007, Ext JS, LLC.
34852  *
34853  * Originally Released Under LGPL - original licence link has changed is not relivant.
34854  *
34855  * Fork - LGPL
34856  * <script type="text/javascript">
34857  */
34858
34859
34860 /**
34861  * @class Roo.bootstrap.SplitBar
34862  * @extends Roo.util.Observable
34863  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34864  * <br><br>
34865  * Usage:
34866  * <pre><code>
34867 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34868                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34869 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34870 split.minSize = 100;
34871 split.maxSize = 600;
34872 split.animate = true;
34873 split.on('moved', splitterMoved);
34874 </code></pre>
34875  * @constructor
34876  * Create a new SplitBar
34877  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34878  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34879  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34880  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34881                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34882                         position of the SplitBar).
34883  */
34884 Roo.bootstrap.SplitBar = function(cfg){
34885     
34886     /** @private */
34887     
34888     //{
34889     //  dragElement : elm
34890     //  resizingElement: el,
34891         // optional..
34892     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34893     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34894         // existingProxy ???
34895     //}
34896     
34897     this.el = Roo.get(cfg.dragElement, true);
34898     this.el.dom.unselectable = "on";
34899     /** @private */
34900     this.resizingEl = Roo.get(cfg.resizingElement, true);
34901
34902     /**
34903      * @private
34904      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34905      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34906      * @type Number
34907      */
34908     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34909     
34910     /**
34911      * The minimum size of the resizing element. (Defaults to 0)
34912      * @type Number
34913      */
34914     this.minSize = 0;
34915     
34916     /**
34917      * The maximum size of the resizing element. (Defaults to 2000)
34918      * @type Number
34919      */
34920     this.maxSize = 2000;
34921     
34922     /**
34923      * Whether to animate the transition to the new size
34924      * @type Boolean
34925      */
34926     this.animate = false;
34927     
34928     /**
34929      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34930      * @type Boolean
34931      */
34932     this.useShim = false;
34933     
34934     /** @private */
34935     this.shim = null;
34936     
34937     if(!cfg.existingProxy){
34938         /** @private */
34939         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34940     }else{
34941         this.proxy = Roo.get(cfg.existingProxy).dom;
34942     }
34943     /** @private */
34944     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34945     
34946     /** @private */
34947     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34948     
34949     /** @private */
34950     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34951     
34952     /** @private */
34953     this.dragSpecs = {};
34954     
34955     /**
34956      * @private The adapter to use to positon and resize elements
34957      */
34958     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34959     this.adapter.init(this);
34960     
34961     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34962         /** @private */
34963         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34964         this.el.addClass("roo-splitbar-h");
34965     }else{
34966         /** @private */
34967         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34968         this.el.addClass("roo-splitbar-v");
34969     }
34970     
34971     this.addEvents({
34972         /**
34973          * @event resize
34974          * Fires when the splitter is moved (alias for {@link #event-moved})
34975          * @param {Roo.bootstrap.SplitBar} this
34976          * @param {Number} newSize the new width or height
34977          */
34978         "resize" : true,
34979         /**
34980          * @event moved
34981          * Fires when the splitter is moved
34982          * @param {Roo.bootstrap.SplitBar} this
34983          * @param {Number} newSize the new width or height
34984          */
34985         "moved" : true,
34986         /**
34987          * @event beforeresize
34988          * Fires before the splitter is dragged
34989          * @param {Roo.bootstrap.SplitBar} this
34990          */
34991         "beforeresize" : true,
34992
34993         "beforeapply" : true
34994     });
34995
34996     Roo.util.Observable.call(this);
34997 };
34998
34999 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
35000     onStartProxyDrag : function(x, y){
35001         this.fireEvent("beforeresize", this);
35002         if(!this.overlay){
35003             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
35004             o.unselectable();
35005             o.enableDisplayMode("block");
35006             // all splitbars share the same overlay
35007             Roo.bootstrap.SplitBar.prototype.overlay = o;
35008         }
35009         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
35010         this.overlay.show();
35011         Roo.get(this.proxy).setDisplayed("block");
35012         var size = this.adapter.getElementSize(this);
35013         this.activeMinSize = this.getMinimumSize();;
35014         this.activeMaxSize = this.getMaximumSize();;
35015         var c1 = size - this.activeMinSize;
35016         var c2 = Math.max(this.activeMaxSize - size, 0);
35017         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35018             this.dd.resetConstraints();
35019             this.dd.setXConstraint(
35020                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
35021                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
35022             );
35023             this.dd.setYConstraint(0, 0);
35024         }else{
35025             this.dd.resetConstraints();
35026             this.dd.setXConstraint(0, 0);
35027             this.dd.setYConstraint(
35028                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
35029                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
35030             );
35031          }
35032         this.dragSpecs.startSize = size;
35033         this.dragSpecs.startPoint = [x, y];
35034         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
35035     },
35036     
35037     /** 
35038      * @private Called after the drag operation by the DDProxy
35039      */
35040     onEndProxyDrag : function(e){
35041         Roo.get(this.proxy).setDisplayed(false);
35042         var endPoint = Roo.lib.Event.getXY(e);
35043         if(this.overlay){
35044             this.overlay.hide();
35045         }
35046         var newSize;
35047         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35048             newSize = this.dragSpecs.startSize + 
35049                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
35050                     endPoint[0] - this.dragSpecs.startPoint[0] :
35051                     this.dragSpecs.startPoint[0] - endPoint[0]
35052                 );
35053         }else{
35054             newSize = this.dragSpecs.startSize + 
35055                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
35056                     endPoint[1] - this.dragSpecs.startPoint[1] :
35057                     this.dragSpecs.startPoint[1] - endPoint[1]
35058                 );
35059         }
35060         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
35061         if(newSize != this.dragSpecs.startSize){
35062             if(this.fireEvent('beforeapply', this, newSize) !== false){
35063                 this.adapter.setElementSize(this, newSize);
35064                 this.fireEvent("moved", this, newSize);
35065                 this.fireEvent("resize", this, newSize);
35066             }
35067         }
35068     },
35069     
35070     /**
35071      * Get the adapter this SplitBar uses
35072      * @return The adapter object
35073      */
35074     getAdapter : function(){
35075         return this.adapter;
35076     },
35077     
35078     /**
35079      * Set the adapter this SplitBar uses
35080      * @param {Object} adapter A SplitBar adapter object
35081      */
35082     setAdapter : function(adapter){
35083         this.adapter = adapter;
35084         this.adapter.init(this);
35085     },
35086     
35087     /**
35088      * Gets the minimum size for the resizing element
35089      * @return {Number} The minimum size
35090      */
35091     getMinimumSize : function(){
35092         return this.minSize;
35093     },
35094     
35095     /**
35096      * Sets the minimum size for the resizing element
35097      * @param {Number} minSize The minimum size
35098      */
35099     setMinimumSize : function(minSize){
35100         this.minSize = minSize;
35101     },
35102     
35103     /**
35104      * Gets the maximum size for the resizing element
35105      * @return {Number} The maximum size
35106      */
35107     getMaximumSize : function(){
35108         return this.maxSize;
35109     },
35110     
35111     /**
35112      * Sets the maximum size for the resizing element
35113      * @param {Number} maxSize The maximum size
35114      */
35115     setMaximumSize : function(maxSize){
35116         this.maxSize = maxSize;
35117     },
35118     
35119     /**
35120      * Sets the initialize size for the resizing element
35121      * @param {Number} size The initial size
35122      */
35123     setCurrentSize : function(size){
35124         var oldAnimate = this.animate;
35125         this.animate = false;
35126         this.adapter.setElementSize(this, size);
35127         this.animate = oldAnimate;
35128     },
35129     
35130     /**
35131      * Destroy this splitbar. 
35132      * @param {Boolean} removeEl True to remove the element
35133      */
35134     destroy : function(removeEl){
35135         if(this.shim){
35136             this.shim.remove();
35137         }
35138         this.dd.unreg();
35139         this.proxy.parentNode.removeChild(this.proxy);
35140         if(removeEl){
35141             this.el.remove();
35142         }
35143     }
35144 });
35145
35146 /**
35147  * @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.
35148  */
35149 Roo.bootstrap.SplitBar.createProxy = function(dir){
35150     var proxy = new Roo.Element(document.createElement("div"));
35151     proxy.unselectable();
35152     var cls = 'roo-splitbar-proxy';
35153     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
35154     document.body.appendChild(proxy.dom);
35155     return proxy.dom;
35156 };
35157
35158 /** 
35159  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
35160  * Default Adapter. It assumes the splitter and resizing element are not positioned
35161  * elements and only gets/sets the width of the element. Generally used for table based layouts.
35162  */
35163 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
35164 };
35165
35166 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
35167     // do nothing for now
35168     init : function(s){
35169     
35170     },
35171     /**
35172      * Called before drag operations to get the current size of the resizing element. 
35173      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35174      */
35175      getElementSize : function(s){
35176         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35177             return s.resizingEl.getWidth();
35178         }else{
35179             return s.resizingEl.getHeight();
35180         }
35181     },
35182     
35183     /**
35184      * Called after drag operations to set the size of the resizing element.
35185      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35186      * @param {Number} newSize The new size to set
35187      * @param {Function} onComplete A function to be invoked when resizing is complete
35188      */
35189     setElementSize : function(s, newSize, onComplete){
35190         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35191             if(!s.animate){
35192                 s.resizingEl.setWidth(newSize);
35193                 if(onComplete){
35194                     onComplete(s, newSize);
35195                 }
35196             }else{
35197                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
35198             }
35199         }else{
35200             
35201             if(!s.animate){
35202                 s.resizingEl.setHeight(newSize);
35203                 if(onComplete){
35204                     onComplete(s, newSize);
35205                 }
35206             }else{
35207                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
35208             }
35209         }
35210     }
35211 };
35212
35213 /** 
35214  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
35215  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
35216  * Adapter that  moves the splitter element to align with the resized sizing element. 
35217  * Used with an absolute positioned SplitBar.
35218  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
35219  * document.body, make sure you assign an id to the body element.
35220  */
35221 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
35222     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35223     this.container = Roo.get(container);
35224 };
35225
35226 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
35227     init : function(s){
35228         this.basic.init(s);
35229     },
35230     
35231     getElementSize : function(s){
35232         return this.basic.getElementSize(s);
35233     },
35234     
35235     setElementSize : function(s, newSize, onComplete){
35236         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
35237     },
35238     
35239     moveSplitter : function(s){
35240         var yes = Roo.bootstrap.SplitBar;
35241         switch(s.placement){
35242             case yes.LEFT:
35243                 s.el.setX(s.resizingEl.getRight());
35244                 break;
35245             case yes.RIGHT:
35246                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
35247                 break;
35248             case yes.TOP:
35249                 s.el.setY(s.resizingEl.getBottom());
35250                 break;
35251             case yes.BOTTOM:
35252                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
35253                 break;
35254         }
35255     }
35256 };
35257
35258 /**
35259  * Orientation constant - Create a vertical SplitBar
35260  * @static
35261  * @type Number
35262  */
35263 Roo.bootstrap.SplitBar.VERTICAL = 1;
35264
35265 /**
35266  * Orientation constant - Create a horizontal SplitBar
35267  * @static
35268  * @type Number
35269  */
35270 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
35271
35272 /**
35273  * Placement constant - The resizing element is to the left of the splitter element
35274  * @static
35275  * @type Number
35276  */
35277 Roo.bootstrap.SplitBar.LEFT = 1;
35278
35279 /**
35280  * Placement constant - The resizing element is to the right of the splitter element
35281  * @static
35282  * @type Number
35283  */
35284 Roo.bootstrap.SplitBar.RIGHT = 2;
35285
35286 /**
35287  * Placement constant - The resizing element is positioned above the splitter element
35288  * @static
35289  * @type Number
35290  */
35291 Roo.bootstrap.SplitBar.TOP = 3;
35292
35293 /**
35294  * Placement constant - The resizing element is positioned under splitter element
35295  * @static
35296  * @type Number
35297  */
35298 Roo.bootstrap.SplitBar.BOTTOM = 4;
35299 Roo.namespace("Roo.bootstrap.layout");/*
35300  * Based on:
35301  * Ext JS Library 1.1.1
35302  * Copyright(c) 2006-2007, Ext JS, LLC.
35303  *
35304  * Originally Released Under LGPL - original licence link has changed is not relivant.
35305  *
35306  * Fork - LGPL
35307  * <script type="text/javascript">
35308  */
35309
35310 /**
35311  * @class Roo.bootstrap.layout.Manager
35312  * @extends Roo.bootstrap.Component
35313  * Base class for layout managers.
35314  */
35315 Roo.bootstrap.layout.Manager = function(config)
35316 {
35317     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
35318
35319
35320
35321
35322
35323     /** false to disable window resize monitoring @type Boolean */
35324     this.monitorWindowResize = true;
35325     this.regions = {};
35326     this.addEvents({
35327         /**
35328          * @event layout
35329          * Fires when a layout is performed.
35330          * @param {Roo.LayoutManager} this
35331          */
35332         "layout" : true,
35333         /**
35334          * @event regionresized
35335          * Fires when the user resizes a region.
35336          * @param {Roo.LayoutRegion} region The resized region
35337          * @param {Number} newSize The new size (width for east/west, height for north/south)
35338          */
35339         "regionresized" : true,
35340         /**
35341          * @event regioncollapsed
35342          * Fires when a region is collapsed.
35343          * @param {Roo.LayoutRegion} region The collapsed region
35344          */
35345         "regioncollapsed" : true,
35346         /**
35347          * @event regionexpanded
35348          * Fires when a region is expanded.
35349          * @param {Roo.LayoutRegion} region The expanded region
35350          */
35351         "regionexpanded" : true
35352     });
35353     this.updating = false;
35354
35355     if (config.el) {
35356         this.el = Roo.get(config.el);
35357         this.initEvents();
35358     }
35359
35360 };
35361
35362 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35363
35364
35365     regions : null,
35366
35367     monitorWindowResize : true,
35368
35369
35370     updating : false,
35371
35372
35373     onRender : function(ct, position)
35374     {
35375         if(!this.el){
35376             this.el = Roo.get(ct);
35377             this.initEvents();
35378         }
35379         //this.fireEvent('render',this);
35380     },
35381
35382
35383     initEvents: function()
35384     {
35385
35386
35387         // ie scrollbar fix
35388         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35389             document.body.scroll = "no";
35390         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35391             this.el.position('relative');
35392         }
35393         this.id = this.el.id;
35394         this.el.addClass("roo-layout-container");
35395         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35396         if(this.el.dom != document.body ) {
35397             this.el.on('resize', this.layout,this);
35398             this.el.on('show', this.layout,this);
35399         }
35400
35401     },
35402
35403     /**
35404      * Returns true if this layout is currently being updated
35405      * @return {Boolean}
35406      */
35407     isUpdating : function(){
35408         return this.updating;
35409     },
35410
35411     /**
35412      * Suspend the LayoutManager from doing auto-layouts while
35413      * making multiple add or remove calls
35414      */
35415     beginUpdate : function(){
35416         this.updating = true;
35417     },
35418
35419     /**
35420      * Restore auto-layouts and optionally disable the manager from performing a layout
35421      * @param {Boolean} noLayout true to disable a layout update
35422      */
35423     endUpdate : function(noLayout){
35424         this.updating = false;
35425         if(!noLayout){
35426             this.layout();
35427         }
35428     },
35429
35430     layout: function(){
35431         // abstract...
35432     },
35433
35434     onRegionResized : function(region, newSize){
35435         this.fireEvent("regionresized", region, newSize);
35436         this.layout();
35437     },
35438
35439     onRegionCollapsed : function(region){
35440         this.fireEvent("regioncollapsed", region);
35441     },
35442
35443     onRegionExpanded : function(region){
35444         this.fireEvent("regionexpanded", region);
35445     },
35446
35447     /**
35448      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35449      * performs box-model adjustments.
35450      * @return {Object} The size as an object {width: (the width), height: (the height)}
35451      */
35452     getViewSize : function()
35453     {
35454         var size;
35455         if(this.el.dom != document.body){
35456             size = this.el.getSize();
35457         }else{
35458             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35459         }
35460         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35461         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35462         return size;
35463     },
35464
35465     /**
35466      * Returns the Element this layout is bound to.
35467      * @return {Roo.Element}
35468      */
35469     getEl : function(){
35470         return this.el;
35471     },
35472
35473     /**
35474      * Returns the specified region.
35475      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35476      * @return {Roo.LayoutRegion}
35477      */
35478     getRegion : function(target){
35479         return this.regions[target.toLowerCase()];
35480     },
35481
35482     onWindowResize : function(){
35483         if(this.monitorWindowResize){
35484             this.layout();
35485         }
35486     }
35487 });
35488 /*
35489  * Based on:
35490  * Ext JS Library 1.1.1
35491  * Copyright(c) 2006-2007, Ext JS, LLC.
35492  *
35493  * Originally Released Under LGPL - original licence link has changed is not relivant.
35494  *
35495  * Fork - LGPL
35496  * <script type="text/javascript">
35497  */
35498 /**
35499  * @class Roo.bootstrap.layout.Border
35500  * @extends Roo.bootstrap.layout.Manager
35501  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35502  * please see: examples/bootstrap/nested.html<br><br>
35503  
35504 <b>The container the layout is rendered into can be either the body element or any other element.
35505 If it is not the body element, the container needs to either be an absolute positioned element,
35506 or you will need to add "position:relative" to the css of the container.  You will also need to specify
35507 the container size if it is not the body element.</b>
35508
35509 * @constructor
35510 * Create a new Border
35511 * @param {Object} config Configuration options
35512  */
35513 Roo.bootstrap.layout.Border = function(config){
35514     config = config || {};
35515     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35516     
35517     
35518     
35519     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35520         if(config[region]){
35521             config[region].region = region;
35522             this.addRegion(config[region]);
35523         }
35524     },this);
35525     
35526 };
35527
35528 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35529
35530 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35531     
35532     parent : false, // this might point to a 'nest' or a ???
35533     
35534     /**
35535      * Creates and adds a new region if it doesn't already exist.
35536      * @param {String} target The target region key (north, south, east, west or center).
35537      * @param {Object} config The regions config object
35538      * @return {BorderLayoutRegion} The new region
35539      */
35540     addRegion : function(config)
35541     {
35542         if(!this.regions[config.region]){
35543             var r = this.factory(config);
35544             this.bindRegion(r);
35545         }
35546         return this.regions[config.region];
35547     },
35548
35549     // private (kinda)
35550     bindRegion : function(r){
35551         this.regions[r.config.region] = r;
35552         
35553         r.on("visibilitychange",    this.layout, this);
35554         r.on("paneladded",          this.layout, this);
35555         r.on("panelremoved",        this.layout, this);
35556         r.on("invalidated",         this.layout, this);
35557         r.on("resized",             this.onRegionResized, this);
35558         r.on("collapsed",           this.onRegionCollapsed, this);
35559         r.on("expanded",            this.onRegionExpanded, this);
35560     },
35561
35562     /**
35563      * Performs a layout update.
35564      */
35565     layout : function()
35566     {
35567         if(this.updating) {
35568             return;
35569         }
35570         
35571         // render all the rebions if they have not been done alreayd?
35572         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35573             if(this.regions[region] && !this.regions[region].bodyEl){
35574                 this.regions[region].onRender(this.el)
35575             }
35576         },this);
35577         
35578         var size = this.getViewSize();
35579         var w = size.width;
35580         var h = size.height;
35581         var centerW = w;
35582         var centerH = h;
35583         var centerY = 0;
35584         var centerX = 0;
35585         //var x = 0, y = 0;
35586
35587         var rs = this.regions;
35588         var north = rs["north"];
35589         var south = rs["south"]; 
35590         var west = rs["west"];
35591         var east = rs["east"];
35592         var center = rs["center"];
35593         //if(this.hideOnLayout){ // not supported anymore
35594             //c.el.setStyle("display", "none");
35595         //}
35596         if(north && north.isVisible()){
35597             var b = north.getBox();
35598             var m = north.getMargins();
35599             b.width = w - (m.left+m.right);
35600             b.x = m.left;
35601             b.y = m.top;
35602             centerY = b.height + b.y + m.bottom;
35603             centerH -= centerY;
35604             north.updateBox(this.safeBox(b));
35605         }
35606         if(south && south.isVisible()){
35607             var b = south.getBox();
35608             var m = south.getMargins();
35609             b.width = w - (m.left+m.right);
35610             b.x = m.left;
35611             var totalHeight = (b.height + m.top + m.bottom);
35612             b.y = h - totalHeight + m.top;
35613             centerH -= totalHeight;
35614             south.updateBox(this.safeBox(b));
35615         }
35616         if(west && west.isVisible()){
35617             var b = west.getBox();
35618             var m = west.getMargins();
35619             b.height = centerH - (m.top+m.bottom);
35620             b.x = m.left;
35621             b.y = centerY + m.top;
35622             var totalWidth = (b.width + m.left + m.right);
35623             centerX += totalWidth;
35624             centerW -= totalWidth;
35625             west.updateBox(this.safeBox(b));
35626         }
35627         if(east && east.isVisible()){
35628             var b = east.getBox();
35629             var m = east.getMargins();
35630             b.height = centerH - (m.top+m.bottom);
35631             var totalWidth = (b.width + m.left + m.right);
35632             b.x = w - totalWidth + m.left;
35633             b.y = centerY + m.top;
35634             centerW -= totalWidth;
35635             east.updateBox(this.safeBox(b));
35636         }
35637         if(center){
35638             var m = center.getMargins();
35639             var centerBox = {
35640                 x: centerX + m.left,
35641                 y: centerY + m.top,
35642                 width: centerW - (m.left+m.right),
35643                 height: centerH - (m.top+m.bottom)
35644             };
35645             //if(this.hideOnLayout){
35646                 //center.el.setStyle("display", "block");
35647             //}
35648             center.updateBox(this.safeBox(centerBox));
35649         }
35650         this.el.repaint();
35651         this.fireEvent("layout", this);
35652     },
35653
35654     // private
35655     safeBox : function(box){
35656         box.width = Math.max(0, box.width);
35657         box.height = Math.max(0, box.height);
35658         return box;
35659     },
35660
35661     /**
35662      * Adds a ContentPanel (or subclass) to this layout.
35663      * @param {String} target The target region key (north, south, east, west or center).
35664      * @param {Roo.ContentPanel} panel The panel to add
35665      * @return {Roo.ContentPanel} The added panel
35666      */
35667     add : function(target, panel){
35668          
35669         target = target.toLowerCase();
35670         return this.regions[target].add(panel);
35671     },
35672
35673     /**
35674      * Remove a ContentPanel (or subclass) to this layout.
35675      * @param {String} target The target region key (north, south, east, west or center).
35676      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35677      * @return {Roo.ContentPanel} The removed panel
35678      */
35679     remove : function(target, panel){
35680         target = target.toLowerCase();
35681         return this.regions[target].remove(panel);
35682     },
35683
35684     /**
35685      * Searches all regions for a panel with the specified id
35686      * @param {String} panelId
35687      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35688      */
35689     findPanel : function(panelId){
35690         var rs = this.regions;
35691         for(var target in rs){
35692             if(typeof rs[target] != "function"){
35693                 var p = rs[target].getPanel(panelId);
35694                 if(p){
35695                     return p;
35696                 }
35697             }
35698         }
35699         return null;
35700     },
35701
35702     /**
35703      * Searches all regions for a panel with the specified id and activates (shows) it.
35704      * @param {String/ContentPanel} panelId The panels id or the panel itself
35705      * @return {Roo.ContentPanel} The shown panel or null
35706      */
35707     showPanel : function(panelId) {
35708       var rs = this.regions;
35709       for(var target in rs){
35710          var r = rs[target];
35711          if(typeof r != "function"){
35712             if(r.hasPanel(panelId)){
35713                return r.showPanel(panelId);
35714             }
35715          }
35716       }
35717       return null;
35718    },
35719
35720    /**
35721      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35722      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35723      */
35724    /*
35725     restoreState : function(provider){
35726         if(!provider){
35727             provider = Roo.state.Manager;
35728         }
35729         var sm = new Roo.LayoutStateManager();
35730         sm.init(this, provider);
35731     },
35732 */
35733  
35734  
35735     /**
35736      * Adds a xtype elements to the layout.
35737      * <pre><code>
35738
35739 layout.addxtype({
35740        xtype : 'ContentPanel',
35741        region: 'west',
35742        items: [ .... ]
35743    }
35744 );
35745
35746 layout.addxtype({
35747         xtype : 'NestedLayoutPanel',
35748         region: 'west',
35749         layout: {
35750            center: { },
35751            west: { }   
35752         },
35753         items : [ ... list of content panels or nested layout panels.. ]
35754    }
35755 );
35756 </code></pre>
35757      * @param {Object} cfg Xtype definition of item to add.
35758      */
35759     addxtype : function(cfg)
35760     {
35761         // basically accepts a pannel...
35762         // can accept a layout region..!?!?
35763         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35764         
35765         
35766         // theory?  children can only be panels??
35767         
35768         //if (!cfg.xtype.match(/Panel$/)) {
35769         //    return false;
35770         //}
35771         var ret = false;
35772         
35773         if (typeof(cfg.region) == 'undefined') {
35774             Roo.log("Failed to add Panel, region was not set");
35775             Roo.log(cfg);
35776             return false;
35777         }
35778         var region = cfg.region;
35779         delete cfg.region;
35780         
35781           
35782         var xitems = [];
35783         if (cfg.items) {
35784             xitems = cfg.items;
35785             delete cfg.items;
35786         }
35787         var nb = false;
35788         
35789         if ( region == 'center') {
35790             Roo.log("Center: " + cfg.title);
35791         }
35792         
35793         
35794         switch(cfg.xtype) 
35795         {
35796             case 'Content':  // ContentPanel (el, cfg)
35797             case 'Scroll':  // ContentPanel (el, cfg)
35798             case 'View': 
35799                 cfg.autoCreate = cfg.autoCreate || true;
35800                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35801                 //} else {
35802                 //    var el = this.el.createChild();
35803                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35804                 //}
35805                 
35806                 this.add(region, ret);
35807                 break;
35808             
35809             /*
35810             case 'TreePanel': // our new panel!
35811                 cfg.el = this.el.createChild();
35812                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35813                 this.add(region, ret);
35814                 break;
35815             */
35816             
35817             case 'Nest': 
35818                 // create a new Layout (which is  a Border Layout...
35819                 
35820                 var clayout = cfg.layout;
35821                 clayout.el  = this.el.createChild();
35822                 clayout.items   = clayout.items  || [];
35823                 
35824                 delete cfg.layout;
35825                 
35826                 // replace this exitems with the clayout ones..
35827                 xitems = clayout.items;
35828                  
35829                 // force background off if it's in center...
35830                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35831                     cfg.background = false;
35832                 }
35833                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35834                 
35835                 
35836                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35837                 //console.log('adding nested layout panel '  + cfg.toSource());
35838                 this.add(region, ret);
35839                 nb = {}; /// find first...
35840                 break;
35841             
35842             case 'Grid':
35843                 
35844                 // needs grid and region
35845                 
35846                 //var el = this.getRegion(region).el.createChild();
35847                 /*
35848                  *var el = this.el.createChild();
35849                 // create the grid first...
35850                 cfg.grid.container = el;
35851                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35852                 */
35853                 
35854                 if (region == 'center' && this.active ) {
35855                     cfg.background = false;
35856                 }
35857                 
35858                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35859                 
35860                 this.add(region, ret);
35861                 /*
35862                 if (cfg.background) {
35863                     // render grid on panel activation (if panel background)
35864                     ret.on('activate', function(gp) {
35865                         if (!gp.grid.rendered) {
35866                     //        gp.grid.render(el);
35867                         }
35868                     });
35869                 } else {
35870                   //  cfg.grid.render(el);
35871                 }
35872                 */
35873                 break;
35874            
35875            
35876             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35877                 // it was the old xcomponent building that caused this before.
35878                 // espeically if border is the top element in the tree.
35879                 ret = this;
35880                 break; 
35881                 
35882                     
35883                 
35884                 
35885                 
35886             default:
35887                 /*
35888                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35889                     
35890                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35891                     this.add(region, ret);
35892                 } else {
35893                 */
35894                     Roo.log(cfg);
35895                     throw "Can not add '" + cfg.xtype + "' to Border";
35896                     return null;
35897              
35898                                 
35899              
35900         }
35901         this.beginUpdate();
35902         // add children..
35903         var region = '';
35904         var abn = {};
35905         Roo.each(xitems, function(i)  {
35906             region = nb && i.region ? i.region : false;
35907             
35908             var add = ret.addxtype(i);
35909            
35910             if (region) {
35911                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35912                 if (!i.background) {
35913                     abn[region] = nb[region] ;
35914                 }
35915             }
35916             
35917         });
35918         this.endUpdate();
35919
35920         // make the last non-background panel active..
35921         //if (nb) { Roo.log(abn); }
35922         if (nb) {
35923             
35924             for(var r in abn) {
35925                 region = this.getRegion(r);
35926                 if (region) {
35927                     // tried using nb[r], but it does not work..
35928                      
35929                     region.showPanel(abn[r]);
35930                    
35931                 }
35932             }
35933         }
35934         return ret;
35935         
35936     },
35937     
35938     
35939 // private
35940     factory : function(cfg)
35941     {
35942         
35943         var validRegions = Roo.bootstrap.layout.Border.regions;
35944
35945         var target = cfg.region;
35946         cfg.mgr = this;
35947         
35948         var r = Roo.bootstrap.layout;
35949         Roo.log(target);
35950         switch(target){
35951             case "north":
35952                 return new r.North(cfg);
35953             case "south":
35954                 return new r.South(cfg);
35955             case "east":
35956                 return new r.East(cfg);
35957             case "west":
35958                 return new r.West(cfg);
35959             case "center":
35960                 return new r.Center(cfg);
35961         }
35962         throw 'Layout region "'+target+'" not supported.';
35963     }
35964     
35965     
35966 });
35967  /*
35968  * Based on:
35969  * Ext JS Library 1.1.1
35970  * Copyright(c) 2006-2007, Ext JS, LLC.
35971  *
35972  * Originally Released Under LGPL - original licence link has changed is not relivant.
35973  *
35974  * Fork - LGPL
35975  * <script type="text/javascript">
35976  */
35977  
35978 /**
35979  * @class Roo.bootstrap.layout.Basic
35980  * @extends Roo.util.Observable
35981  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35982  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35983  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35984  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35985  * @cfg {string}   region  the region that it inhabits..
35986  * @cfg {bool}   skipConfig skip config?
35987  * 
35988
35989  */
35990 Roo.bootstrap.layout.Basic = function(config){
35991     
35992     this.mgr = config.mgr;
35993     
35994     this.position = config.region;
35995     
35996     var skipConfig = config.skipConfig;
35997     
35998     this.events = {
35999         /**
36000          * @scope Roo.BasicLayoutRegion
36001          */
36002         
36003         /**
36004          * @event beforeremove
36005          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
36006          * @param {Roo.LayoutRegion} this
36007          * @param {Roo.ContentPanel} panel The panel
36008          * @param {Object} e The cancel event object
36009          */
36010         "beforeremove" : true,
36011         /**
36012          * @event invalidated
36013          * Fires when the layout for this region is changed.
36014          * @param {Roo.LayoutRegion} this
36015          */
36016         "invalidated" : true,
36017         /**
36018          * @event visibilitychange
36019          * Fires when this region is shown or hidden 
36020          * @param {Roo.LayoutRegion} this
36021          * @param {Boolean} visibility true or false
36022          */
36023         "visibilitychange" : true,
36024         /**
36025          * @event paneladded
36026          * Fires when a panel is added. 
36027          * @param {Roo.LayoutRegion} this
36028          * @param {Roo.ContentPanel} panel The panel
36029          */
36030         "paneladded" : true,
36031         /**
36032          * @event panelremoved
36033          * Fires when a panel is removed. 
36034          * @param {Roo.LayoutRegion} this
36035          * @param {Roo.ContentPanel} panel The panel
36036          */
36037         "panelremoved" : true,
36038         /**
36039          * @event beforecollapse
36040          * Fires when this region before collapse.
36041          * @param {Roo.LayoutRegion} this
36042          */
36043         "beforecollapse" : true,
36044         /**
36045          * @event collapsed
36046          * Fires when this region is collapsed.
36047          * @param {Roo.LayoutRegion} this
36048          */
36049         "collapsed" : true,
36050         /**
36051          * @event expanded
36052          * Fires when this region is expanded.
36053          * @param {Roo.LayoutRegion} this
36054          */
36055         "expanded" : true,
36056         /**
36057          * @event slideshow
36058          * Fires when this region is slid into view.
36059          * @param {Roo.LayoutRegion} this
36060          */
36061         "slideshow" : true,
36062         /**
36063          * @event slidehide
36064          * Fires when this region slides out of view. 
36065          * @param {Roo.LayoutRegion} this
36066          */
36067         "slidehide" : true,
36068         /**
36069          * @event panelactivated
36070          * Fires when a panel is activated. 
36071          * @param {Roo.LayoutRegion} this
36072          * @param {Roo.ContentPanel} panel The activated panel
36073          */
36074         "panelactivated" : true,
36075         /**
36076          * @event resized
36077          * Fires when the user resizes this region. 
36078          * @param {Roo.LayoutRegion} this
36079          * @param {Number} newSize The new size (width for east/west, height for north/south)
36080          */
36081         "resized" : true
36082     };
36083     /** A collection of panels in this region. @type Roo.util.MixedCollection */
36084     this.panels = new Roo.util.MixedCollection();
36085     this.panels.getKey = this.getPanelId.createDelegate(this);
36086     this.box = null;
36087     this.activePanel = null;
36088     // ensure listeners are added...
36089     
36090     if (config.listeners || config.events) {
36091         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
36092             listeners : config.listeners || {},
36093             events : config.events || {}
36094         });
36095     }
36096     
36097     if(skipConfig !== true){
36098         this.applyConfig(config);
36099     }
36100 };
36101
36102 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
36103 {
36104     getPanelId : function(p){
36105         return p.getId();
36106     },
36107     
36108     applyConfig : function(config){
36109         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36110         this.config = config;
36111         
36112     },
36113     
36114     /**
36115      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
36116      * the width, for horizontal (north, south) the height.
36117      * @param {Number} newSize The new width or height
36118      */
36119     resizeTo : function(newSize){
36120         var el = this.el ? this.el :
36121                  (this.activePanel ? this.activePanel.getEl() : null);
36122         if(el){
36123             switch(this.position){
36124                 case "east":
36125                 case "west":
36126                     el.setWidth(newSize);
36127                     this.fireEvent("resized", this, newSize);
36128                 break;
36129                 case "north":
36130                 case "south":
36131                     el.setHeight(newSize);
36132                     this.fireEvent("resized", this, newSize);
36133                 break;                
36134             }
36135         }
36136     },
36137     
36138     getBox : function(){
36139         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
36140     },
36141     
36142     getMargins : function(){
36143         return this.margins;
36144     },
36145     
36146     updateBox : function(box){
36147         this.box = box;
36148         var el = this.activePanel.getEl();
36149         el.dom.style.left = box.x + "px";
36150         el.dom.style.top = box.y + "px";
36151         this.activePanel.setSize(box.width, box.height);
36152     },
36153     
36154     /**
36155      * Returns the container element for this region.
36156      * @return {Roo.Element}
36157      */
36158     getEl : function(){
36159         return this.activePanel;
36160     },
36161     
36162     /**
36163      * Returns true if this region is currently visible.
36164      * @return {Boolean}
36165      */
36166     isVisible : function(){
36167         return this.activePanel ? true : false;
36168     },
36169     
36170     setActivePanel : function(panel){
36171         panel = this.getPanel(panel);
36172         if(this.activePanel && this.activePanel != panel){
36173             this.activePanel.setActiveState(false);
36174             this.activePanel.getEl().setLeftTop(-10000,-10000);
36175         }
36176         this.activePanel = panel;
36177         panel.setActiveState(true);
36178         if(this.box){
36179             panel.setSize(this.box.width, this.box.height);
36180         }
36181         this.fireEvent("panelactivated", this, panel);
36182         this.fireEvent("invalidated");
36183     },
36184     
36185     /**
36186      * Show the specified panel.
36187      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
36188      * @return {Roo.ContentPanel} The shown panel or null
36189      */
36190     showPanel : function(panel){
36191         panel = this.getPanel(panel);
36192         if(panel){
36193             this.setActivePanel(panel);
36194         }
36195         return panel;
36196     },
36197     
36198     /**
36199      * Get the active panel for this region.
36200      * @return {Roo.ContentPanel} The active panel or null
36201      */
36202     getActivePanel : function(){
36203         return this.activePanel;
36204     },
36205     
36206     /**
36207      * Add the passed ContentPanel(s)
36208      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36209      * @return {Roo.ContentPanel} The panel added (if only one was added)
36210      */
36211     add : function(panel){
36212         if(arguments.length > 1){
36213             for(var i = 0, len = arguments.length; i < len; i++) {
36214                 this.add(arguments[i]);
36215             }
36216             return null;
36217         }
36218         if(this.hasPanel(panel)){
36219             this.showPanel(panel);
36220             return panel;
36221         }
36222         var el = panel.getEl();
36223         if(el.dom.parentNode != this.mgr.el.dom){
36224             this.mgr.el.dom.appendChild(el.dom);
36225         }
36226         if(panel.setRegion){
36227             panel.setRegion(this);
36228         }
36229         this.panels.add(panel);
36230         el.setStyle("position", "absolute");
36231         if(!panel.background){
36232             this.setActivePanel(panel);
36233             if(this.config.initialSize && this.panels.getCount()==1){
36234                 this.resizeTo(this.config.initialSize);
36235             }
36236         }
36237         this.fireEvent("paneladded", this, panel);
36238         return panel;
36239     },
36240     
36241     /**
36242      * Returns true if the panel is in this region.
36243      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36244      * @return {Boolean}
36245      */
36246     hasPanel : function(panel){
36247         if(typeof panel == "object"){ // must be panel obj
36248             panel = panel.getId();
36249         }
36250         return this.getPanel(panel) ? true : false;
36251     },
36252     
36253     /**
36254      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36255      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36256      * @param {Boolean} preservePanel Overrides the config preservePanel option
36257      * @return {Roo.ContentPanel} The panel that was removed
36258      */
36259     remove : function(panel, preservePanel){
36260         panel = this.getPanel(panel);
36261         if(!panel){
36262             return null;
36263         }
36264         var e = {};
36265         this.fireEvent("beforeremove", this, panel, e);
36266         if(e.cancel === true){
36267             return null;
36268         }
36269         var panelId = panel.getId();
36270         this.panels.removeKey(panelId);
36271         return panel;
36272     },
36273     
36274     /**
36275      * Returns the panel specified or null if it's not in this region.
36276      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36277      * @return {Roo.ContentPanel}
36278      */
36279     getPanel : function(id){
36280         if(typeof id == "object"){ // must be panel obj
36281             return id;
36282         }
36283         return this.panels.get(id);
36284     },
36285     
36286     /**
36287      * Returns this regions position (north/south/east/west/center).
36288      * @return {String} 
36289      */
36290     getPosition: function(){
36291         return this.position;    
36292     }
36293 });/*
36294  * Based on:
36295  * Ext JS Library 1.1.1
36296  * Copyright(c) 2006-2007, Ext JS, LLC.
36297  *
36298  * Originally Released Under LGPL - original licence link has changed is not relivant.
36299  *
36300  * Fork - LGPL
36301  * <script type="text/javascript">
36302  */
36303  
36304 /**
36305  * @class Roo.bootstrap.layout.Region
36306  * @extends Roo.bootstrap.layout.Basic
36307  * This class represents a region in a layout manager.
36308  
36309  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
36310  * @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})
36311  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
36312  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
36313  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
36314  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
36315  * @cfg {String}    title           The title for the region (overrides panel titles)
36316  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
36317  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
36318  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
36319  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
36320  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
36321  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
36322  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
36323  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
36324  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
36325  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
36326
36327  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
36328  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
36329  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
36330  * @cfg {Number}    width           For East/West panels
36331  * @cfg {Number}    height          For North/South panels
36332  * @cfg {Boolean}   split           To show the splitter
36333  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
36334  * 
36335  * @cfg {string}   cls             Extra CSS classes to add to region
36336  * 
36337  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
36338  * @cfg {string}   region  the region that it inhabits..
36339  *
36340
36341  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
36342  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
36343
36344  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
36345  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
36346  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
36347  */
36348 Roo.bootstrap.layout.Region = function(config)
36349 {
36350     this.applyConfig(config);
36351
36352     var mgr = config.mgr;
36353     var pos = config.region;
36354     config.skipConfig = true;
36355     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36356     
36357     if (mgr.el) {
36358         this.onRender(mgr.el);   
36359     }
36360      
36361     this.visible = true;
36362     this.collapsed = false;
36363     this.unrendered_panels = [];
36364 };
36365
36366 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36367
36368     position: '', // set by wrapper (eg. north/south etc..)
36369     unrendered_panels : null,  // unrendered panels.
36370     
36371     tabPosition : false,
36372     
36373     mgr: false, // points to 'Border'
36374     
36375     
36376     createBody : function(){
36377         /** This region's body element 
36378         * @type Roo.Element */
36379         this.bodyEl = this.el.createChild({
36380                 tag: "div",
36381                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36382         });
36383     },
36384
36385     onRender: function(ctr, pos)
36386     {
36387         var dh = Roo.DomHelper;
36388         /** This region's container element 
36389         * @type Roo.Element */
36390         this.el = dh.append(ctr.dom, {
36391                 tag: "div",
36392                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36393             }, true);
36394         /** This region's title element 
36395         * @type Roo.Element */
36396     
36397         this.titleEl = dh.append(this.el.dom,  {
36398                 tag: "div",
36399                 unselectable: "on",
36400                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36401                 children:[
36402                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
36403                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36404                 ]
36405             }, true);
36406         
36407         this.titleEl.enableDisplayMode();
36408         /** This region's title text element 
36409         * @type HTMLElement */
36410         this.titleTextEl = this.titleEl.dom.firstChild;
36411         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36412         /*
36413         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36414         this.closeBtn.enableDisplayMode();
36415         this.closeBtn.on("click", this.closeClicked, this);
36416         this.closeBtn.hide();
36417     */
36418         this.createBody(this.config);
36419         if(this.config.hideWhenEmpty){
36420             this.hide();
36421             this.on("paneladded", this.validateVisibility, this);
36422             this.on("panelremoved", this.validateVisibility, this);
36423         }
36424         if(this.autoScroll){
36425             this.bodyEl.setStyle("overflow", "auto");
36426         }else{
36427             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36428         }
36429         //if(c.titlebar !== false){
36430             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36431                 this.titleEl.hide();
36432             }else{
36433                 this.titleEl.show();
36434                 if(this.config.title){
36435                     this.titleTextEl.innerHTML = this.config.title;
36436                 }
36437             }
36438         //}
36439         if(this.config.collapsed){
36440             this.collapse(true);
36441         }
36442         if(this.config.hidden){
36443             this.hide();
36444         }
36445         
36446         if (this.unrendered_panels && this.unrendered_panels.length) {
36447             for (var i =0;i< this.unrendered_panels.length; i++) {
36448                 this.add(this.unrendered_panels[i]);
36449             }
36450             this.unrendered_panels = null;
36451             
36452         }
36453         
36454     },
36455     
36456     applyConfig : function(c)
36457     {
36458         /*
36459          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36460             var dh = Roo.DomHelper;
36461             if(c.titlebar !== false){
36462                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36463                 this.collapseBtn.on("click", this.collapse, this);
36464                 this.collapseBtn.enableDisplayMode();
36465                 /*
36466                 if(c.showPin === true || this.showPin){
36467                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36468                     this.stickBtn.enableDisplayMode();
36469                     this.stickBtn.on("click", this.expand, this);
36470                     this.stickBtn.hide();
36471                 }
36472                 
36473             }
36474             */
36475             /** This region's collapsed element
36476             * @type Roo.Element */
36477             /*
36478              *
36479             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36480                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36481             ]}, true);
36482             
36483             if(c.floatable !== false){
36484                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36485                this.collapsedEl.on("click", this.collapseClick, this);
36486             }
36487
36488             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36489                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36490                    id: "message", unselectable: "on", style:{"float":"left"}});
36491                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36492              }
36493             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36494             this.expandBtn.on("click", this.expand, this);
36495             
36496         }
36497         
36498         if(this.collapseBtn){
36499             this.collapseBtn.setVisible(c.collapsible == true);
36500         }
36501         
36502         this.cmargins = c.cmargins || this.cmargins ||
36503                          (this.position == "west" || this.position == "east" ?
36504                              {top: 0, left: 2, right:2, bottom: 0} :
36505                              {top: 2, left: 0, right:0, bottom: 2});
36506         */
36507         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36508         
36509         
36510         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36511         
36512         this.autoScroll = c.autoScroll || false;
36513         
36514         
36515        
36516         
36517         this.duration = c.duration || .30;
36518         this.slideDuration = c.slideDuration || .45;
36519         this.config = c;
36520        
36521     },
36522     /**
36523      * Returns true if this region is currently visible.
36524      * @return {Boolean}
36525      */
36526     isVisible : function(){
36527         return this.visible;
36528     },
36529
36530     /**
36531      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36532      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36533      */
36534     //setCollapsedTitle : function(title){
36535     //    title = title || "&#160;";
36536      //   if(this.collapsedTitleTextEl){
36537       //      this.collapsedTitleTextEl.innerHTML = title;
36538        // }
36539     //},
36540
36541     getBox : function(){
36542         var b;
36543       //  if(!this.collapsed){
36544             b = this.el.getBox(false, true);
36545        // }else{
36546           //  b = this.collapsedEl.getBox(false, true);
36547         //}
36548         return b;
36549     },
36550
36551     getMargins : function(){
36552         return this.margins;
36553         //return this.collapsed ? this.cmargins : this.margins;
36554     },
36555 /*
36556     highlight : function(){
36557         this.el.addClass("x-layout-panel-dragover");
36558     },
36559
36560     unhighlight : function(){
36561         this.el.removeClass("x-layout-panel-dragover");
36562     },
36563 */
36564     updateBox : function(box)
36565     {
36566         if (!this.bodyEl) {
36567             return; // not rendered yet..
36568         }
36569         
36570         this.box = box;
36571         if(!this.collapsed){
36572             this.el.dom.style.left = box.x + "px";
36573             this.el.dom.style.top = box.y + "px";
36574             this.updateBody(box.width, box.height);
36575         }else{
36576             this.collapsedEl.dom.style.left = box.x + "px";
36577             this.collapsedEl.dom.style.top = box.y + "px";
36578             this.collapsedEl.setSize(box.width, box.height);
36579         }
36580         if(this.tabs){
36581             this.tabs.autoSizeTabs();
36582         }
36583     },
36584
36585     updateBody : function(w, h)
36586     {
36587         if(w !== null){
36588             this.el.setWidth(w);
36589             w -= this.el.getBorderWidth("rl");
36590             if(this.config.adjustments){
36591                 w += this.config.adjustments[0];
36592             }
36593         }
36594         if(h !== null && h > 0){
36595             this.el.setHeight(h);
36596             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36597             h -= this.el.getBorderWidth("tb");
36598             if(this.config.adjustments){
36599                 h += this.config.adjustments[1];
36600             }
36601             this.bodyEl.setHeight(h);
36602             if(this.tabs){
36603                 h = this.tabs.syncHeight(h);
36604             }
36605         }
36606         if(this.panelSize){
36607             w = w !== null ? w : this.panelSize.width;
36608             h = h !== null ? h : this.panelSize.height;
36609         }
36610         if(this.activePanel){
36611             var el = this.activePanel.getEl();
36612             w = w !== null ? w : el.getWidth();
36613             h = h !== null ? h : el.getHeight();
36614             this.panelSize = {width: w, height: h};
36615             this.activePanel.setSize(w, h);
36616         }
36617         if(Roo.isIE && this.tabs){
36618             this.tabs.el.repaint();
36619         }
36620     },
36621
36622     /**
36623      * Returns the container element for this region.
36624      * @return {Roo.Element}
36625      */
36626     getEl : function(){
36627         return this.el;
36628     },
36629
36630     /**
36631      * Hides this region.
36632      */
36633     hide : function(){
36634         //if(!this.collapsed){
36635             this.el.dom.style.left = "-2000px";
36636             this.el.hide();
36637         //}else{
36638          //   this.collapsedEl.dom.style.left = "-2000px";
36639          //   this.collapsedEl.hide();
36640        // }
36641         this.visible = false;
36642         this.fireEvent("visibilitychange", this, false);
36643     },
36644
36645     /**
36646      * Shows this region if it was previously hidden.
36647      */
36648     show : function(){
36649         //if(!this.collapsed){
36650             this.el.show();
36651         //}else{
36652         //    this.collapsedEl.show();
36653        // }
36654         this.visible = true;
36655         this.fireEvent("visibilitychange", this, true);
36656     },
36657 /*
36658     closeClicked : function(){
36659         if(this.activePanel){
36660             this.remove(this.activePanel);
36661         }
36662     },
36663
36664     collapseClick : function(e){
36665         if(this.isSlid){
36666            e.stopPropagation();
36667            this.slideIn();
36668         }else{
36669            e.stopPropagation();
36670            this.slideOut();
36671         }
36672     },
36673 */
36674     /**
36675      * Collapses this region.
36676      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36677      */
36678     /*
36679     collapse : function(skipAnim, skipCheck = false){
36680         if(this.collapsed) {
36681             return;
36682         }
36683         
36684         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36685             
36686             this.collapsed = true;
36687             if(this.split){
36688                 this.split.el.hide();
36689             }
36690             if(this.config.animate && skipAnim !== true){
36691                 this.fireEvent("invalidated", this);
36692                 this.animateCollapse();
36693             }else{
36694                 this.el.setLocation(-20000,-20000);
36695                 this.el.hide();
36696                 this.collapsedEl.show();
36697                 this.fireEvent("collapsed", this);
36698                 this.fireEvent("invalidated", this);
36699             }
36700         }
36701         
36702     },
36703 */
36704     animateCollapse : function(){
36705         // overridden
36706     },
36707
36708     /**
36709      * Expands this region if it was previously collapsed.
36710      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36711      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36712      */
36713     /*
36714     expand : function(e, skipAnim){
36715         if(e) {
36716             e.stopPropagation();
36717         }
36718         if(!this.collapsed || this.el.hasActiveFx()) {
36719             return;
36720         }
36721         if(this.isSlid){
36722             this.afterSlideIn();
36723             skipAnim = true;
36724         }
36725         this.collapsed = false;
36726         if(this.config.animate && skipAnim !== true){
36727             this.animateExpand();
36728         }else{
36729             this.el.show();
36730             if(this.split){
36731                 this.split.el.show();
36732             }
36733             this.collapsedEl.setLocation(-2000,-2000);
36734             this.collapsedEl.hide();
36735             this.fireEvent("invalidated", this);
36736             this.fireEvent("expanded", this);
36737         }
36738     },
36739 */
36740     animateExpand : function(){
36741         // overridden
36742     },
36743
36744     initTabs : function()
36745     {
36746         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36747         
36748         var ts = new Roo.bootstrap.panel.Tabs({
36749             el: this.bodyEl.dom,
36750             region : this,
36751             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
36752             disableTooltips: this.config.disableTabTips,
36753             toolbar : this.config.toolbar
36754         });
36755         
36756         if(this.config.hideTabs){
36757             ts.stripWrap.setDisplayed(false);
36758         }
36759         this.tabs = ts;
36760         ts.resizeTabs = this.config.resizeTabs === true;
36761         ts.minTabWidth = this.config.minTabWidth || 40;
36762         ts.maxTabWidth = this.config.maxTabWidth || 250;
36763         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36764         ts.monitorResize = false;
36765         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36766         ts.bodyEl.addClass('roo-layout-tabs-body');
36767         this.panels.each(this.initPanelAsTab, this);
36768     },
36769
36770     initPanelAsTab : function(panel){
36771         var ti = this.tabs.addTab(
36772             panel.getEl().id,
36773             panel.getTitle(),
36774             null,
36775             this.config.closeOnTab && panel.isClosable(),
36776             panel.tpl
36777         );
36778         if(panel.tabTip !== undefined){
36779             ti.setTooltip(panel.tabTip);
36780         }
36781         ti.on("activate", function(){
36782               this.setActivePanel(panel);
36783         }, this);
36784         
36785         if(this.config.closeOnTab){
36786             ti.on("beforeclose", function(t, e){
36787                 e.cancel = true;
36788                 this.remove(panel);
36789             }, this);
36790         }
36791         
36792         panel.tabItem = ti;
36793         
36794         return ti;
36795     },
36796
36797     updatePanelTitle : function(panel, title)
36798     {
36799         if(this.activePanel == panel){
36800             this.updateTitle(title);
36801         }
36802         if(this.tabs){
36803             var ti = this.tabs.getTab(panel.getEl().id);
36804             ti.setText(title);
36805             if(panel.tabTip !== undefined){
36806                 ti.setTooltip(panel.tabTip);
36807             }
36808         }
36809     },
36810
36811     updateTitle : function(title){
36812         if(this.titleTextEl && !this.config.title){
36813             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36814         }
36815     },
36816
36817     setActivePanel : function(panel)
36818     {
36819         panel = this.getPanel(panel);
36820         if(this.activePanel && this.activePanel != panel){
36821             if(this.activePanel.setActiveState(false) === false){
36822                 return;
36823             }
36824         }
36825         this.activePanel = panel;
36826         panel.setActiveState(true);
36827         if(this.panelSize){
36828             panel.setSize(this.panelSize.width, this.panelSize.height);
36829         }
36830         if(this.closeBtn){
36831             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36832         }
36833         this.updateTitle(panel.getTitle());
36834         if(this.tabs){
36835             this.fireEvent("invalidated", this);
36836         }
36837         this.fireEvent("panelactivated", this, panel);
36838     },
36839
36840     /**
36841      * Shows the specified panel.
36842      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36843      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36844      */
36845     showPanel : function(panel)
36846     {
36847         panel = this.getPanel(panel);
36848         if(panel){
36849             if(this.tabs){
36850                 var tab = this.tabs.getTab(panel.getEl().id);
36851                 if(tab.isHidden()){
36852                     this.tabs.unhideTab(tab.id);
36853                 }
36854                 tab.activate();
36855             }else{
36856                 this.setActivePanel(panel);
36857             }
36858         }
36859         return panel;
36860     },
36861
36862     /**
36863      * Get the active panel for this region.
36864      * @return {Roo.ContentPanel} The active panel or null
36865      */
36866     getActivePanel : function(){
36867         return this.activePanel;
36868     },
36869
36870     validateVisibility : function(){
36871         if(this.panels.getCount() < 1){
36872             this.updateTitle("&#160;");
36873             this.closeBtn.hide();
36874             this.hide();
36875         }else{
36876             if(!this.isVisible()){
36877                 this.show();
36878             }
36879         }
36880     },
36881
36882     /**
36883      * Adds the passed ContentPanel(s) to this region.
36884      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36885      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36886      */
36887     add : function(panel)
36888     {
36889         if(arguments.length > 1){
36890             for(var i = 0, len = arguments.length; i < len; i++) {
36891                 this.add(arguments[i]);
36892             }
36893             return null;
36894         }
36895         
36896         // if we have not been rendered yet, then we can not really do much of this..
36897         if (!this.bodyEl) {
36898             this.unrendered_panels.push(panel);
36899             return panel;
36900         }
36901         
36902         
36903         
36904         
36905         if(this.hasPanel(panel)){
36906             this.showPanel(panel);
36907             return panel;
36908         }
36909         panel.setRegion(this);
36910         this.panels.add(panel);
36911        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36912             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36913             // and hide them... ???
36914             this.bodyEl.dom.appendChild(panel.getEl().dom);
36915             if(panel.background !== true){
36916                 this.setActivePanel(panel);
36917             }
36918             this.fireEvent("paneladded", this, panel);
36919             return panel;
36920         }
36921         */
36922         if(!this.tabs){
36923             this.initTabs();
36924         }else{
36925             this.initPanelAsTab(panel);
36926         }
36927         
36928         
36929         if(panel.background !== true){
36930             this.tabs.activate(panel.getEl().id);
36931         }
36932         this.fireEvent("paneladded", this, panel);
36933         return panel;
36934     },
36935
36936     /**
36937      * Hides the tab for the specified panel.
36938      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36939      */
36940     hidePanel : function(panel){
36941         if(this.tabs && (panel = this.getPanel(panel))){
36942             this.tabs.hideTab(panel.getEl().id);
36943         }
36944     },
36945
36946     /**
36947      * Unhides the tab for a previously hidden panel.
36948      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36949      */
36950     unhidePanel : function(panel){
36951         if(this.tabs && (panel = this.getPanel(panel))){
36952             this.tabs.unhideTab(panel.getEl().id);
36953         }
36954     },
36955
36956     clearPanels : function(){
36957         while(this.panels.getCount() > 0){
36958              this.remove(this.panels.first());
36959         }
36960     },
36961
36962     /**
36963      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36964      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36965      * @param {Boolean} preservePanel Overrides the config preservePanel option
36966      * @return {Roo.ContentPanel} The panel that was removed
36967      */
36968     remove : function(panel, preservePanel)
36969     {
36970         panel = this.getPanel(panel);
36971         if(!panel){
36972             return null;
36973         }
36974         var e = {};
36975         this.fireEvent("beforeremove", this, panel, e);
36976         if(e.cancel === true){
36977             return null;
36978         }
36979         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36980         var panelId = panel.getId();
36981         this.panels.removeKey(panelId);
36982         if(preservePanel){
36983             document.body.appendChild(panel.getEl().dom);
36984         }
36985         if(this.tabs){
36986             this.tabs.removeTab(panel.getEl().id);
36987         }else if (!preservePanel){
36988             this.bodyEl.dom.removeChild(panel.getEl().dom);
36989         }
36990         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36991             var p = this.panels.first();
36992             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36993             tempEl.appendChild(p.getEl().dom);
36994             this.bodyEl.update("");
36995             this.bodyEl.dom.appendChild(p.getEl().dom);
36996             tempEl = null;
36997             this.updateTitle(p.getTitle());
36998             this.tabs = null;
36999             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
37000             this.setActivePanel(p);
37001         }
37002         panel.setRegion(null);
37003         if(this.activePanel == panel){
37004             this.activePanel = null;
37005         }
37006         if(this.config.autoDestroy !== false && preservePanel !== true){
37007             try{panel.destroy();}catch(e){}
37008         }
37009         this.fireEvent("panelremoved", this, panel);
37010         return panel;
37011     },
37012
37013     /**
37014      * Returns the TabPanel component used by this region
37015      * @return {Roo.TabPanel}
37016      */
37017     getTabs : function(){
37018         return this.tabs;
37019     },
37020
37021     createTool : function(parentEl, className){
37022         var btn = Roo.DomHelper.append(parentEl, {
37023             tag: "div",
37024             cls: "x-layout-tools-button",
37025             children: [ {
37026                 tag: "div",
37027                 cls: "roo-layout-tools-button-inner " + className,
37028                 html: "&#160;"
37029             }]
37030         }, true);
37031         btn.addClassOnOver("roo-layout-tools-button-over");
37032         return btn;
37033     }
37034 });/*
37035  * Based on:
37036  * Ext JS Library 1.1.1
37037  * Copyright(c) 2006-2007, Ext JS, LLC.
37038  *
37039  * Originally Released Under LGPL - original licence link has changed is not relivant.
37040  *
37041  * Fork - LGPL
37042  * <script type="text/javascript">
37043  */
37044  
37045
37046
37047 /**
37048  * @class Roo.SplitLayoutRegion
37049  * @extends Roo.LayoutRegion
37050  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
37051  */
37052 Roo.bootstrap.layout.Split = function(config){
37053     this.cursor = config.cursor;
37054     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
37055 };
37056
37057 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
37058 {
37059     splitTip : "Drag to resize.",
37060     collapsibleSplitTip : "Drag to resize. Double click to hide.",
37061     useSplitTips : false,
37062
37063     applyConfig : function(config){
37064         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
37065     },
37066     
37067     onRender : function(ctr,pos) {
37068         
37069         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
37070         if(!this.config.split){
37071             return;
37072         }
37073         if(!this.split){
37074             
37075             var splitEl = Roo.DomHelper.append(ctr.dom,  {
37076                             tag: "div",
37077                             id: this.el.id + "-split",
37078                             cls: "roo-layout-split roo-layout-split-"+this.position,
37079                             html: "&#160;"
37080             });
37081             /** The SplitBar for this region 
37082             * @type Roo.SplitBar */
37083             // does not exist yet...
37084             Roo.log([this.position, this.orientation]);
37085             
37086             this.split = new Roo.bootstrap.SplitBar({
37087                 dragElement : splitEl,
37088                 resizingElement: this.el,
37089                 orientation : this.orientation
37090             });
37091             
37092             this.split.on("moved", this.onSplitMove, this);
37093             this.split.useShim = this.config.useShim === true;
37094             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
37095             if(this.useSplitTips){
37096                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
37097             }
37098             //if(config.collapsible){
37099             //    this.split.el.on("dblclick", this.collapse,  this);
37100             //}
37101         }
37102         if(typeof this.config.minSize != "undefined"){
37103             this.split.minSize = this.config.minSize;
37104         }
37105         if(typeof this.config.maxSize != "undefined"){
37106             this.split.maxSize = this.config.maxSize;
37107         }
37108         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
37109             this.hideSplitter();
37110         }
37111         
37112     },
37113
37114     getHMaxSize : function(){
37115          var cmax = this.config.maxSize || 10000;
37116          var center = this.mgr.getRegion("center");
37117          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
37118     },
37119
37120     getVMaxSize : function(){
37121          var cmax = this.config.maxSize || 10000;
37122          var center = this.mgr.getRegion("center");
37123          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
37124     },
37125
37126     onSplitMove : function(split, newSize){
37127         this.fireEvent("resized", this, newSize);
37128     },
37129     
37130     /** 
37131      * Returns the {@link Roo.SplitBar} for this region.
37132      * @return {Roo.SplitBar}
37133      */
37134     getSplitBar : function(){
37135         return this.split;
37136     },
37137     
37138     hide : function(){
37139         this.hideSplitter();
37140         Roo.bootstrap.layout.Split.superclass.hide.call(this);
37141     },
37142
37143     hideSplitter : function(){
37144         if(this.split){
37145             this.split.el.setLocation(-2000,-2000);
37146             this.split.el.hide();
37147         }
37148     },
37149
37150     show : function(){
37151         if(this.split){
37152             this.split.el.show();
37153         }
37154         Roo.bootstrap.layout.Split.superclass.show.call(this);
37155     },
37156     
37157     beforeSlide: function(){
37158         if(Roo.isGecko){// firefox overflow auto bug workaround
37159             this.bodyEl.clip();
37160             if(this.tabs) {
37161                 this.tabs.bodyEl.clip();
37162             }
37163             if(this.activePanel){
37164                 this.activePanel.getEl().clip();
37165                 
37166                 if(this.activePanel.beforeSlide){
37167                     this.activePanel.beforeSlide();
37168                 }
37169             }
37170         }
37171     },
37172     
37173     afterSlide : function(){
37174         if(Roo.isGecko){// firefox overflow auto bug workaround
37175             this.bodyEl.unclip();
37176             if(this.tabs) {
37177                 this.tabs.bodyEl.unclip();
37178             }
37179             if(this.activePanel){
37180                 this.activePanel.getEl().unclip();
37181                 if(this.activePanel.afterSlide){
37182                     this.activePanel.afterSlide();
37183                 }
37184             }
37185         }
37186     },
37187
37188     initAutoHide : function(){
37189         if(this.autoHide !== false){
37190             if(!this.autoHideHd){
37191                 var st = new Roo.util.DelayedTask(this.slideIn, this);
37192                 this.autoHideHd = {
37193                     "mouseout": function(e){
37194                         if(!e.within(this.el, true)){
37195                             st.delay(500);
37196                         }
37197                     },
37198                     "mouseover" : function(e){
37199                         st.cancel();
37200                     },
37201                     scope : this
37202                 };
37203             }
37204             this.el.on(this.autoHideHd);
37205         }
37206     },
37207
37208     clearAutoHide : function(){
37209         if(this.autoHide !== false){
37210             this.el.un("mouseout", this.autoHideHd.mouseout);
37211             this.el.un("mouseover", this.autoHideHd.mouseover);
37212         }
37213     },
37214
37215     clearMonitor : function(){
37216         Roo.get(document).un("click", this.slideInIf, this);
37217     },
37218
37219     // these names are backwards but not changed for compat
37220     slideOut : function(){
37221         if(this.isSlid || this.el.hasActiveFx()){
37222             return;
37223         }
37224         this.isSlid = true;
37225         if(this.collapseBtn){
37226             this.collapseBtn.hide();
37227         }
37228         this.closeBtnState = this.closeBtn.getStyle('display');
37229         this.closeBtn.hide();
37230         if(this.stickBtn){
37231             this.stickBtn.show();
37232         }
37233         this.el.show();
37234         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
37235         this.beforeSlide();
37236         this.el.setStyle("z-index", 10001);
37237         this.el.slideIn(this.getSlideAnchor(), {
37238             callback: function(){
37239                 this.afterSlide();
37240                 this.initAutoHide();
37241                 Roo.get(document).on("click", this.slideInIf, this);
37242                 this.fireEvent("slideshow", this);
37243             },
37244             scope: this,
37245             block: true
37246         });
37247     },
37248
37249     afterSlideIn : function(){
37250         this.clearAutoHide();
37251         this.isSlid = false;
37252         this.clearMonitor();
37253         this.el.setStyle("z-index", "");
37254         if(this.collapseBtn){
37255             this.collapseBtn.show();
37256         }
37257         this.closeBtn.setStyle('display', this.closeBtnState);
37258         if(this.stickBtn){
37259             this.stickBtn.hide();
37260         }
37261         this.fireEvent("slidehide", this);
37262     },
37263
37264     slideIn : function(cb){
37265         if(!this.isSlid || this.el.hasActiveFx()){
37266             Roo.callback(cb);
37267             return;
37268         }
37269         this.isSlid = false;
37270         this.beforeSlide();
37271         this.el.slideOut(this.getSlideAnchor(), {
37272             callback: function(){
37273                 this.el.setLeftTop(-10000, -10000);
37274                 this.afterSlide();
37275                 this.afterSlideIn();
37276                 Roo.callback(cb);
37277             },
37278             scope: this,
37279             block: true
37280         });
37281     },
37282     
37283     slideInIf : function(e){
37284         if(!e.within(this.el)){
37285             this.slideIn();
37286         }
37287     },
37288
37289     animateCollapse : function(){
37290         this.beforeSlide();
37291         this.el.setStyle("z-index", 20000);
37292         var anchor = this.getSlideAnchor();
37293         this.el.slideOut(anchor, {
37294             callback : function(){
37295                 this.el.setStyle("z-index", "");
37296                 this.collapsedEl.slideIn(anchor, {duration:.3});
37297                 this.afterSlide();
37298                 this.el.setLocation(-10000,-10000);
37299                 this.el.hide();
37300                 this.fireEvent("collapsed", this);
37301             },
37302             scope: this,
37303             block: true
37304         });
37305     },
37306
37307     animateExpand : function(){
37308         this.beforeSlide();
37309         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
37310         this.el.setStyle("z-index", 20000);
37311         this.collapsedEl.hide({
37312             duration:.1
37313         });
37314         this.el.slideIn(this.getSlideAnchor(), {
37315             callback : function(){
37316                 this.el.setStyle("z-index", "");
37317                 this.afterSlide();
37318                 if(this.split){
37319                     this.split.el.show();
37320                 }
37321                 this.fireEvent("invalidated", this);
37322                 this.fireEvent("expanded", this);
37323             },
37324             scope: this,
37325             block: true
37326         });
37327     },
37328
37329     anchors : {
37330         "west" : "left",
37331         "east" : "right",
37332         "north" : "top",
37333         "south" : "bottom"
37334     },
37335
37336     sanchors : {
37337         "west" : "l",
37338         "east" : "r",
37339         "north" : "t",
37340         "south" : "b"
37341     },
37342
37343     canchors : {
37344         "west" : "tl-tr",
37345         "east" : "tr-tl",
37346         "north" : "tl-bl",
37347         "south" : "bl-tl"
37348     },
37349
37350     getAnchor : function(){
37351         return this.anchors[this.position];
37352     },
37353
37354     getCollapseAnchor : function(){
37355         return this.canchors[this.position];
37356     },
37357
37358     getSlideAnchor : function(){
37359         return this.sanchors[this.position];
37360     },
37361
37362     getAlignAdj : function(){
37363         var cm = this.cmargins;
37364         switch(this.position){
37365             case "west":
37366                 return [0, 0];
37367             break;
37368             case "east":
37369                 return [0, 0];
37370             break;
37371             case "north":
37372                 return [0, 0];
37373             break;
37374             case "south":
37375                 return [0, 0];
37376             break;
37377         }
37378     },
37379
37380     getExpandAdj : function(){
37381         var c = this.collapsedEl, cm = this.cmargins;
37382         switch(this.position){
37383             case "west":
37384                 return [-(cm.right+c.getWidth()+cm.left), 0];
37385             break;
37386             case "east":
37387                 return [cm.right+c.getWidth()+cm.left, 0];
37388             break;
37389             case "north":
37390                 return [0, -(cm.top+cm.bottom+c.getHeight())];
37391             break;
37392             case "south":
37393                 return [0, cm.top+cm.bottom+c.getHeight()];
37394             break;
37395         }
37396     }
37397 });/*
37398  * Based on:
37399  * Ext JS Library 1.1.1
37400  * Copyright(c) 2006-2007, Ext JS, LLC.
37401  *
37402  * Originally Released Under LGPL - original licence link has changed is not relivant.
37403  *
37404  * Fork - LGPL
37405  * <script type="text/javascript">
37406  */
37407 /*
37408  * These classes are private internal classes
37409  */
37410 Roo.bootstrap.layout.Center = function(config){
37411     config.region = "center";
37412     Roo.bootstrap.layout.Region.call(this, config);
37413     this.visible = true;
37414     this.minWidth = config.minWidth || 20;
37415     this.minHeight = config.minHeight || 20;
37416 };
37417
37418 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37419     hide : function(){
37420         // center panel can't be hidden
37421     },
37422     
37423     show : function(){
37424         // center panel can't be hidden
37425     },
37426     
37427     getMinWidth: function(){
37428         return this.minWidth;
37429     },
37430     
37431     getMinHeight: function(){
37432         return this.minHeight;
37433     }
37434 });
37435
37436
37437
37438
37439  
37440
37441
37442
37443
37444
37445
37446 Roo.bootstrap.layout.North = function(config)
37447 {
37448     config.region = 'north';
37449     config.cursor = 'n-resize';
37450     
37451     Roo.bootstrap.layout.Split.call(this, config);
37452     
37453     
37454     if(this.split){
37455         this.split.placement = Roo.bootstrap.SplitBar.TOP;
37456         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37457         this.split.el.addClass("roo-layout-split-v");
37458     }
37459     var size = config.initialSize || config.height;
37460     if(typeof size != "undefined"){
37461         this.el.setHeight(size);
37462     }
37463 };
37464 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37465 {
37466     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37467     
37468     
37469     
37470     getBox : function(){
37471         if(this.collapsed){
37472             return this.collapsedEl.getBox();
37473         }
37474         var box = this.el.getBox();
37475         if(this.split){
37476             box.height += this.split.el.getHeight();
37477         }
37478         return box;
37479     },
37480     
37481     updateBox : function(box){
37482         if(this.split && !this.collapsed){
37483             box.height -= this.split.el.getHeight();
37484             this.split.el.setLeft(box.x);
37485             this.split.el.setTop(box.y+box.height);
37486             this.split.el.setWidth(box.width);
37487         }
37488         if(this.collapsed){
37489             this.updateBody(box.width, null);
37490         }
37491         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37492     }
37493 });
37494
37495
37496
37497
37498
37499 Roo.bootstrap.layout.South = function(config){
37500     config.region = 'south';
37501     config.cursor = 's-resize';
37502     Roo.bootstrap.layout.Split.call(this, config);
37503     if(this.split){
37504         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37505         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37506         this.split.el.addClass("roo-layout-split-v");
37507     }
37508     var size = config.initialSize || config.height;
37509     if(typeof size != "undefined"){
37510         this.el.setHeight(size);
37511     }
37512 };
37513
37514 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37515     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37516     getBox : function(){
37517         if(this.collapsed){
37518             return this.collapsedEl.getBox();
37519         }
37520         var box = this.el.getBox();
37521         if(this.split){
37522             var sh = this.split.el.getHeight();
37523             box.height += sh;
37524             box.y -= sh;
37525         }
37526         return box;
37527     },
37528     
37529     updateBox : function(box){
37530         if(this.split && !this.collapsed){
37531             var sh = this.split.el.getHeight();
37532             box.height -= sh;
37533             box.y += sh;
37534             this.split.el.setLeft(box.x);
37535             this.split.el.setTop(box.y-sh);
37536             this.split.el.setWidth(box.width);
37537         }
37538         if(this.collapsed){
37539             this.updateBody(box.width, null);
37540         }
37541         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37542     }
37543 });
37544
37545 Roo.bootstrap.layout.East = function(config){
37546     config.region = "east";
37547     config.cursor = "e-resize";
37548     Roo.bootstrap.layout.Split.call(this, config);
37549     if(this.split){
37550         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37551         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37552         this.split.el.addClass("roo-layout-split-h");
37553     }
37554     var size = config.initialSize || config.width;
37555     if(typeof size != "undefined"){
37556         this.el.setWidth(size);
37557     }
37558 };
37559 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37560     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37561     getBox : function(){
37562         if(this.collapsed){
37563             return this.collapsedEl.getBox();
37564         }
37565         var box = this.el.getBox();
37566         if(this.split){
37567             var sw = this.split.el.getWidth();
37568             box.width += sw;
37569             box.x -= sw;
37570         }
37571         return box;
37572     },
37573
37574     updateBox : function(box){
37575         if(this.split && !this.collapsed){
37576             var sw = this.split.el.getWidth();
37577             box.width -= sw;
37578             this.split.el.setLeft(box.x);
37579             this.split.el.setTop(box.y);
37580             this.split.el.setHeight(box.height);
37581             box.x += sw;
37582         }
37583         if(this.collapsed){
37584             this.updateBody(null, box.height);
37585         }
37586         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37587     }
37588 });
37589
37590 Roo.bootstrap.layout.West = function(config){
37591     config.region = "west";
37592     config.cursor = "w-resize";
37593     
37594     Roo.bootstrap.layout.Split.call(this, config);
37595     if(this.split){
37596         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37597         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37598         this.split.el.addClass("roo-layout-split-h");
37599     }
37600     
37601 };
37602 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37603     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37604     
37605     onRender: function(ctr, pos)
37606     {
37607         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37608         var size = this.config.initialSize || this.config.width;
37609         if(typeof size != "undefined"){
37610             this.el.setWidth(size);
37611         }
37612     },
37613     
37614     getBox : function(){
37615         if(this.collapsed){
37616             return this.collapsedEl.getBox();
37617         }
37618         var box = this.el.getBox();
37619         if(this.split){
37620             box.width += this.split.el.getWidth();
37621         }
37622         return box;
37623     },
37624     
37625     updateBox : function(box){
37626         if(this.split && !this.collapsed){
37627             var sw = this.split.el.getWidth();
37628             box.width -= sw;
37629             this.split.el.setLeft(box.x+box.width);
37630             this.split.el.setTop(box.y);
37631             this.split.el.setHeight(box.height);
37632         }
37633         if(this.collapsed){
37634             this.updateBody(null, box.height);
37635         }
37636         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37637     }
37638 });Roo.namespace("Roo.bootstrap.panel");/*
37639  * Based on:
37640  * Ext JS Library 1.1.1
37641  * Copyright(c) 2006-2007, Ext JS, LLC.
37642  *
37643  * Originally Released Under LGPL - original licence link has changed is not relivant.
37644  *
37645  * Fork - LGPL
37646  * <script type="text/javascript">
37647  */
37648 /**
37649  * @class Roo.ContentPanel
37650  * @extends Roo.util.Observable
37651  * A basic ContentPanel element.
37652  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37653  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37654  * @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
37655  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37656  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37657  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37658  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37659  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37660  * @cfg {String} title          The title for this panel
37661  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37662  * @cfg {String} url            Calls {@link #setUrl} with this value
37663  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37664  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37665  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37666  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37667  * @cfg {Boolean} badges render the badges
37668
37669  * @constructor
37670  * Create a new ContentPanel.
37671  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37672  * @param {String/Object} config A string to set only the title or a config object
37673  * @param {String} content (optional) Set the HTML content for this panel
37674  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37675  */
37676 Roo.bootstrap.panel.Content = function( config){
37677     
37678     this.tpl = config.tpl || false;
37679     
37680     var el = config.el;
37681     var content = config.content;
37682
37683     if(config.autoCreate){ // xtype is available if this is called from factory
37684         el = Roo.id();
37685     }
37686     this.el = Roo.get(el);
37687     if(!this.el && config && config.autoCreate){
37688         if(typeof config.autoCreate == "object"){
37689             if(!config.autoCreate.id){
37690                 config.autoCreate.id = config.id||el;
37691             }
37692             this.el = Roo.DomHelper.append(document.body,
37693                         config.autoCreate, true);
37694         }else{
37695             var elcfg =  {   tag: "div",
37696                             cls: "roo-layout-inactive-content",
37697                             id: config.id||el
37698                             };
37699             if (config.html) {
37700                 elcfg.html = config.html;
37701                 
37702             }
37703                         
37704             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37705         }
37706     } 
37707     this.closable = false;
37708     this.loaded = false;
37709     this.active = false;
37710    
37711       
37712     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37713         
37714         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37715         
37716         this.wrapEl = this.el; //this.el.wrap();
37717         var ti = [];
37718         if (config.toolbar.items) {
37719             ti = config.toolbar.items ;
37720             delete config.toolbar.items ;
37721         }
37722         
37723         var nitems = [];
37724         this.toolbar.render(this.wrapEl, 'before');
37725         for(var i =0;i < ti.length;i++) {
37726           //  Roo.log(['add child', items[i]]);
37727             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37728         }
37729         this.toolbar.items = nitems;
37730         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37731         delete config.toolbar;
37732         
37733     }
37734     /*
37735     // xtype created footer. - not sure if will work as we normally have to render first..
37736     if (this.footer && !this.footer.el && this.footer.xtype) {
37737         if (!this.wrapEl) {
37738             this.wrapEl = this.el.wrap();
37739         }
37740     
37741         this.footer.container = this.wrapEl.createChild();
37742          
37743         this.footer = Roo.factory(this.footer, Roo);
37744         
37745     }
37746     */
37747     
37748      if(typeof config == "string"){
37749         this.title = config;
37750     }else{
37751         Roo.apply(this, config);
37752     }
37753     
37754     if(this.resizeEl){
37755         this.resizeEl = Roo.get(this.resizeEl, true);
37756     }else{
37757         this.resizeEl = this.el;
37758     }
37759     // handle view.xtype
37760     
37761  
37762     
37763     
37764     this.addEvents({
37765         /**
37766          * @event activate
37767          * Fires when this panel is activated. 
37768          * @param {Roo.ContentPanel} this
37769          */
37770         "activate" : true,
37771         /**
37772          * @event deactivate
37773          * Fires when this panel is activated. 
37774          * @param {Roo.ContentPanel} this
37775          */
37776         "deactivate" : true,
37777
37778         /**
37779          * @event resize
37780          * Fires when this panel is resized if fitToFrame is true.
37781          * @param {Roo.ContentPanel} this
37782          * @param {Number} width The width after any component adjustments
37783          * @param {Number} height The height after any component adjustments
37784          */
37785         "resize" : true,
37786         
37787          /**
37788          * @event render
37789          * Fires when this tab is created
37790          * @param {Roo.ContentPanel} this
37791          */
37792         "render" : true
37793         
37794         
37795         
37796     });
37797     
37798
37799     
37800     
37801     if(this.autoScroll){
37802         this.resizeEl.setStyle("overflow", "auto");
37803     } else {
37804         // fix randome scrolling
37805         //this.el.on('scroll', function() {
37806         //    Roo.log('fix random scolling');
37807         //    this.scrollTo('top',0); 
37808         //});
37809     }
37810     content = content || this.content;
37811     if(content){
37812         this.setContent(content);
37813     }
37814     if(config && config.url){
37815         this.setUrl(this.url, this.params, this.loadOnce);
37816     }
37817     
37818     
37819     
37820     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37821     
37822     if (this.view && typeof(this.view.xtype) != 'undefined') {
37823         this.view.el = this.el.appendChild(document.createElement("div"));
37824         this.view = Roo.factory(this.view); 
37825         this.view.render  &&  this.view.render(false, '');  
37826     }
37827     
37828     
37829     this.fireEvent('render', this);
37830 };
37831
37832 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37833     
37834     tabTip : '',
37835     
37836     setRegion : function(region){
37837         this.region = region;
37838         this.setActiveClass(region && !this.background);
37839     },
37840     
37841     
37842     setActiveClass: function(state)
37843     {
37844         if(state){
37845            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37846            this.el.setStyle('position','relative');
37847         }else{
37848            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37849            this.el.setStyle('position', 'absolute');
37850         } 
37851     },
37852     
37853     /**
37854      * Returns the toolbar for this Panel if one was configured. 
37855      * @return {Roo.Toolbar} 
37856      */
37857     getToolbar : function(){
37858         return this.toolbar;
37859     },
37860     
37861     setActiveState : function(active)
37862     {
37863         this.active = active;
37864         this.setActiveClass(active);
37865         if(!active){
37866             if(this.fireEvent("deactivate", this) === false){
37867                 return false;
37868             }
37869             return true;
37870         }
37871         this.fireEvent("activate", this);
37872         return true;
37873     },
37874     /**
37875      * Updates this panel's element
37876      * @param {String} content The new content
37877      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37878     */
37879     setContent : function(content, loadScripts){
37880         this.el.update(content, loadScripts);
37881     },
37882
37883     ignoreResize : function(w, h){
37884         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37885             return true;
37886         }else{
37887             this.lastSize = {width: w, height: h};
37888             return false;
37889         }
37890     },
37891     /**
37892      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37893      * @return {Roo.UpdateManager} The UpdateManager
37894      */
37895     getUpdateManager : function(){
37896         return this.el.getUpdateManager();
37897     },
37898      /**
37899      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37900      * @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:
37901 <pre><code>
37902 panel.load({
37903     url: "your-url.php",
37904     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37905     callback: yourFunction,
37906     scope: yourObject, //(optional scope)
37907     discardUrl: false,
37908     nocache: false,
37909     text: "Loading...",
37910     timeout: 30,
37911     scripts: false
37912 });
37913 </code></pre>
37914      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37915      * 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.
37916      * @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}
37917      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37918      * @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.
37919      * @return {Roo.ContentPanel} this
37920      */
37921     load : function(){
37922         var um = this.el.getUpdateManager();
37923         um.update.apply(um, arguments);
37924         return this;
37925     },
37926
37927
37928     /**
37929      * 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.
37930      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37931      * @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)
37932      * @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)
37933      * @return {Roo.UpdateManager} The UpdateManager
37934      */
37935     setUrl : function(url, params, loadOnce){
37936         if(this.refreshDelegate){
37937             this.removeListener("activate", this.refreshDelegate);
37938         }
37939         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37940         this.on("activate", this.refreshDelegate);
37941         return this.el.getUpdateManager();
37942     },
37943     
37944     _handleRefresh : function(url, params, loadOnce){
37945         if(!loadOnce || !this.loaded){
37946             var updater = this.el.getUpdateManager();
37947             updater.update(url, params, this._setLoaded.createDelegate(this));
37948         }
37949     },
37950     
37951     _setLoaded : function(){
37952         this.loaded = true;
37953     }, 
37954     
37955     /**
37956      * Returns this panel's id
37957      * @return {String} 
37958      */
37959     getId : function(){
37960         return this.el.id;
37961     },
37962     
37963     /** 
37964      * Returns this panel's element - used by regiosn to add.
37965      * @return {Roo.Element} 
37966      */
37967     getEl : function(){
37968         return this.wrapEl || this.el;
37969     },
37970     
37971    
37972     
37973     adjustForComponents : function(width, height)
37974     {
37975         //Roo.log('adjustForComponents ');
37976         if(this.resizeEl != this.el){
37977             width -= this.el.getFrameWidth('lr');
37978             height -= this.el.getFrameWidth('tb');
37979         }
37980         if(this.toolbar){
37981             var te = this.toolbar.getEl();
37982             te.setWidth(width);
37983             height -= te.getHeight();
37984         }
37985         if(this.footer){
37986             var te = this.footer.getEl();
37987             te.setWidth(width);
37988             height -= te.getHeight();
37989         }
37990         
37991         
37992         if(this.adjustments){
37993             width += this.adjustments[0];
37994             height += this.adjustments[1];
37995         }
37996         return {"width": width, "height": height};
37997     },
37998     
37999     setSize : function(width, height){
38000         if(this.fitToFrame && !this.ignoreResize(width, height)){
38001             if(this.fitContainer && this.resizeEl != this.el){
38002                 this.el.setSize(width, height);
38003             }
38004             var size = this.adjustForComponents(width, height);
38005             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
38006             this.fireEvent('resize', this, size.width, size.height);
38007         }
38008     },
38009     
38010     /**
38011      * Returns this panel's title
38012      * @return {String} 
38013      */
38014     getTitle : function(){
38015         
38016         if (typeof(this.title) != 'object') {
38017             return this.title;
38018         }
38019         
38020         var t = '';
38021         for (var k in this.title) {
38022             if (!this.title.hasOwnProperty(k)) {
38023                 continue;
38024             }
38025             
38026             if (k.indexOf('-') >= 0) {
38027                 var s = k.split('-');
38028                 for (var i = 0; i<s.length; i++) {
38029                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
38030                 }
38031             } else {
38032                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
38033             }
38034         }
38035         return t;
38036     },
38037     
38038     /**
38039      * Set this panel's title
38040      * @param {String} title
38041      */
38042     setTitle : function(title){
38043         this.title = title;
38044         if(this.region){
38045             this.region.updatePanelTitle(this, title);
38046         }
38047     },
38048     
38049     /**
38050      * Returns true is this panel was configured to be closable
38051      * @return {Boolean} 
38052      */
38053     isClosable : function(){
38054         return this.closable;
38055     },
38056     
38057     beforeSlide : function(){
38058         this.el.clip();
38059         this.resizeEl.clip();
38060     },
38061     
38062     afterSlide : function(){
38063         this.el.unclip();
38064         this.resizeEl.unclip();
38065     },
38066     
38067     /**
38068      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
38069      *   Will fail silently if the {@link #setUrl} method has not been called.
38070      *   This does not activate the panel, just updates its content.
38071      */
38072     refresh : function(){
38073         if(this.refreshDelegate){
38074            this.loaded = false;
38075            this.refreshDelegate();
38076         }
38077     },
38078     
38079     /**
38080      * Destroys this panel
38081      */
38082     destroy : function(){
38083         this.el.removeAllListeners();
38084         var tempEl = document.createElement("span");
38085         tempEl.appendChild(this.el.dom);
38086         tempEl.innerHTML = "";
38087         this.el.remove();
38088         this.el = null;
38089     },
38090     
38091     /**
38092      * form - if the content panel contains a form - this is a reference to it.
38093      * @type {Roo.form.Form}
38094      */
38095     form : false,
38096     /**
38097      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
38098      *    This contains a reference to it.
38099      * @type {Roo.View}
38100      */
38101     view : false,
38102     
38103       /**
38104      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
38105      * <pre><code>
38106
38107 layout.addxtype({
38108        xtype : 'Form',
38109        items: [ .... ]
38110    }
38111 );
38112
38113 </code></pre>
38114      * @param {Object} cfg Xtype definition of item to add.
38115      */
38116     
38117     
38118     getChildContainer: function () {
38119         return this.getEl();
38120     }
38121     
38122     
38123     /*
38124         var  ret = new Roo.factory(cfg);
38125         return ret;
38126         
38127         
38128         // add form..
38129         if (cfg.xtype.match(/^Form$/)) {
38130             
38131             var el;
38132             //if (this.footer) {
38133             //    el = this.footer.container.insertSibling(false, 'before');
38134             //} else {
38135                 el = this.el.createChild();
38136             //}
38137
38138             this.form = new  Roo.form.Form(cfg);
38139             
38140             
38141             if ( this.form.allItems.length) {
38142                 this.form.render(el.dom);
38143             }
38144             return this.form;
38145         }
38146         // should only have one of theses..
38147         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
38148             // views.. should not be just added - used named prop 'view''
38149             
38150             cfg.el = this.el.appendChild(document.createElement("div"));
38151             // factory?
38152             
38153             var ret = new Roo.factory(cfg);
38154              
38155              ret.render && ret.render(false, ''); // render blank..
38156             this.view = ret;
38157             return ret;
38158         }
38159         return false;
38160     }
38161     \*/
38162 });
38163  
38164 /**
38165  * @class Roo.bootstrap.panel.Grid
38166  * @extends Roo.bootstrap.panel.Content
38167  * @constructor
38168  * Create a new GridPanel.
38169  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
38170  * @param {Object} config A the config object
38171   
38172  */
38173
38174
38175
38176 Roo.bootstrap.panel.Grid = function(config)
38177 {
38178     
38179       
38180     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
38181         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
38182
38183     config.el = this.wrapper;
38184     //this.el = this.wrapper;
38185     
38186       if (config.container) {
38187         // ctor'ed from a Border/panel.grid
38188         
38189         
38190         this.wrapper.setStyle("overflow", "hidden");
38191         this.wrapper.addClass('roo-grid-container');
38192
38193     }
38194     
38195     
38196     if(config.toolbar){
38197         var tool_el = this.wrapper.createChild();    
38198         this.toolbar = Roo.factory(config.toolbar);
38199         var ti = [];
38200         if (config.toolbar.items) {
38201             ti = config.toolbar.items ;
38202             delete config.toolbar.items ;
38203         }
38204         
38205         var nitems = [];
38206         this.toolbar.render(tool_el);
38207         for(var i =0;i < ti.length;i++) {
38208           //  Roo.log(['add child', items[i]]);
38209             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
38210         }
38211         this.toolbar.items = nitems;
38212         
38213         delete config.toolbar;
38214     }
38215     
38216     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
38217     config.grid.scrollBody = true;;
38218     config.grid.monitorWindowResize = false; // turn off autosizing
38219     config.grid.autoHeight = false;
38220     config.grid.autoWidth = false;
38221     
38222     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
38223     
38224     if (config.background) {
38225         // render grid on panel activation (if panel background)
38226         this.on('activate', function(gp) {
38227             if (!gp.grid.rendered) {
38228                 gp.grid.render(this.wrapper);
38229                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
38230             }
38231         });
38232             
38233     } else {
38234         this.grid.render(this.wrapper);
38235         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
38236
38237     }
38238     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
38239     // ??? needed ??? config.el = this.wrapper;
38240     
38241     
38242     
38243   
38244     // xtype created footer. - not sure if will work as we normally have to render first..
38245     if (this.footer && !this.footer.el && this.footer.xtype) {
38246         
38247         var ctr = this.grid.getView().getFooterPanel(true);
38248         this.footer.dataSource = this.grid.dataSource;
38249         this.footer = Roo.factory(this.footer, Roo);
38250         this.footer.render(ctr);
38251         
38252     }
38253     
38254     
38255     
38256     
38257      
38258 };
38259
38260 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
38261     getId : function(){
38262         return this.grid.id;
38263     },
38264     
38265     /**
38266      * Returns the grid for this panel
38267      * @return {Roo.bootstrap.Table} 
38268      */
38269     getGrid : function(){
38270         return this.grid;    
38271     },
38272     
38273     setSize : function(width, height){
38274         if(!this.ignoreResize(width, height)){
38275             var grid = this.grid;
38276             var size = this.adjustForComponents(width, height);
38277             var gridel = grid.getGridEl();
38278             gridel.setSize(size.width, size.height);
38279             /*
38280             var thd = grid.getGridEl().select('thead',true).first();
38281             var tbd = grid.getGridEl().select('tbody', true).first();
38282             if (tbd) {
38283                 tbd.setSize(width, height - thd.getHeight());
38284             }
38285             */
38286             grid.autoSize();
38287         }
38288     },
38289      
38290     
38291     
38292     beforeSlide : function(){
38293         this.grid.getView().scroller.clip();
38294     },
38295     
38296     afterSlide : function(){
38297         this.grid.getView().scroller.unclip();
38298     },
38299     
38300     destroy : function(){
38301         this.grid.destroy();
38302         delete this.grid;
38303         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
38304     }
38305 });
38306
38307 /**
38308  * @class Roo.bootstrap.panel.Nest
38309  * @extends Roo.bootstrap.panel.Content
38310  * @constructor
38311  * Create a new Panel, that can contain a layout.Border.
38312  * 
38313  * 
38314  * @param {Roo.BorderLayout} layout The layout for this panel
38315  * @param {String/Object} config A string to set only the title or a config object
38316  */
38317 Roo.bootstrap.panel.Nest = function(config)
38318 {
38319     // construct with only one argument..
38320     /* FIXME - implement nicer consturctors
38321     if (layout.layout) {
38322         config = layout;
38323         layout = config.layout;
38324         delete config.layout;
38325     }
38326     if (layout.xtype && !layout.getEl) {
38327         // then layout needs constructing..
38328         layout = Roo.factory(layout, Roo);
38329     }
38330     */
38331     
38332     config.el =  config.layout.getEl();
38333     
38334     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
38335     
38336     config.layout.monitorWindowResize = false; // turn off autosizing
38337     this.layout = config.layout;
38338     this.layout.getEl().addClass("roo-layout-nested-layout");
38339     this.layout.parent = this;
38340     
38341     
38342     
38343     
38344 };
38345
38346 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
38347
38348     setSize : function(width, height){
38349         if(!this.ignoreResize(width, height)){
38350             var size = this.adjustForComponents(width, height);
38351             var el = this.layout.getEl();
38352             if (size.height < 1) {
38353                 el.setWidth(size.width);   
38354             } else {
38355                 el.setSize(size.width, size.height);
38356             }
38357             var touch = el.dom.offsetWidth;
38358             this.layout.layout();
38359             // ie requires a double layout on the first pass
38360             if(Roo.isIE && !this.initialized){
38361                 this.initialized = true;
38362                 this.layout.layout();
38363             }
38364         }
38365     },
38366     
38367     // activate all subpanels if not currently active..
38368     
38369     setActiveState : function(active){
38370         this.active = active;
38371         this.setActiveClass(active);
38372         
38373         if(!active){
38374             this.fireEvent("deactivate", this);
38375             return;
38376         }
38377         
38378         this.fireEvent("activate", this);
38379         // not sure if this should happen before or after..
38380         if (!this.layout) {
38381             return; // should not happen..
38382         }
38383         var reg = false;
38384         for (var r in this.layout.regions) {
38385             reg = this.layout.getRegion(r);
38386             if (reg.getActivePanel()) {
38387                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
38388                 reg.setActivePanel(reg.getActivePanel());
38389                 continue;
38390             }
38391             if (!reg.panels.length) {
38392                 continue;
38393             }
38394             reg.showPanel(reg.getPanel(0));
38395         }
38396         
38397         
38398         
38399         
38400     },
38401     
38402     /**
38403      * Returns the nested BorderLayout for this panel
38404      * @return {Roo.BorderLayout} 
38405      */
38406     getLayout : function(){
38407         return this.layout;
38408     },
38409     
38410      /**
38411      * Adds a xtype elements to the layout of the nested panel
38412      * <pre><code>
38413
38414 panel.addxtype({
38415        xtype : 'ContentPanel',
38416        region: 'west',
38417        items: [ .... ]
38418    }
38419 );
38420
38421 panel.addxtype({
38422         xtype : 'NestedLayoutPanel',
38423         region: 'west',
38424         layout: {
38425            center: { },
38426            west: { }   
38427         },
38428         items : [ ... list of content panels or nested layout panels.. ]
38429    }
38430 );
38431 </code></pre>
38432      * @param {Object} cfg Xtype definition of item to add.
38433      */
38434     addxtype : function(cfg) {
38435         return this.layout.addxtype(cfg);
38436     
38437     }
38438 });/*
38439  * Based on:
38440  * Ext JS Library 1.1.1
38441  * Copyright(c) 2006-2007, Ext JS, LLC.
38442  *
38443  * Originally Released Under LGPL - original licence link has changed is not relivant.
38444  *
38445  * Fork - LGPL
38446  * <script type="text/javascript">
38447  */
38448 /**
38449  * @class Roo.TabPanel
38450  * @extends Roo.util.Observable
38451  * A lightweight tab container.
38452  * <br><br>
38453  * Usage:
38454  * <pre><code>
38455 // basic tabs 1, built from existing content
38456 var tabs = new Roo.TabPanel("tabs1");
38457 tabs.addTab("script", "View Script");
38458 tabs.addTab("markup", "View Markup");
38459 tabs.activate("script");
38460
38461 // more advanced tabs, built from javascript
38462 var jtabs = new Roo.TabPanel("jtabs");
38463 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38464
38465 // set up the UpdateManager
38466 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38467 var updater = tab2.getUpdateManager();
38468 updater.setDefaultUrl("ajax1.htm");
38469 tab2.on('activate', updater.refresh, updater, true);
38470
38471 // Use setUrl for Ajax loading
38472 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38473 tab3.setUrl("ajax2.htm", null, true);
38474
38475 // Disabled tab
38476 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38477 tab4.disable();
38478
38479 jtabs.activate("jtabs-1");
38480  * </code></pre>
38481  * @constructor
38482  * Create a new TabPanel.
38483  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38484  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38485  */
38486 Roo.bootstrap.panel.Tabs = function(config){
38487     /**
38488     * The container element for this TabPanel.
38489     * @type Roo.Element
38490     */
38491     this.el = Roo.get(config.el);
38492     delete config.el;
38493     if(config){
38494         if(typeof config == "boolean"){
38495             this.tabPosition = config ? "bottom" : "top";
38496         }else{
38497             Roo.apply(this, config);
38498         }
38499     }
38500     
38501     if(this.tabPosition == "bottom"){
38502         // if tabs are at the bottom = create the body first.
38503         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38504         this.el.addClass("roo-tabs-bottom");
38505     }
38506     // next create the tabs holders
38507     
38508     if (this.tabPosition == "west"){
38509         
38510         var reg = this.region; // fake it..
38511         while (reg) {
38512             if (!reg.mgr.parent) {
38513                 break;
38514             }
38515             reg = reg.mgr.parent.region;
38516         }
38517         Roo.log("got nest?");
38518         Roo.log(reg);
38519         if (reg.mgr.getRegion('west')) {
38520             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38521             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38522             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38523             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38524             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38525         
38526             
38527         }
38528         
38529         
38530     } else {
38531      
38532         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38533         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38534         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38535         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38536     }
38537     
38538     
38539     if(Roo.isIE){
38540         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38541     }
38542     
38543     // finally - if tabs are at the top, then create the body last..
38544     if(this.tabPosition != "bottom"){
38545         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38546          * @type Roo.Element
38547          */
38548         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38549         this.el.addClass("roo-tabs-top");
38550     }
38551     this.items = [];
38552
38553     this.bodyEl.setStyle("position", "relative");
38554
38555     this.active = null;
38556     this.activateDelegate = this.activate.createDelegate(this);
38557
38558     this.addEvents({
38559         /**
38560          * @event tabchange
38561          * Fires when the active tab changes
38562          * @param {Roo.TabPanel} this
38563          * @param {Roo.TabPanelItem} activePanel The new active tab
38564          */
38565         "tabchange": true,
38566         /**
38567          * @event beforetabchange
38568          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38569          * @param {Roo.TabPanel} this
38570          * @param {Object} e Set cancel to true on this object to cancel the tab change
38571          * @param {Roo.TabPanelItem} tab The tab being changed to
38572          */
38573         "beforetabchange" : true
38574     });
38575
38576     Roo.EventManager.onWindowResize(this.onResize, this);
38577     this.cpad = this.el.getPadding("lr");
38578     this.hiddenCount = 0;
38579
38580
38581     // toolbar on the tabbar support...
38582     if (this.toolbar) {
38583         alert("no toolbar support yet");
38584         this.toolbar  = false;
38585         /*
38586         var tcfg = this.toolbar;
38587         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38588         this.toolbar = new Roo.Toolbar(tcfg);
38589         if (Roo.isSafari) {
38590             var tbl = tcfg.container.child('table', true);
38591             tbl.setAttribute('width', '100%');
38592         }
38593         */
38594         
38595     }
38596    
38597
38598
38599     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38600 };
38601
38602 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38603     /*
38604      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38605      */
38606     tabPosition : "top",
38607     /*
38608      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38609      */
38610     currentTabWidth : 0,
38611     /*
38612      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38613      */
38614     minTabWidth : 40,
38615     /*
38616      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38617      */
38618     maxTabWidth : 250,
38619     /*
38620      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38621      */
38622     preferredTabWidth : 175,
38623     /*
38624      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38625      */
38626     resizeTabs : false,
38627     /*
38628      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38629      */
38630     monitorResize : true,
38631     /*
38632      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38633      */
38634     toolbar : false,  // set by caller..
38635     
38636     region : false, /// set by caller
38637     
38638     disableTooltips : true, // not used yet...
38639
38640     /**
38641      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38642      * @param {String} id The id of the div to use <b>or create</b>
38643      * @param {String} text The text for the tab
38644      * @param {String} content (optional) Content to put in the TabPanelItem body
38645      * @param {Boolean} closable (optional) True to create a close icon on the tab
38646      * @return {Roo.TabPanelItem} The created TabPanelItem
38647      */
38648     addTab : function(id, text, content, closable, tpl)
38649     {
38650         var item = new Roo.bootstrap.panel.TabItem({
38651             panel: this,
38652             id : id,
38653             text : text,
38654             closable : closable,
38655             tpl : tpl
38656         });
38657         this.addTabItem(item);
38658         if(content){
38659             item.setContent(content);
38660         }
38661         return item;
38662     },
38663
38664     /**
38665      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38666      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38667      * @return {Roo.TabPanelItem}
38668      */
38669     getTab : function(id){
38670         return this.items[id];
38671     },
38672
38673     /**
38674      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38675      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38676      */
38677     hideTab : function(id){
38678         var t = this.items[id];
38679         if(!t.isHidden()){
38680            t.setHidden(true);
38681            this.hiddenCount++;
38682            this.autoSizeTabs();
38683         }
38684     },
38685
38686     /**
38687      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38688      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38689      */
38690     unhideTab : function(id){
38691         var t = this.items[id];
38692         if(t.isHidden()){
38693            t.setHidden(false);
38694            this.hiddenCount--;
38695            this.autoSizeTabs();
38696         }
38697     },
38698
38699     /**
38700      * Adds an existing {@link Roo.TabPanelItem}.
38701      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38702      */
38703     addTabItem : function(item)
38704     {
38705         this.items[item.id] = item;
38706         this.items.push(item);
38707         this.autoSizeTabs();
38708       //  if(this.resizeTabs){
38709     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38710   //         this.autoSizeTabs();
38711 //        }else{
38712 //            item.autoSize();
38713        // }
38714     },
38715
38716     /**
38717      * Removes a {@link Roo.TabPanelItem}.
38718      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38719      */
38720     removeTab : function(id){
38721         var items = this.items;
38722         var tab = items[id];
38723         if(!tab) { return; }
38724         var index = items.indexOf(tab);
38725         if(this.active == tab && items.length > 1){
38726             var newTab = this.getNextAvailable(index);
38727             if(newTab) {
38728                 newTab.activate();
38729             }
38730         }
38731         this.stripEl.dom.removeChild(tab.pnode.dom);
38732         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38733             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38734         }
38735         items.splice(index, 1);
38736         delete this.items[tab.id];
38737         tab.fireEvent("close", tab);
38738         tab.purgeListeners();
38739         this.autoSizeTabs();
38740     },
38741
38742     getNextAvailable : function(start){
38743         var items = this.items;
38744         var index = start;
38745         // look for a next tab that will slide over to
38746         // replace the one being removed
38747         while(index < items.length){
38748             var item = items[++index];
38749             if(item && !item.isHidden()){
38750                 return item;
38751             }
38752         }
38753         // if one isn't found select the previous tab (on the left)
38754         index = start;
38755         while(index >= 0){
38756             var item = items[--index];
38757             if(item && !item.isHidden()){
38758                 return item;
38759             }
38760         }
38761         return null;
38762     },
38763
38764     /**
38765      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38766      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38767      */
38768     disableTab : function(id){
38769         var tab = this.items[id];
38770         if(tab && this.active != tab){
38771             tab.disable();
38772         }
38773     },
38774
38775     /**
38776      * Enables a {@link Roo.TabPanelItem} that is disabled.
38777      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38778      */
38779     enableTab : function(id){
38780         var tab = this.items[id];
38781         tab.enable();
38782     },
38783
38784     /**
38785      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38786      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38787      * @return {Roo.TabPanelItem} The TabPanelItem.
38788      */
38789     activate : function(id)
38790     {
38791         //Roo.log('activite:'  + id);
38792         
38793         var tab = this.items[id];
38794         if(!tab){
38795             return null;
38796         }
38797         if(tab == this.active || tab.disabled){
38798             return tab;
38799         }
38800         var e = {};
38801         this.fireEvent("beforetabchange", this, e, tab);
38802         if(e.cancel !== true && !tab.disabled){
38803             if(this.active){
38804                 this.active.hide();
38805             }
38806             this.active = this.items[id];
38807             this.active.show();
38808             this.fireEvent("tabchange", this, this.active);
38809         }
38810         return tab;
38811     },
38812
38813     /**
38814      * Gets the active {@link Roo.TabPanelItem}.
38815      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38816      */
38817     getActiveTab : function(){
38818         return this.active;
38819     },
38820
38821     /**
38822      * Updates the tab body element to fit the height of the container element
38823      * for overflow scrolling
38824      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38825      */
38826     syncHeight : function(targetHeight){
38827         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38828         var bm = this.bodyEl.getMargins();
38829         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38830         this.bodyEl.setHeight(newHeight);
38831         return newHeight;
38832     },
38833
38834     onResize : function(){
38835         if(this.monitorResize){
38836             this.autoSizeTabs();
38837         }
38838     },
38839
38840     /**
38841      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38842      */
38843     beginUpdate : function(){
38844         this.updating = true;
38845     },
38846
38847     /**
38848      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38849      */
38850     endUpdate : function(){
38851         this.updating = false;
38852         this.autoSizeTabs();
38853     },
38854
38855     /**
38856      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38857      */
38858     autoSizeTabs : function()
38859     {
38860         var count = this.items.length;
38861         var vcount = count - this.hiddenCount;
38862         
38863         if (vcount < 2) {
38864             this.stripEl.hide();
38865         } else {
38866             this.stripEl.show();
38867         }
38868         
38869         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38870             return;
38871         }
38872         
38873         
38874         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38875         var availWidth = Math.floor(w / vcount);
38876         var b = this.stripBody;
38877         if(b.getWidth() > w){
38878             var tabs = this.items;
38879             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38880             if(availWidth < this.minTabWidth){
38881                 /*if(!this.sleft){    // incomplete scrolling code
38882                     this.createScrollButtons();
38883                 }
38884                 this.showScroll();
38885                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38886             }
38887         }else{
38888             if(this.currentTabWidth < this.preferredTabWidth){
38889                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38890             }
38891         }
38892     },
38893
38894     /**
38895      * Returns the number of tabs in this TabPanel.
38896      * @return {Number}
38897      */
38898      getCount : function(){
38899          return this.items.length;
38900      },
38901
38902     /**
38903      * Resizes all the tabs to the passed width
38904      * @param {Number} The new width
38905      */
38906     setTabWidth : function(width){
38907         this.currentTabWidth = width;
38908         for(var i = 0, len = this.items.length; i < len; i++) {
38909                 if(!this.items[i].isHidden()) {
38910                 this.items[i].setWidth(width);
38911             }
38912         }
38913     },
38914
38915     /**
38916      * Destroys this TabPanel
38917      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38918      */
38919     destroy : function(removeEl){
38920         Roo.EventManager.removeResizeListener(this.onResize, this);
38921         for(var i = 0, len = this.items.length; i < len; i++){
38922             this.items[i].purgeListeners();
38923         }
38924         if(removeEl === true){
38925             this.el.update("");
38926             this.el.remove();
38927         }
38928     },
38929     
38930     createStrip : function(container)
38931     {
38932         var strip = document.createElement("nav");
38933         strip.className = Roo.bootstrap.version == 4 ?
38934             "navbar-light bg-light" : 
38935             "navbar navbar-default"; //"x-tabs-wrap";
38936         container.appendChild(strip);
38937         return strip;
38938     },
38939     
38940     createStripList : function(strip)
38941     {
38942         // div wrapper for retard IE
38943         // returns the "tr" element.
38944         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38945         //'<div class="x-tabs-strip-wrap">'+
38946           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38947           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38948         return strip.firstChild; //.firstChild.firstChild.firstChild;
38949     },
38950     createBody : function(container)
38951     {
38952         var body = document.createElement("div");
38953         Roo.id(body, "tab-body");
38954         //Roo.fly(body).addClass("x-tabs-body");
38955         Roo.fly(body).addClass("tab-content");
38956         container.appendChild(body);
38957         return body;
38958     },
38959     createItemBody :function(bodyEl, id){
38960         var body = Roo.getDom(id);
38961         if(!body){
38962             body = document.createElement("div");
38963             body.id = id;
38964         }
38965         //Roo.fly(body).addClass("x-tabs-item-body");
38966         Roo.fly(body).addClass("tab-pane");
38967          bodyEl.insertBefore(body, bodyEl.firstChild);
38968         return body;
38969     },
38970     /** @private */
38971     createStripElements :  function(stripEl, text, closable, tpl)
38972     {
38973         var td = document.createElement("li"); // was td..
38974         td.className = 'nav-item';
38975         
38976         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38977         
38978         
38979         stripEl.appendChild(td);
38980         /*if(closable){
38981             td.className = "x-tabs-closable";
38982             if(!this.closeTpl){
38983                 this.closeTpl = new Roo.Template(
38984                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38985                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38986                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38987                 );
38988             }
38989             var el = this.closeTpl.overwrite(td, {"text": text});
38990             var close = el.getElementsByTagName("div")[0];
38991             var inner = el.getElementsByTagName("em")[0];
38992             return {"el": el, "close": close, "inner": inner};
38993         } else {
38994         */
38995         // not sure what this is..
38996 //            if(!this.tabTpl){
38997                 //this.tabTpl = new Roo.Template(
38998                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38999                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
39000                 //);
39001 //                this.tabTpl = new Roo.Template(
39002 //                   '<a href="#">' +
39003 //                   '<span unselectable="on"' +
39004 //                            (this.disableTooltips ? '' : ' title="{text}"') +
39005 //                            ' >{text}</span></a>'
39006 //                );
39007 //                
39008 //            }
39009
39010
39011             var template = tpl || this.tabTpl || false;
39012             
39013             if(!template){
39014                 template =  new Roo.Template(
39015                         Roo.bootstrap.version == 4 ? 
39016                             (
39017                                 '<a class="nav-link" href="#" unselectable="on"' +
39018                                      (this.disableTooltips ? '' : ' title="{text}"') +
39019                                      ' >{text}</a>'
39020                             ) : (
39021                                 '<a class="nav-link" href="#">' +
39022                                 '<span unselectable="on"' +
39023                                          (this.disableTooltips ? '' : ' title="{text}"') +
39024                                     ' >{text}</span></a>'
39025                             )
39026                 );
39027             }
39028             
39029             switch (typeof(template)) {
39030                 case 'object' :
39031                     break;
39032                 case 'string' :
39033                     template = new Roo.Template(template);
39034                     break;
39035                 default :
39036                     break;
39037             }
39038             
39039             var el = template.overwrite(td, {"text": text});
39040             
39041             var inner = el.getElementsByTagName("span")[0];
39042             
39043             return {"el": el, "inner": inner};
39044             
39045     }
39046         
39047     
39048 });
39049
39050 /**
39051  * @class Roo.TabPanelItem
39052  * @extends Roo.util.Observable
39053  * Represents an individual item (tab plus body) in a TabPanel.
39054  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
39055  * @param {String} id The id of this TabPanelItem
39056  * @param {String} text The text for the tab of this TabPanelItem
39057  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
39058  */
39059 Roo.bootstrap.panel.TabItem = function(config){
39060     /**
39061      * The {@link Roo.TabPanel} this TabPanelItem belongs to
39062      * @type Roo.TabPanel
39063      */
39064     this.tabPanel = config.panel;
39065     /**
39066      * The id for this TabPanelItem
39067      * @type String
39068      */
39069     this.id = config.id;
39070     /** @private */
39071     this.disabled = false;
39072     /** @private */
39073     this.text = config.text;
39074     /** @private */
39075     this.loaded = false;
39076     this.closable = config.closable;
39077
39078     /**
39079      * The body element for this TabPanelItem.
39080      * @type Roo.Element
39081      */
39082     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
39083     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
39084     this.bodyEl.setStyle("display", "block");
39085     this.bodyEl.setStyle("zoom", "1");
39086     //this.hideAction();
39087
39088     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
39089     /** @private */
39090     this.el = Roo.get(els.el);
39091     this.inner = Roo.get(els.inner, true);
39092      this.textEl = Roo.bootstrap.version == 4 ?
39093         this.el : Roo.get(this.el.dom.firstChild, true);
39094
39095     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
39096     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
39097
39098     
39099 //    this.el.on("mousedown", this.onTabMouseDown, this);
39100     this.el.on("click", this.onTabClick, this);
39101     /** @private */
39102     if(config.closable){
39103         var c = Roo.get(els.close, true);
39104         c.dom.title = this.closeText;
39105         c.addClassOnOver("close-over");
39106         c.on("click", this.closeClick, this);
39107      }
39108
39109     this.addEvents({
39110          /**
39111          * @event activate
39112          * Fires when this tab becomes the active tab.
39113          * @param {Roo.TabPanel} tabPanel The parent TabPanel
39114          * @param {Roo.TabPanelItem} this
39115          */
39116         "activate": true,
39117         /**
39118          * @event beforeclose
39119          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
39120          * @param {Roo.TabPanelItem} this
39121          * @param {Object} e Set cancel to true on this object to cancel the close.
39122          */
39123         "beforeclose": true,
39124         /**
39125          * @event close
39126          * Fires when this tab is closed.
39127          * @param {Roo.TabPanelItem} this
39128          */
39129          "close": true,
39130         /**
39131          * @event deactivate
39132          * Fires when this tab is no longer the active tab.
39133          * @param {Roo.TabPanel} tabPanel The parent TabPanel
39134          * @param {Roo.TabPanelItem} this
39135          */
39136          "deactivate" : true
39137     });
39138     this.hidden = false;
39139
39140     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
39141 };
39142
39143 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
39144            {
39145     purgeListeners : function(){
39146        Roo.util.Observable.prototype.purgeListeners.call(this);
39147        this.el.removeAllListeners();
39148     },
39149     /**
39150      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
39151      */
39152     show : function(){
39153         this.status_node.addClass("active");
39154         this.showAction();
39155         if(Roo.isOpera){
39156             this.tabPanel.stripWrap.repaint();
39157         }
39158         this.fireEvent("activate", this.tabPanel, this);
39159     },
39160
39161     /**
39162      * Returns true if this tab is the active tab.
39163      * @return {Boolean}
39164      */
39165     isActive : function(){
39166         return this.tabPanel.getActiveTab() == this;
39167     },
39168
39169     /**
39170      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
39171      */
39172     hide : function(){
39173         this.status_node.removeClass("active");
39174         this.hideAction();
39175         this.fireEvent("deactivate", this.tabPanel, this);
39176     },
39177
39178     hideAction : function(){
39179         this.bodyEl.hide();
39180         this.bodyEl.setStyle("position", "absolute");
39181         this.bodyEl.setLeft("-20000px");
39182         this.bodyEl.setTop("-20000px");
39183     },
39184
39185     showAction : function(){
39186         this.bodyEl.setStyle("position", "relative");
39187         this.bodyEl.setTop("");
39188         this.bodyEl.setLeft("");
39189         this.bodyEl.show();
39190     },
39191
39192     /**
39193      * Set the tooltip for the tab.
39194      * @param {String} tooltip The tab's tooltip
39195      */
39196     setTooltip : function(text){
39197         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
39198             this.textEl.dom.qtip = text;
39199             this.textEl.dom.removeAttribute('title');
39200         }else{
39201             this.textEl.dom.title = text;
39202         }
39203     },
39204
39205     onTabClick : function(e){
39206         e.preventDefault();
39207         this.tabPanel.activate(this.id);
39208     },
39209
39210     onTabMouseDown : function(e){
39211         e.preventDefault();
39212         this.tabPanel.activate(this.id);
39213     },
39214 /*
39215     getWidth : function(){
39216         return this.inner.getWidth();
39217     },
39218
39219     setWidth : function(width){
39220         var iwidth = width - this.linode.getPadding("lr");
39221         this.inner.setWidth(iwidth);
39222         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
39223         this.linode.setWidth(width);
39224     },
39225 */
39226     /**
39227      * Show or hide the tab
39228      * @param {Boolean} hidden True to hide or false to show.
39229      */
39230     setHidden : function(hidden){
39231         this.hidden = hidden;
39232         this.linode.setStyle("display", hidden ? "none" : "");
39233     },
39234
39235     /**
39236      * Returns true if this tab is "hidden"
39237      * @return {Boolean}
39238      */
39239     isHidden : function(){
39240         return this.hidden;
39241     },
39242
39243     /**
39244      * Returns the text for this tab
39245      * @return {String}
39246      */
39247     getText : function(){
39248         return this.text;
39249     },
39250     /*
39251     autoSize : function(){
39252         //this.el.beginMeasure();
39253         this.textEl.setWidth(1);
39254         /*
39255          *  #2804 [new] Tabs in Roojs
39256          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
39257          */
39258         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
39259         //this.el.endMeasure();
39260     //},
39261
39262     /**
39263      * Sets the text for the tab (Note: this also sets the tooltip text)
39264      * @param {String} text The tab's text and tooltip
39265      */
39266     setText : function(text){
39267         this.text = text;
39268         this.textEl.update(text);
39269         this.setTooltip(text);
39270         //if(!this.tabPanel.resizeTabs){
39271         //    this.autoSize();
39272         //}
39273     },
39274     /**
39275      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
39276      */
39277     activate : function(){
39278         this.tabPanel.activate(this.id);
39279     },
39280
39281     /**
39282      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
39283      */
39284     disable : function(){
39285         if(this.tabPanel.active != this){
39286             this.disabled = true;
39287             this.status_node.addClass("disabled");
39288         }
39289     },
39290
39291     /**
39292      * Enables this TabPanelItem if it was previously disabled.
39293      */
39294     enable : function(){
39295         this.disabled = false;
39296         this.status_node.removeClass("disabled");
39297     },
39298
39299     /**
39300      * Sets the content for this TabPanelItem.
39301      * @param {String} content The content
39302      * @param {Boolean} loadScripts true to look for and load scripts
39303      */
39304     setContent : function(content, loadScripts){
39305         this.bodyEl.update(content, loadScripts);
39306     },
39307
39308     /**
39309      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
39310      * @return {Roo.UpdateManager} The UpdateManager
39311      */
39312     getUpdateManager : function(){
39313         return this.bodyEl.getUpdateManager();
39314     },
39315
39316     /**
39317      * Set a URL to be used to load the content for this TabPanelItem.
39318      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
39319      * @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)
39320      * @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)
39321      * @return {Roo.UpdateManager} The UpdateManager
39322      */
39323     setUrl : function(url, params, loadOnce){
39324         if(this.refreshDelegate){
39325             this.un('activate', this.refreshDelegate);
39326         }
39327         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39328         this.on("activate", this.refreshDelegate);
39329         return this.bodyEl.getUpdateManager();
39330     },
39331
39332     /** @private */
39333     _handleRefresh : function(url, params, loadOnce){
39334         if(!loadOnce || !this.loaded){
39335             var updater = this.bodyEl.getUpdateManager();
39336             updater.update(url, params, this._setLoaded.createDelegate(this));
39337         }
39338     },
39339
39340     /**
39341      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
39342      *   Will fail silently if the setUrl method has not been called.
39343      *   This does not activate the panel, just updates its content.
39344      */
39345     refresh : function(){
39346         if(this.refreshDelegate){
39347            this.loaded = false;
39348            this.refreshDelegate();
39349         }
39350     },
39351
39352     /** @private */
39353     _setLoaded : function(){
39354         this.loaded = true;
39355     },
39356
39357     /** @private */
39358     closeClick : function(e){
39359         var o = {};
39360         e.stopEvent();
39361         this.fireEvent("beforeclose", this, o);
39362         if(o.cancel !== true){
39363             this.tabPanel.removeTab(this.id);
39364         }
39365     },
39366     /**
39367      * The text displayed in the tooltip for the close icon.
39368      * @type String
39369      */
39370     closeText : "Close this tab"
39371 });
39372 /**
39373 *    This script refer to:
39374 *    Title: International Telephone Input
39375 *    Author: Jack O'Connor
39376 *    Code version:  v12.1.12
39377 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39378 **/
39379
39380 Roo.bootstrap.PhoneInputData = function() {
39381     var d = [
39382       [
39383         "Afghanistan (‫افغانستان‬‎)",
39384         "af",
39385         "93"
39386       ],
39387       [
39388         "Albania (Shqipëri)",
39389         "al",
39390         "355"
39391       ],
39392       [
39393         "Algeria (‫الجزائر‬‎)",
39394         "dz",
39395         "213"
39396       ],
39397       [
39398         "American Samoa",
39399         "as",
39400         "1684"
39401       ],
39402       [
39403         "Andorra",
39404         "ad",
39405         "376"
39406       ],
39407       [
39408         "Angola",
39409         "ao",
39410         "244"
39411       ],
39412       [
39413         "Anguilla",
39414         "ai",
39415         "1264"
39416       ],
39417       [
39418         "Antigua and Barbuda",
39419         "ag",
39420         "1268"
39421       ],
39422       [
39423         "Argentina",
39424         "ar",
39425         "54"
39426       ],
39427       [
39428         "Armenia (Հայաստան)",
39429         "am",
39430         "374"
39431       ],
39432       [
39433         "Aruba",
39434         "aw",
39435         "297"
39436       ],
39437       [
39438         "Australia",
39439         "au",
39440         "61",
39441         0
39442       ],
39443       [
39444         "Austria (Österreich)",
39445         "at",
39446         "43"
39447       ],
39448       [
39449         "Azerbaijan (Azərbaycan)",
39450         "az",
39451         "994"
39452       ],
39453       [
39454         "Bahamas",
39455         "bs",
39456         "1242"
39457       ],
39458       [
39459         "Bahrain (‫البحرين‬‎)",
39460         "bh",
39461         "973"
39462       ],
39463       [
39464         "Bangladesh (বাংলাদেশ)",
39465         "bd",
39466         "880"
39467       ],
39468       [
39469         "Barbados",
39470         "bb",
39471         "1246"
39472       ],
39473       [
39474         "Belarus (Беларусь)",
39475         "by",
39476         "375"
39477       ],
39478       [
39479         "Belgium (België)",
39480         "be",
39481         "32"
39482       ],
39483       [
39484         "Belize",
39485         "bz",
39486         "501"
39487       ],
39488       [
39489         "Benin (Bénin)",
39490         "bj",
39491         "229"
39492       ],
39493       [
39494         "Bermuda",
39495         "bm",
39496         "1441"
39497       ],
39498       [
39499         "Bhutan (འབྲུག)",
39500         "bt",
39501         "975"
39502       ],
39503       [
39504         "Bolivia",
39505         "bo",
39506         "591"
39507       ],
39508       [
39509         "Bosnia and Herzegovina (Босна и Херцеговина)",
39510         "ba",
39511         "387"
39512       ],
39513       [
39514         "Botswana",
39515         "bw",
39516         "267"
39517       ],
39518       [
39519         "Brazil (Brasil)",
39520         "br",
39521         "55"
39522       ],
39523       [
39524         "British Indian Ocean Territory",
39525         "io",
39526         "246"
39527       ],
39528       [
39529         "British Virgin Islands",
39530         "vg",
39531         "1284"
39532       ],
39533       [
39534         "Brunei",
39535         "bn",
39536         "673"
39537       ],
39538       [
39539         "Bulgaria (България)",
39540         "bg",
39541         "359"
39542       ],
39543       [
39544         "Burkina Faso",
39545         "bf",
39546         "226"
39547       ],
39548       [
39549         "Burundi (Uburundi)",
39550         "bi",
39551         "257"
39552       ],
39553       [
39554         "Cambodia (កម្ពុជា)",
39555         "kh",
39556         "855"
39557       ],
39558       [
39559         "Cameroon (Cameroun)",
39560         "cm",
39561         "237"
39562       ],
39563       [
39564         "Canada",
39565         "ca",
39566         "1",
39567         1,
39568         ["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"]
39569       ],
39570       [
39571         "Cape Verde (Kabu Verdi)",
39572         "cv",
39573         "238"
39574       ],
39575       [
39576         "Caribbean Netherlands",
39577         "bq",
39578         "599",
39579         1
39580       ],
39581       [
39582         "Cayman Islands",
39583         "ky",
39584         "1345"
39585       ],
39586       [
39587         "Central African Republic (République centrafricaine)",
39588         "cf",
39589         "236"
39590       ],
39591       [
39592         "Chad (Tchad)",
39593         "td",
39594         "235"
39595       ],
39596       [
39597         "Chile",
39598         "cl",
39599         "56"
39600       ],
39601       [
39602         "China (中国)",
39603         "cn",
39604         "86"
39605       ],
39606       [
39607         "Christmas Island",
39608         "cx",
39609         "61",
39610         2
39611       ],
39612       [
39613         "Cocos (Keeling) Islands",
39614         "cc",
39615         "61",
39616         1
39617       ],
39618       [
39619         "Colombia",
39620         "co",
39621         "57"
39622       ],
39623       [
39624         "Comoros (‫جزر القمر‬‎)",
39625         "km",
39626         "269"
39627       ],
39628       [
39629         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39630         "cd",
39631         "243"
39632       ],
39633       [
39634         "Congo (Republic) (Congo-Brazzaville)",
39635         "cg",
39636         "242"
39637       ],
39638       [
39639         "Cook Islands",
39640         "ck",
39641         "682"
39642       ],
39643       [
39644         "Costa Rica",
39645         "cr",
39646         "506"
39647       ],
39648       [
39649         "Côte d’Ivoire",
39650         "ci",
39651         "225"
39652       ],
39653       [
39654         "Croatia (Hrvatska)",
39655         "hr",
39656         "385"
39657       ],
39658       [
39659         "Cuba",
39660         "cu",
39661         "53"
39662       ],
39663       [
39664         "Curaçao",
39665         "cw",
39666         "599",
39667         0
39668       ],
39669       [
39670         "Cyprus (Κύπρος)",
39671         "cy",
39672         "357"
39673       ],
39674       [
39675         "Czech Republic (Česká republika)",
39676         "cz",
39677         "420"
39678       ],
39679       [
39680         "Denmark (Danmark)",
39681         "dk",
39682         "45"
39683       ],
39684       [
39685         "Djibouti",
39686         "dj",
39687         "253"
39688       ],
39689       [
39690         "Dominica",
39691         "dm",
39692         "1767"
39693       ],
39694       [
39695         "Dominican Republic (República Dominicana)",
39696         "do",
39697         "1",
39698         2,
39699         ["809", "829", "849"]
39700       ],
39701       [
39702         "Ecuador",
39703         "ec",
39704         "593"
39705       ],
39706       [
39707         "Egypt (‫مصر‬‎)",
39708         "eg",
39709         "20"
39710       ],
39711       [
39712         "El Salvador",
39713         "sv",
39714         "503"
39715       ],
39716       [
39717         "Equatorial Guinea (Guinea Ecuatorial)",
39718         "gq",
39719         "240"
39720       ],
39721       [
39722         "Eritrea",
39723         "er",
39724         "291"
39725       ],
39726       [
39727         "Estonia (Eesti)",
39728         "ee",
39729         "372"
39730       ],
39731       [
39732         "Ethiopia",
39733         "et",
39734         "251"
39735       ],
39736       [
39737         "Falkland Islands (Islas Malvinas)",
39738         "fk",
39739         "500"
39740       ],
39741       [
39742         "Faroe Islands (Føroyar)",
39743         "fo",
39744         "298"
39745       ],
39746       [
39747         "Fiji",
39748         "fj",
39749         "679"
39750       ],
39751       [
39752         "Finland (Suomi)",
39753         "fi",
39754         "358",
39755         0
39756       ],
39757       [
39758         "France",
39759         "fr",
39760         "33"
39761       ],
39762       [
39763         "French Guiana (Guyane française)",
39764         "gf",
39765         "594"
39766       ],
39767       [
39768         "French Polynesia (Polynésie française)",
39769         "pf",
39770         "689"
39771       ],
39772       [
39773         "Gabon",
39774         "ga",
39775         "241"
39776       ],
39777       [
39778         "Gambia",
39779         "gm",
39780         "220"
39781       ],
39782       [
39783         "Georgia (საქართველო)",
39784         "ge",
39785         "995"
39786       ],
39787       [
39788         "Germany (Deutschland)",
39789         "de",
39790         "49"
39791       ],
39792       [
39793         "Ghana (Gaana)",
39794         "gh",
39795         "233"
39796       ],
39797       [
39798         "Gibraltar",
39799         "gi",
39800         "350"
39801       ],
39802       [
39803         "Greece (Ελλάδα)",
39804         "gr",
39805         "30"
39806       ],
39807       [
39808         "Greenland (Kalaallit Nunaat)",
39809         "gl",
39810         "299"
39811       ],
39812       [
39813         "Grenada",
39814         "gd",
39815         "1473"
39816       ],
39817       [
39818         "Guadeloupe",
39819         "gp",
39820         "590",
39821         0
39822       ],
39823       [
39824         "Guam",
39825         "gu",
39826         "1671"
39827       ],
39828       [
39829         "Guatemala",
39830         "gt",
39831         "502"
39832       ],
39833       [
39834         "Guernsey",
39835         "gg",
39836         "44",
39837         1
39838       ],
39839       [
39840         "Guinea (Guinée)",
39841         "gn",
39842         "224"
39843       ],
39844       [
39845         "Guinea-Bissau (Guiné Bissau)",
39846         "gw",
39847         "245"
39848       ],
39849       [
39850         "Guyana",
39851         "gy",
39852         "592"
39853       ],
39854       [
39855         "Haiti",
39856         "ht",
39857         "509"
39858       ],
39859       [
39860         "Honduras",
39861         "hn",
39862         "504"
39863       ],
39864       [
39865         "Hong Kong (香港)",
39866         "hk",
39867         "852"
39868       ],
39869       [
39870         "Hungary (Magyarország)",
39871         "hu",
39872         "36"
39873       ],
39874       [
39875         "Iceland (Ísland)",
39876         "is",
39877         "354"
39878       ],
39879       [
39880         "India (भारत)",
39881         "in",
39882         "91"
39883       ],
39884       [
39885         "Indonesia",
39886         "id",
39887         "62"
39888       ],
39889       [
39890         "Iran (‫ایران‬‎)",
39891         "ir",
39892         "98"
39893       ],
39894       [
39895         "Iraq (‫العراق‬‎)",
39896         "iq",
39897         "964"
39898       ],
39899       [
39900         "Ireland",
39901         "ie",
39902         "353"
39903       ],
39904       [
39905         "Isle of Man",
39906         "im",
39907         "44",
39908         2
39909       ],
39910       [
39911         "Israel (‫ישראל‬‎)",
39912         "il",
39913         "972"
39914       ],
39915       [
39916         "Italy (Italia)",
39917         "it",
39918         "39",
39919         0
39920       ],
39921       [
39922         "Jamaica",
39923         "jm",
39924         "1876"
39925       ],
39926       [
39927         "Japan (日本)",
39928         "jp",
39929         "81"
39930       ],
39931       [
39932         "Jersey",
39933         "je",
39934         "44",
39935         3
39936       ],
39937       [
39938         "Jordan (‫الأردن‬‎)",
39939         "jo",
39940         "962"
39941       ],
39942       [
39943         "Kazakhstan (Казахстан)",
39944         "kz",
39945         "7",
39946         1
39947       ],
39948       [
39949         "Kenya",
39950         "ke",
39951         "254"
39952       ],
39953       [
39954         "Kiribati",
39955         "ki",
39956         "686"
39957       ],
39958       [
39959         "Kosovo",
39960         "xk",
39961         "383"
39962       ],
39963       [
39964         "Kuwait (‫الكويت‬‎)",
39965         "kw",
39966         "965"
39967       ],
39968       [
39969         "Kyrgyzstan (Кыргызстан)",
39970         "kg",
39971         "996"
39972       ],
39973       [
39974         "Laos (ລາວ)",
39975         "la",
39976         "856"
39977       ],
39978       [
39979         "Latvia (Latvija)",
39980         "lv",
39981         "371"
39982       ],
39983       [
39984         "Lebanon (‫لبنان‬‎)",
39985         "lb",
39986         "961"
39987       ],
39988       [
39989         "Lesotho",
39990         "ls",
39991         "266"
39992       ],
39993       [
39994         "Liberia",
39995         "lr",
39996         "231"
39997       ],
39998       [
39999         "Libya (‫ليبيا‬‎)",
40000         "ly",
40001         "218"
40002       ],
40003       [
40004         "Liechtenstein",
40005         "li",
40006         "423"
40007       ],
40008       [
40009         "Lithuania (Lietuva)",
40010         "lt",
40011         "370"
40012       ],
40013       [
40014         "Luxembourg",
40015         "lu",
40016         "352"
40017       ],
40018       [
40019         "Macau (澳門)",
40020         "mo",
40021         "853"
40022       ],
40023       [
40024         "Macedonia (FYROM) (Македонија)",
40025         "mk",
40026         "389"
40027       ],
40028       [
40029         "Madagascar (Madagasikara)",
40030         "mg",
40031         "261"
40032       ],
40033       [
40034         "Malawi",
40035         "mw",
40036         "265"
40037       ],
40038       [
40039         "Malaysia",
40040         "my",
40041         "60"
40042       ],
40043       [
40044         "Maldives",
40045         "mv",
40046         "960"
40047       ],
40048       [
40049         "Mali",
40050         "ml",
40051         "223"
40052       ],
40053       [
40054         "Malta",
40055         "mt",
40056         "356"
40057       ],
40058       [
40059         "Marshall Islands",
40060         "mh",
40061         "692"
40062       ],
40063       [
40064         "Martinique",
40065         "mq",
40066         "596"
40067       ],
40068       [
40069         "Mauritania (‫موريتانيا‬‎)",
40070         "mr",
40071         "222"
40072       ],
40073       [
40074         "Mauritius (Moris)",
40075         "mu",
40076         "230"
40077       ],
40078       [
40079         "Mayotte",
40080         "yt",
40081         "262",
40082         1
40083       ],
40084       [
40085         "Mexico (México)",
40086         "mx",
40087         "52"
40088       ],
40089       [
40090         "Micronesia",
40091         "fm",
40092         "691"
40093       ],
40094       [
40095         "Moldova (Republica Moldova)",
40096         "md",
40097         "373"
40098       ],
40099       [
40100         "Monaco",
40101         "mc",
40102         "377"
40103       ],
40104       [
40105         "Mongolia (Монгол)",
40106         "mn",
40107         "976"
40108       ],
40109       [
40110         "Montenegro (Crna Gora)",
40111         "me",
40112         "382"
40113       ],
40114       [
40115         "Montserrat",
40116         "ms",
40117         "1664"
40118       ],
40119       [
40120         "Morocco (‫المغرب‬‎)",
40121         "ma",
40122         "212",
40123         0
40124       ],
40125       [
40126         "Mozambique (Moçambique)",
40127         "mz",
40128         "258"
40129       ],
40130       [
40131         "Myanmar (Burma) (မြန်မာ)",
40132         "mm",
40133         "95"
40134       ],
40135       [
40136         "Namibia (Namibië)",
40137         "na",
40138         "264"
40139       ],
40140       [
40141         "Nauru",
40142         "nr",
40143         "674"
40144       ],
40145       [
40146         "Nepal (नेपाल)",
40147         "np",
40148         "977"
40149       ],
40150       [
40151         "Netherlands (Nederland)",
40152         "nl",
40153         "31"
40154       ],
40155       [
40156         "New Caledonia (Nouvelle-Calédonie)",
40157         "nc",
40158         "687"
40159       ],
40160       [
40161         "New Zealand",
40162         "nz",
40163         "64"
40164       ],
40165       [
40166         "Nicaragua",
40167         "ni",
40168         "505"
40169       ],
40170       [
40171         "Niger (Nijar)",
40172         "ne",
40173         "227"
40174       ],
40175       [
40176         "Nigeria",
40177         "ng",
40178         "234"
40179       ],
40180       [
40181         "Niue",
40182         "nu",
40183         "683"
40184       ],
40185       [
40186         "Norfolk Island",
40187         "nf",
40188         "672"
40189       ],
40190       [
40191         "North Korea (조선 민주주의 인민 공화국)",
40192         "kp",
40193         "850"
40194       ],
40195       [
40196         "Northern Mariana Islands",
40197         "mp",
40198         "1670"
40199       ],
40200       [
40201         "Norway (Norge)",
40202         "no",
40203         "47",
40204         0
40205       ],
40206       [
40207         "Oman (‫عُمان‬‎)",
40208         "om",
40209         "968"
40210       ],
40211       [
40212         "Pakistan (‫پاکستان‬‎)",
40213         "pk",
40214         "92"
40215       ],
40216       [
40217         "Palau",
40218         "pw",
40219         "680"
40220       ],
40221       [
40222         "Palestine (‫فلسطين‬‎)",
40223         "ps",
40224         "970"
40225       ],
40226       [
40227         "Panama (Panamá)",
40228         "pa",
40229         "507"
40230       ],
40231       [
40232         "Papua New Guinea",
40233         "pg",
40234         "675"
40235       ],
40236       [
40237         "Paraguay",
40238         "py",
40239         "595"
40240       ],
40241       [
40242         "Peru (Perú)",
40243         "pe",
40244         "51"
40245       ],
40246       [
40247         "Philippines",
40248         "ph",
40249         "63"
40250       ],
40251       [
40252         "Poland (Polska)",
40253         "pl",
40254         "48"
40255       ],
40256       [
40257         "Portugal",
40258         "pt",
40259         "351"
40260       ],
40261       [
40262         "Puerto Rico",
40263         "pr",
40264         "1",
40265         3,
40266         ["787", "939"]
40267       ],
40268       [
40269         "Qatar (‫قطر‬‎)",
40270         "qa",
40271         "974"
40272       ],
40273       [
40274         "Réunion (La Réunion)",
40275         "re",
40276         "262",
40277         0
40278       ],
40279       [
40280         "Romania (România)",
40281         "ro",
40282         "40"
40283       ],
40284       [
40285         "Russia (Россия)",
40286         "ru",
40287         "7",
40288         0
40289       ],
40290       [
40291         "Rwanda",
40292         "rw",
40293         "250"
40294       ],
40295       [
40296         "Saint Barthélemy",
40297         "bl",
40298         "590",
40299         1
40300       ],
40301       [
40302         "Saint Helena",
40303         "sh",
40304         "290"
40305       ],
40306       [
40307         "Saint Kitts and Nevis",
40308         "kn",
40309         "1869"
40310       ],
40311       [
40312         "Saint Lucia",
40313         "lc",
40314         "1758"
40315       ],
40316       [
40317         "Saint Martin (Saint-Martin (partie française))",
40318         "mf",
40319         "590",
40320         2
40321       ],
40322       [
40323         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
40324         "pm",
40325         "508"
40326       ],
40327       [
40328         "Saint Vincent and the Grenadines",
40329         "vc",
40330         "1784"
40331       ],
40332       [
40333         "Samoa",
40334         "ws",
40335         "685"
40336       ],
40337       [
40338         "San Marino",
40339         "sm",
40340         "378"
40341       ],
40342       [
40343         "São Tomé and Príncipe (São Tomé e Príncipe)",
40344         "st",
40345         "239"
40346       ],
40347       [
40348         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
40349         "sa",
40350         "966"
40351       ],
40352       [
40353         "Senegal (Sénégal)",
40354         "sn",
40355         "221"
40356       ],
40357       [
40358         "Serbia (Србија)",
40359         "rs",
40360         "381"
40361       ],
40362       [
40363         "Seychelles",
40364         "sc",
40365         "248"
40366       ],
40367       [
40368         "Sierra Leone",
40369         "sl",
40370         "232"
40371       ],
40372       [
40373         "Singapore",
40374         "sg",
40375         "65"
40376       ],
40377       [
40378         "Sint Maarten",
40379         "sx",
40380         "1721"
40381       ],
40382       [
40383         "Slovakia (Slovensko)",
40384         "sk",
40385         "421"
40386       ],
40387       [
40388         "Slovenia (Slovenija)",
40389         "si",
40390         "386"
40391       ],
40392       [
40393         "Solomon Islands",
40394         "sb",
40395         "677"
40396       ],
40397       [
40398         "Somalia (Soomaaliya)",
40399         "so",
40400         "252"
40401       ],
40402       [
40403         "South Africa",
40404         "za",
40405         "27"
40406       ],
40407       [
40408         "South Korea (대한민국)",
40409         "kr",
40410         "82"
40411       ],
40412       [
40413         "South Sudan (‫جنوب السودان‬‎)",
40414         "ss",
40415         "211"
40416       ],
40417       [
40418         "Spain (España)",
40419         "es",
40420         "34"
40421       ],
40422       [
40423         "Sri Lanka (ශ්‍රී ලංකාව)",
40424         "lk",
40425         "94"
40426       ],
40427       [
40428         "Sudan (‫السودان‬‎)",
40429         "sd",
40430         "249"
40431       ],
40432       [
40433         "Suriname",
40434         "sr",
40435         "597"
40436       ],
40437       [
40438         "Svalbard and Jan Mayen",
40439         "sj",
40440         "47",
40441         1
40442       ],
40443       [
40444         "Swaziland",
40445         "sz",
40446         "268"
40447       ],
40448       [
40449         "Sweden (Sverige)",
40450         "se",
40451         "46"
40452       ],
40453       [
40454         "Switzerland (Schweiz)",
40455         "ch",
40456         "41"
40457       ],
40458       [
40459         "Syria (‫سوريا‬‎)",
40460         "sy",
40461         "963"
40462       ],
40463       [
40464         "Taiwan (台灣)",
40465         "tw",
40466         "886"
40467       ],
40468       [
40469         "Tajikistan",
40470         "tj",
40471         "992"
40472       ],
40473       [
40474         "Tanzania",
40475         "tz",
40476         "255"
40477       ],
40478       [
40479         "Thailand (ไทย)",
40480         "th",
40481         "66"
40482       ],
40483       [
40484         "Timor-Leste",
40485         "tl",
40486         "670"
40487       ],
40488       [
40489         "Togo",
40490         "tg",
40491         "228"
40492       ],
40493       [
40494         "Tokelau",
40495         "tk",
40496         "690"
40497       ],
40498       [
40499         "Tonga",
40500         "to",
40501         "676"
40502       ],
40503       [
40504         "Trinidad and Tobago",
40505         "tt",
40506         "1868"
40507       ],
40508       [
40509         "Tunisia (‫تونس‬‎)",
40510         "tn",
40511         "216"
40512       ],
40513       [
40514         "Turkey (Türkiye)",
40515         "tr",
40516         "90"
40517       ],
40518       [
40519         "Turkmenistan",
40520         "tm",
40521         "993"
40522       ],
40523       [
40524         "Turks and Caicos Islands",
40525         "tc",
40526         "1649"
40527       ],
40528       [
40529         "Tuvalu",
40530         "tv",
40531         "688"
40532       ],
40533       [
40534         "U.S. Virgin Islands",
40535         "vi",
40536         "1340"
40537       ],
40538       [
40539         "Uganda",
40540         "ug",
40541         "256"
40542       ],
40543       [
40544         "Ukraine (Україна)",
40545         "ua",
40546         "380"
40547       ],
40548       [
40549         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
40550         "ae",
40551         "971"
40552       ],
40553       [
40554         "United Kingdom",
40555         "gb",
40556         "44",
40557         0
40558       ],
40559       [
40560         "United States",
40561         "us",
40562         "1",
40563         0
40564       ],
40565       [
40566         "Uruguay",
40567         "uy",
40568         "598"
40569       ],
40570       [
40571         "Uzbekistan (Oʻzbekiston)",
40572         "uz",
40573         "998"
40574       ],
40575       [
40576         "Vanuatu",
40577         "vu",
40578         "678"
40579       ],
40580       [
40581         "Vatican City (Città del Vaticano)",
40582         "va",
40583         "39",
40584         1
40585       ],
40586       [
40587         "Venezuela",
40588         "ve",
40589         "58"
40590       ],
40591       [
40592         "Vietnam (Việt Nam)",
40593         "vn",
40594         "84"
40595       ],
40596       [
40597         "Wallis and Futuna (Wallis-et-Futuna)",
40598         "wf",
40599         "681"
40600       ],
40601       [
40602         "Western Sahara (‫الصحراء الغربية‬‎)",
40603         "eh",
40604         "212",
40605         1
40606       ],
40607       [
40608         "Yemen (‫اليمن‬‎)",
40609         "ye",
40610         "967"
40611       ],
40612       [
40613         "Zambia",
40614         "zm",
40615         "260"
40616       ],
40617       [
40618         "Zimbabwe",
40619         "zw",
40620         "263"
40621       ],
40622       [
40623         "Åland Islands",
40624         "ax",
40625         "358",
40626         1
40627       ]
40628   ];
40629   
40630   return d;
40631 }/**
40632 *    This script refer to:
40633 *    Title: International Telephone Input
40634 *    Author: Jack O'Connor
40635 *    Code version:  v12.1.12
40636 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40637 **/
40638
40639 /**
40640  * @class Roo.bootstrap.PhoneInput
40641  * @extends Roo.bootstrap.TriggerField
40642  * An input with International dial-code selection
40643  
40644  * @cfg {String} defaultDialCode default '+852'
40645  * @cfg {Array} preferedCountries default []
40646   
40647  * @constructor
40648  * Create a new PhoneInput.
40649  * @param {Object} config Configuration options
40650  */
40651
40652 Roo.bootstrap.PhoneInput = function(config) {
40653     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40654 };
40655
40656 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40657         
40658         listWidth: undefined,
40659         
40660         selectedClass: 'active',
40661         
40662         invalidClass : "has-warning",
40663         
40664         validClass: 'has-success',
40665         
40666         allowed: '0123456789',
40667         
40668         max_length: 15,
40669         
40670         /**
40671          * @cfg {String} defaultDialCode The default dial code when initializing the input
40672          */
40673         defaultDialCode: '+852',
40674         
40675         /**
40676          * @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
40677          */
40678         preferedCountries: false,
40679         
40680         getAutoCreate : function()
40681         {
40682             var data = Roo.bootstrap.PhoneInputData();
40683             var align = this.labelAlign || this.parentLabelAlign();
40684             var id = Roo.id();
40685             
40686             this.allCountries = [];
40687             this.dialCodeMapping = [];
40688             
40689             for (var i = 0; i < data.length; i++) {
40690               var c = data[i];
40691               this.allCountries[i] = {
40692                 name: c[0],
40693                 iso2: c[1],
40694                 dialCode: c[2],
40695                 priority: c[3] || 0,
40696                 areaCodes: c[4] || null
40697               };
40698               this.dialCodeMapping[c[2]] = {
40699                   name: c[0],
40700                   iso2: c[1],
40701                   priority: c[3] || 0,
40702                   areaCodes: c[4] || null
40703               };
40704             }
40705             
40706             var cfg = {
40707                 cls: 'form-group',
40708                 cn: []
40709             };
40710             
40711             var input =  {
40712                 tag: 'input',
40713                 id : id,
40714                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40715                 maxlength: this.max_length,
40716                 cls : 'form-control tel-input',
40717                 autocomplete: 'new-password'
40718             };
40719             
40720             var hiddenInput = {
40721                 tag: 'input',
40722                 type: 'hidden',
40723                 cls: 'hidden-tel-input'
40724             };
40725             
40726             if (this.name) {
40727                 hiddenInput.name = this.name;
40728             }
40729             
40730             if (this.disabled) {
40731                 input.disabled = true;
40732             }
40733             
40734             var flag_container = {
40735                 tag: 'div',
40736                 cls: 'flag-box',
40737                 cn: [
40738                     {
40739                         tag: 'div',
40740                         cls: 'flag'
40741                     },
40742                     {
40743                         tag: 'div',
40744                         cls: 'caret'
40745                     }
40746                 ]
40747             };
40748             
40749             var box = {
40750                 tag: 'div',
40751                 cls: this.hasFeedback ? 'has-feedback' : '',
40752                 cn: [
40753                     hiddenInput,
40754                     input,
40755                     {
40756                         tag: 'input',
40757                         cls: 'dial-code-holder',
40758                         disabled: true
40759                     }
40760                 ]
40761             };
40762             
40763             var container = {
40764                 cls: 'roo-select2-container input-group',
40765                 cn: [
40766                     flag_container,
40767                     box
40768                 ]
40769             };
40770             
40771             if (this.fieldLabel.length) {
40772                 var indicator = {
40773                     tag: 'i',
40774                     tooltip: 'This field is required'
40775                 };
40776                 
40777                 var label = {
40778                     tag: 'label',
40779                     'for':  id,
40780                     cls: 'control-label',
40781                     cn: []
40782                 };
40783                 
40784                 var label_text = {
40785                     tag: 'span',
40786                     html: this.fieldLabel
40787                 };
40788                 
40789                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40790                 label.cn = [
40791                     indicator,
40792                     label_text
40793                 ];
40794                 
40795                 if(this.indicatorpos == 'right') {
40796                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40797                     label.cn = [
40798                         label_text,
40799                         indicator
40800                     ];
40801                 }
40802                 
40803                 if(align == 'left') {
40804                     container = {
40805                         tag: 'div',
40806                         cn: [
40807                             container
40808                         ]
40809                     };
40810                     
40811                     if(this.labelWidth > 12){
40812                         label.style = "width: " + this.labelWidth + 'px';
40813                     }
40814                     if(this.labelWidth < 13 && this.labelmd == 0){
40815                         this.labelmd = this.labelWidth;
40816                     }
40817                     if(this.labellg > 0){
40818                         label.cls += ' col-lg-' + this.labellg;
40819                         input.cls += ' col-lg-' + (12 - this.labellg);
40820                     }
40821                     if(this.labelmd > 0){
40822                         label.cls += ' col-md-' + this.labelmd;
40823                         container.cls += ' col-md-' + (12 - this.labelmd);
40824                     }
40825                     if(this.labelsm > 0){
40826                         label.cls += ' col-sm-' + this.labelsm;
40827                         container.cls += ' col-sm-' + (12 - this.labelsm);
40828                     }
40829                     if(this.labelxs > 0){
40830                         label.cls += ' col-xs-' + this.labelxs;
40831                         container.cls += ' col-xs-' + (12 - this.labelxs);
40832                     }
40833                 }
40834             }
40835             
40836             cfg.cn = [
40837                 label,
40838                 container
40839             ];
40840             
40841             var settings = this;
40842             
40843             ['xs','sm','md','lg'].map(function(size){
40844                 if (settings[size]) {
40845                     cfg.cls += ' col-' + size + '-' + settings[size];
40846                 }
40847             });
40848             
40849             this.store = new Roo.data.Store({
40850                 proxy : new Roo.data.MemoryProxy({}),
40851                 reader : new Roo.data.JsonReader({
40852                     fields : [
40853                         {
40854                             'name' : 'name',
40855                             'type' : 'string'
40856                         },
40857                         {
40858                             'name' : 'iso2',
40859                             'type' : 'string'
40860                         },
40861                         {
40862                             'name' : 'dialCode',
40863                             'type' : 'string'
40864                         },
40865                         {
40866                             'name' : 'priority',
40867                             'type' : 'string'
40868                         },
40869                         {
40870                             'name' : 'areaCodes',
40871                             'type' : 'string'
40872                         }
40873                     ]
40874                 })
40875             });
40876             
40877             if(!this.preferedCountries) {
40878                 this.preferedCountries = [
40879                     'hk',
40880                     'gb',
40881                     'us'
40882                 ];
40883             }
40884             
40885             var p = this.preferedCountries.reverse();
40886             
40887             if(p) {
40888                 for (var i = 0; i < p.length; i++) {
40889                     for (var j = 0; j < this.allCountries.length; j++) {
40890                         if(this.allCountries[j].iso2 == p[i]) {
40891                             var t = this.allCountries[j];
40892                             this.allCountries.splice(j,1);
40893                             this.allCountries.unshift(t);
40894                         }
40895                     } 
40896                 }
40897             }
40898             
40899             this.store.proxy.data = {
40900                 success: true,
40901                 data: this.allCountries
40902             };
40903             
40904             return cfg;
40905         },
40906         
40907         initEvents : function()
40908         {
40909             this.createList();
40910             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40911             
40912             this.indicator = this.indicatorEl();
40913             this.flag = this.flagEl();
40914             this.dialCodeHolder = this.dialCodeHolderEl();
40915             
40916             this.trigger = this.el.select('div.flag-box',true).first();
40917             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40918             
40919             var _this = this;
40920             
40921             (function(){
40922                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40923                 _this.list.setWidth(lw);
40924             }).defer(100);
40925             
40926             this.list.on('mouseover', this.onViewOver, this);
40927             this.list.on('mousemove', this.onViewMove, this);
40928             this.inputEl().on("keyup", this.onKeyUp, this);
40929             this.inputEl().on("keypress", this.onKeyPress, this);
40930             
40931             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40932
40933             this.view = new Roo.View(this.list, this.tpl, {
40934                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40935             });
40936             
40937             this.view.on('click', this.onViewClick, this);
40938             this.setValue(this.defaultDialCode);
40939         },
40940         
40941         onTriggerClick : function(e)
40942         {
40943             Roo.log('trigger click');
40944             if(this.disabled){
40945                 return;
40946             }
40947             
40948             if(this.isExpanded()){
40949                 this.collapse();
40950                 this.hasFocus = false;
40951             }else {
40952                 this.store.load({});
40953                 this.hasFocus = true;
40954                 this.expand();
40955             }
40956         },
40957         
40958         isExpanded : function()
40959         {
40960             return this.list.isVisible();
40961         },
40962         
40963         collapse : function()
40964         {
40965             if(!this.isExpanded()){
40966                 return;
40967             }
40968             this.list.hide();
40969             Roo.get(document).un('mousedown', this.collapseIf, this);
40970             Roo.get(document).un('mousewheel', this.collapseIf, this);
40971             this.fireEvent('collapse', this);
40972             this.validate();
40973         },
40974         
40975         expand : function()
40976         {
40977             Roo.log('expand');
40978
40979             if(this.isExpanded() || !this.hasFocus){
40980                 return;
40981             }
40982             
40983             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40984             this.list.setWidth(lw);
40985             
40986             this.list.show();
40987             this.restrictHeight();
40988             
40989             Roo.get(document).on('mousedown', this.collapseIf, this);
40990             Roo.get(document).on('mousewheel', this.collapseIf, this);
40991             
40992             this.fireEvent('expand', this);
40993         },
40994         
40995         restrictHeight : function()
40996         {
40997             this.list.alignTo(this.inputEl(), this.listAlign);
40998             this.list.alignTo(this.inputEl(), this.listAlign);
40999         },
41000         
41001         onViewOver : function(e, t)
41002         {
41003             if(this.inKeyMode){
41004                 return;
41005             }
41006             var item = this.view.findItemFromChild(t);
41007             
41008             if(item){
41009                 var index = this.view.indexOf(item);
41010                 this.select(index, false);
41011             }
41012         },
41013
41014         // private
41015         onViewClick : function(view, doFocus, el, e)
41016         {
41017             var index = this.view.getSelectedIndexes()[0];
41018             
41019             var r = this.store.getAt(index);
41020             
41021             if(r){
41022                 this.onSelect(r, index);
41023             }
41024             if(doFocus !== false && !this.blockFocus){
41025                 this.inputEl().focus();
41026             }
41027         },
41028         
41029         onViewMove : function(e, t)
41030         {
41031             this.inKeyMode = false;
41032         },
41033         
41034         select : function(index, scrollIntoView)
41035         {
41036             this.selectedIndex = index;
41037             this.view.select(index);
41038             if(scrollIntoView !== false){
41039                 var el = this.view.getNode(index);
41040                 if(el){
41041                     this.list.scrollChildIntoView(el, false);
41042                 }
41043             }
41044         },
41045         
41046         createList : function()
41047         {
41048             this.list = Roo.get(document.body).createChild({
41049                 tag: 'ul',
41050                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
41051                 style: 'display:none'
41052             });
41053             
41054             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
41055         },
41056         
41057         collapseIf : function(e)
41058         {
41059             var in_combo  = e.within(this.el);
41060             var in_list =  e.within(this.list);
41061             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
41062             
41063             if (in_combo || in_list || is_list) {
41064                 return;
41065             }
41066             this.collapse();
41067         },
41068         
41069         onSelect : function(record, index)
41070         {
41071             if(this.fireEvent('beforeselect', this, record, index) !== false){
41072                 
41073                 this.setFlagClass(record.data.iso2);
41074                 this.setDialCode(record.data.dialCode);
41075                 this.hasFocus = false;
41076                 this.collapse();
41077                 this.fireEvent('select', this, record, index);
41078             }
41079         },
41080         
41081         flagEl : function()
41082         {
41083             var flag = this.el.select('div.flag',true).first();
41084             if(!flag){
41085                 return false;
41086             }
41087             return flag;
41088         },
41089         
41090         dialCodeHolderEl : function()
41091         {
41092             var d = this.el.select('input.dial-code-holder',true).first();
41093             if(!d){
41094                 return false;
41095             }
41096             return d;
41097         },
41098         
41099         setDialCode : function(v)
41100         {
41101             this.dialCodeHolder.dom.value = '+'+v;
41102         },
41103         
41104         setFlagClass : function(n)
41105         {
41106             this.flag.dom.className = 'flag '+n;
41107         },
41108         
41109         getValue : function()
41110         {
41111             var v = this.inputEl().getValue();
41112             if(this.dialCodeHolder) {
41113                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
41114             }
41115             return v;
41116         },
41117         
41118         setValue : function(v)
41119         {
41120             var d = this.getDialCode(v);
41121             
41122             //invalid dial code
41123             if(v.length == 0 || !d || d.length == 0) {
41124                 if(this.rendered){
41125                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41126                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41127                 }
41128                 return;
41129             }
41130             
41131             //valid dial code
41132             this.setFlagClass(this.dialCodeMapping[d].iso2);
41133             this.setDialCode(d);
41134             this.inputEl().dom.value = v.replace('+'+d,'');
41135             this.hiddenEl().dom.value = this.getValue();
41136             
41137             this.validate();
41138         },
41139         
41140         getDialCode : function(v)
41141         {
41142             v = v ||  '';
41143             
41144             if (v.length == 0) {
41145                 return this.dialCodeHolder.dom.value;
41146             }
41147             
41148             var dialCode = "";
41149             if (v.charAt(0) != "+") {
41150                 return false;
41151             }
41152             var numericChars = "";
41153             for (var i = 1; i < v.length; i++) {
41154               var c = v.charAt(i);
41155               if (!isNaN(c)) {
41156                 numericChars += c;
41157                 if (this.dialCodeMapping[numericChars]) {
41158                   dialCode = v.substr(1, i);
41159                 }
41160                 if (numericChars.length == 4) {
41161                   break;
41162                 }
41163               }
41164             }
41165             return dialCode;
41166         },
41167         
41168         reset : function()
41169         {
41170             this.setValue(this.defaultDialCode);
41171             this.validate();
41172         },
41173         
41174         hiddenEl : function()
41175         {
41176             return this.el.select('input.hidden-tel-input',true).first();
41177         },
41178         
41179         // after setting val
41180         onKeyUp : function(e){
41181             this.setValue(this.getValue());
41182         },
41183         
41184         onKeyPress : function(e){
41185             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
41186                 e.stopEvent();
41187             }
41188         }
41189         
41190 });
41191 /**
41192  * @class Roo.bootstrap.MoneyField
41193  * @extends Roo.bootstrap.ComboBox
41194  * Bootstrap MoneyField class
41195  * 
41196  * @constructor
41197  * Create a new MoneyField.
41198  * @param {Object} config Configuration options
41199  */
41200
41201 Roo.bootstrap.MoneyField = function(config) {
41202     
41203     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
41204     
41205 };
41206
41207 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
41208     
41209     /**
41210      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41211      */
41212     allowDecimals : true,
41213     /**
41214      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41215      */
41216     decimalSeparator : ".",
41217     /**
41218      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41219      */
41220     decimalPrecision : 0,
41221     /**
41222      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41223      */
41224     allowNegative : true,
41225     /**
41226      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41227      */
41228     allowZero: true,
41229     /**
41230      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41231      */
41232     minValue : Number.NEGATIVE_INFINITY,
41233     /**
41234      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41235      */
41236     maxValue : Number.MAX_VALUE,
41237     /**
41238      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41239      */
41240     minText : "The minimum value for this field is {0}",
41241     /**
41242      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41243      */
41244     maxText : "The maximum value for this field is {0}",
41245     /**
41246      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41247      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41248      */
41249     nanText : "{0} is not a valid number",
41250     /**
41251      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
41252      */
41253     castInt : true,
41254     /**
41255      * @cfg {String} defaults currency of the MoneyField
41256      * value should be in lkey
41257      */
41258     defaultCurrency : false,
41259     /**
41260      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41261      */
41262     thousandsDelimiter : false,
41263     /**
41264      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
41265      */
41266     max_length: false,
41267     
41268     inputlg : 9,
41269     inputmd : 9,
41270     inputsm : 9,
41271     inputxs : 6,
41272     
41273     store : false,
41274     
41275     getAutoCreate : function()
41276     {
41277         var align = this.labelAlign || this.parentLabelAlign();
41278         
41279         var id = Roo.id();
41280
41281         var cfg = {
41282             cls: 'form-group',
41283             cn: []
41284         };
41285
41286         var input =  {
41287             tag: 'input',
41288             id : id,
41289             cls : 'form-control roo-money-amount-input',
41290             autocomplete: 'new-password'
41291         };
41292         
41293         var hiddenInput = {
41294             tag: 'input',
41295             type: 'hidden',
41296             id: Roo.id(),
41297             cls: 'hidden-number-input'
41298         };
41299         
41300         if(this.max_length) {
41301             input.maxlength = this.max_length; 
41302         }
41303         
41304         if (this.name) {
41305             hiddenInput.name = this.name;
41306         }
41307
41308         if (this.disabled) {
41309             input.disabled = true;
41310         }
41311
41312         var clg = 12 - this.inputlg;
41313         var cmd = 12 - this.inputmd;
41314         var csm = 12 - this.inputsm;
41315         var cxs = 12 - this.inputxs;
41316         
41317         var container = {
41318             tag : 'div',
41319             cls : 'row roo-money-field',
41320             cn : [
41321                 {
41322                     tag : 'div',
41323                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
41324                     cn : [
41325                         {
41326                             tag : 'div',
41327                             cls: 'roo-select2-container input-group',
41328                             cn: [
41329                                 {
41330                                     tag : 'input',
41331                                     cls : 'form-control roo-money-currency-input',
41332                                     autocomplete: 'new-password',
41333                                     readOnly : 1,
41334                                     name : this.currencyName
41335                                 },
41336                                 {
41337                                     tag :'span',
41338                                     cls : 'input-group-addon',
41339                                     cn : [
41340                                         {
41341                                             tag: 'span',
41342                                             cls: 'caret'
41343                                         }
41344                                     ]
41345                                 }
41346                             ]
41347                         }
41348                     ]
41349                 },
41350                 {
41351                     tag : 'div',
41352                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41353                     cn : [
41354                         {
41355                             tag: 'div',
41356                             cls: this.hasFeedback ? 'has-feedback' : '',
41357                             cn: [
41358                                 input
41359                             ]
41360                         }
41361                     ]
41362                 }
41363             ]
41364             
41365         };
41366         
41367         if (this.fieldLabel.length) {
41368             var indicator = {
41369                 tag: 'i',
41370                 tooltip: 'This field is required'
41371             };
41372
41373             var label = {
41374                 tag: 'label',
41375                 'for':  id,
41376                 cls: 'control-label',
41377                 cn: []
41378             };
41379
41380             var label_text = {
41381                 tag: 'span',
41382                 html: this.fieldLabel
41383             };
41384
41385             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41386             label.cn = [
41387                 indicator,
41388                 label_text
41389             ];
41390
41391             if(this.indicatorpos == 'right') {
41392                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41393                 label.cn = [
41394                     label_text,
41395                     indicator
41396                 ];
41397             }
41398
41399             if(align == 'left') {
41400                 container = {
41401                     tag: 'div',
41402                     cn: [
41403                         container
41404                     ]
41405                 };
41406
41407                 if(this.labelWidth > 12){
41408                     label.style = "width: " + this.labelWidth + 'px';
41409                 }
41410                 if(this.labelWidth < 13 && this.labelmd == 0){
41411                     this.labelmd = this.labelWidth;
41412                 }
41413                 if(this.labellg > 0){
41414                     label.cls += ' col-lg-' + this.labellg;
41415                     input.cls += ' col-lg-' + (12 - this.labellg);
41416                 }
41417                 if(this.labelmd > 0){
41418                     label.cls += ' col-md-' + this.labelmd;
41419                     container.cls += ' col-md-' + (12 - this.labelmd);
41420                 }
41421                 if(this.labelsm > 0){
41422                     label.cls += ' col-sm-' + this.labelsm;
41423                     container.cls += ' col-sm-' + (12 - this.labelsm);
41424                 }
41425                 if(this.labelxs > 0){
41426                     label.cls += ' col-xs-' + this.labelxs;
41427                     container.cls += ' col-xs-' + (12 - this.labelxs);
41428                 }
41429             }
41430         }
41431
41432         cfg.cn = [
41433             label,
41434             container,
41435             hiddenInput
41436         ];
41437         
41438         var settings = this;
41439
41440         ['xs','sm','md','lg'].map(function(size){
41441             if (settings[size]) {
41442                 cfg.cls += ' col-' + size + '-' + settings[size];
41443             }
41444         });
41445         
41446         return cfg;
41447     },
41448     
41449     initEvents : function()
41450     {
41451         this.indicator = this.indicatorEl();
41452         
41453         this.initCurrencyEvent();
41454         
41455         this.initNumberEvent();
41456     },
41457     
41458     initCurrencyEvent : function()
41459     {
41460         if (!this.store) {
41461             throw "can not find store for combo";
41462         }
41463         
41464         this.store = Roo.factory(this.store, Roo.data);
41465         this.store.parent = this;
41466         
41467         this.createList();
41468         
41469         this.triggerEl = this.el.select('.input-group-addon', true).first();
41470         
41471         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41472         
41473         var _this = this;
41474         
41475         (function(){
41476             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41477             _this.list.setWidth(lw);
41478         }).defer(100);
41479         
41480         this.list.on('mouseover', this.onViewOver, this);
41481         this.list.on('mousemove', this.onViewMove, this);
41482         this.list.on('scroll', this.onViewScroll, this);
41483         
41484         if(!this.tpl){
41485             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41486         }
41487         
41488         this.view = new Roo.View(this.list, this.tpl, {
41489             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41490         });
41491         
41492         this.view.on('click', this.onViewClick, this);
41493         
41494         this.store.on('beforeload', this.onBeforeLoad, this);
41495         this.store.on('load', this.onLoad, this);
41496         this.store.on('loadexception', this.onLoadException, this);
41497         
41498         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41499             "up" : function(e){
41500                 this.inKeyMode = true;
41501                 this.selectPrev();
41502             },
41503
41504             "down" : function(e){
41505                 if(!this.isExpanded()){
41506                     this.onTriggerClick();
41507                 }else{
41508                     this.inKeyMode = true;
41509                     this.selectNext();
41510                 }
41511             },
41512
41513             "enter" : function(e){
41514                 this.collapse();
41515                 
41516                 if(this.fireEvent("specialkey", this, e)){
41517                     this.onViewClick(false);
41518                 }
41519                 
41520                 return true;
41521             },
41522
41523             "esc" : function(e){
41524                 this.collapse();
41525             },
41526
41527             "tab" : function(e){
41528                 this.collapse();
41529                 
41530                 if(this.fireEvent("specialkey", this, e)){
41531                     this.onViewClick(false);
41532                 }
41533                 
41534                 return true;
41535             },
41536
41537             scope : this,
41538
41539             doRelay : function(foo, bar, hname){
41540                 if(hname == 'down' || this.scope.isExpanded()){
41541                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41542                 }
41543                 return true;
41544             },
41545
41546             forceKeyDown: true
41547         });
41548         
41549         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41550         
41551     },
41552     
41553     initNumberEvent : function(e)
41554     {
41555         this.inputEl().on("keydown" , this.fireKey,  this);
41556         this.inputEl().on("focus", this.onFocus,  this);
41557         this.inputEl().on("blur", this.onBlur,  this);
41558         
41559         this.inputEl().relayEvent('keyup', this);
41560         
41561         if(this.indicator){
41562             this.indicator.addClass('invisible');
41563         }
41564  
41565         this.originalValue = this.getValue();
41566         
41567         if(this.validationEvent == 'keyup'){
41568             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41569             this.inputEl().on('keyup', this.filterValidation, this);
41570         }
41571         else if(this.validationEvent !== false){
41572             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41573         }
41574         
41575         if(this.selectOnFocus){
41576             this.on("focus", this.preFocus, this);
41577             
41578         }
41579         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41580             this.inputEl().on("keypress", this.filterKeys, this);
41581         } else {
41582             this.inputEl().relayEvent('keypress', this);
41583         }
41584         
41585         var allowed = "0123456789";
41586         
41587         if(this.allowDecimals){
41588             allowed += this.decimalSeparator;
41589         }
41590         
41591         if(this.allowNegative){
41592             allowed += "-";
41593         }
41594         
41595         if(this.thousandsDelimiter) {
41596             allowed += ",";
41597         }
41598         
41599         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41600         
41601         var keyPress = function(e){
41602             
41603             var k = e.getKey();
41604             
41605             var c = e.getCharCode();
41606             
41607             if(
41608                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41609                     allowed.indexOf(String.fromCharCode(c)) === -1
41610             ){
41611                 e.stopEvent();
41612                 return;
41613             }
41614             
41615             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41616                 return;
41617             }
41618             
41619             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41620                 e.stopEvent();
41621             }
41622         };
41623         
41624         this.inputEl().on("keypress", keyPress, this);
41625         
41626     },
41627     
41628     onTriggerClick : function(e)
41629     {   
41630         if(this.disabled){
41631             return;
41632         }
41633         
41634         this.page = 0;
41635         this.loadNext = false;
41636         
41637         if(this.isExpanded()){
41638             this.collapse();
41639             return;
41640         }
41641         
41642         this.hasFocus = true;
41643         
41644         if(this.triggerAction == 'all') {
41645             this.doQuery(this.allQuery, true);
41646             return;
41647         }
41648         
41649         this.doQuery(this.getRawValue());
41650     },
41651     
41652     getCurrency : function()
41653     {   
41654         var v = this.currencyEl().getValue();
41655         
41656         return v;
41657     },
41658     
41659     restrictHeight : function()
41660     {
41661         this.list.alignTo(this.currencyEl(), this.listAlign);
41662         this.list.alignTo(this.currencyEl(), this.listAlign);
41663     },
41664     
41665     onViewClick : function(view, doFocus, el, e)
41666     {
41667         var index = this.view.getSelectedIndexes()[0];
41668         
41669         var r = this.store.getAt(index);
41670         
41671         if(r){
41672             this.onSelect(r, index);
41673         }
41674     },
41675     
41676     onSelect : function(record, index){
41677         
41678         if(this.fireEvent('beforeselect', this, record, index) !== false){
41679         
41680             this.setFromCurrencyData(index > -1 ? record.data : false);
41681             
41682             this.collapse();
41683             
41684             this.fireEvent('select', this, record, index);
41685         }
41686     },
41687     
41688     setFromCurrencyData : function(o)
41689     {
41690         var currency = '';
41691         
41692         this.lastCurrency = o;
41693         
41694         if (this.currencyField) {
41695             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41696         } else {
41697             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41698         }
41699         
41700         this.lastSelectionText = currency;
41701         
41702         //setting default currency
41703         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41704             this.setCurrency(this.defaultCurrency);
41705             return;
41706         }
41707         
41708         this.setCurrency(currency);
41709     },
41710     
41711     setFromData : function(o)
41712     {
41713         var c = {};
41714         
41715         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41716         
41717         this.setFromCurrencyData(c);
41718         
41719         var value = '';
41720         
41721         if (this.name) {
41722             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41723         } else {
41724             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41725         }
41726         
41727         this.setValue(value);
41728         
41729     },
41730     
41731     setCurrency : function(v)
41732     {   
41733         this.currencyValue = v;
41734         
41735         if(this.rendered){
41736             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41737             this.validate();
41738         }
41739     },
41740     
41741     setValue : function(v)
41742     {
41743         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41744         
41745         this.value = v;
41746         
41747         if(this.rendered){
41748             
41749             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41750             
41751             this.inputEl().dom.value = (v == '') ? '' :
41752                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41753             
41754             if(!this.allowZero && v === '0') {
41755                 this.hiddenEl().dom.value = '';
41756                 this.inputEl().dom.value = '';
41757             }
41758             
41759             this.validate();
41760         }
41761     },
41762     
41763     getRawValue : function()
41764     {
41765         var v = this.inputEl().getValue();
41766         
41767         return v;
41768     },
41769     
41770     getValue : function()
41771     {
41772         return this.fixPrecision(this.parseValue(this.getRawValue()));
41773     },
41774     
41775     parseValue : function(value)
41776     {
41777         if(this.thousandsDelimiter) {
41778             value += "";
41779             r = new RegExp(",", "g");
41780             value = value.replace(r, "");
41781         }
41782         
41783         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41784         return isNaN(value) ? '' : value;
41785         
41786     },
41787     
41788     fixPrecision : function(value)
41789     {
41790         if(this.thousandsDelimiter) {
41791             value += "";
41792             r = new RegExp(",", "g");
41793             value = value.replace(r, "");
41794         }
41795         
41796         var nan = isNaN(value);
41797         
41798         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41799             return nan ? '' : value;
41800         }
41801         return parseFloat(value).toFixed(this.decimalPrecision);
41802     },
41803     
41804     decimalPrecisionFcn : function(v)
41805     {
41806         return Math.floor(v);
41807     },
41808     
41809     validateValue : function(value)
41810     {
41811         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41812             return false;
41813         }
41814         
41815         var num = this.parseValue(value);
41816         
41817         if(isNaN(num)){
41818             this.markInvalid(String.format(this.nanText, value));
41819             return false;
41820         }
41821         
41822         if(num < this.minValue){
41823             this.markInvalid(String.format(this.minText, this.minValue));
41824             return false;
41825         }
41826         
41827         if(num > this.maxValue){
41828             this.markInvalid(String.format(this.maxText, this.maxValue));
41829             return false;
41830         }
41831         
41832         return true;
41833     },
41834     
41835     validate : function()
41836     {
41837         if(this.disabled || this.allowBlank){
41838             this.markValid();
41839             return true;
41840         }
41841         
41842         var currency = this.getCurrency();
41843         
41844         if(this.validateValue(this.getRawValue()) && currency.length){
41845             this.markValid();
41846             return true;
41847         }
41848         
41849         this.markInvalid();
41850         return false;
41851     },
41852     
41853     getName: function()
41854     {
41855         return this.name;
41856     },
41857     
41858     beforeBlur : function()
41859     {
41860         if(!this.castInt){
41861             return;
41862         }
41863         
41864         var v = this.parseValue(this.getRawValue());
41865         
41866         if(v || v == 0){
41867             this.setValue(v);
41868         }
41869     },
41870     
41871     onBlur : function()
41872     {
41873         this.beforeBlur();
41874         
41875         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41876             //this.el.removeClass(this.focusClass);
41877         }
41878         
41879         this.hasFocus = false;
41880         
41881         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41882             this.validate();
41883         }
41884         
41885         var v = this.getValue();
41886         
41887         if(String(v) !== String(this.startValue)){
41888             this.fireEvent('change', this, v, this.startValue);
41889         }
41890         
41891         this.fireEvent("blur", this);
41892     },
41893     
41894     inputEl : function()
41895     {
41896         return this.el.select('.roo-money-amount-input', true).first();
41897     },
41898     
41899     currencyEl : function()
41900     {
41901         return this.el.select('.roo-money-currency-input', true).first();
41902     },
41903     
41904     hiddenEl : function()
41905     {
41906         return this.el.select('input.hidden-number-input',true).first();
41907     }
41908     
41909 });/**
41910  * @class Roo.bootstrap.BezierSignature
41911  * @extends Roo.bootstrap.Component
41912  * Bootstrap BezierSignature class
41913  * This script refer to:
41914  *    Title: Signature Pad
41915  *    Author: szimek
41916  *    Availability: https://github.com/szimek/signature_pad
41917  *
41918  * @constructor
41919  * Create a new BezierSignature
41920  * @param {Object} config The config object
41921  */
41922
41923 Roo.bootstrap.BezierSignature = function(config){
41924     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
41925     this.addEvents({
41926         "resize" : true
41927     });
41928 };
41929
41930 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
41931 {
41932      
41933     curve_data: [],
41934     
41935     is_empty: true,
41936     
41937     mouse_btn_down: true,
41938     
41939     /**
41940      * @cfg {int} canvas height
41941      */
41942     canvas_height: '200px',
41943     
41944     /**
41945      * @cfg {float|function} Radius of a single dot.
41946      */ 
41947     dot_size: false,
41948     
41949     /**
41950      * @cfg {float} Minimum width of a line. Defaults to 0.5.
41951      */
41952     min_width: 0.5,
41953     
41954     /**
41955      * @cfg {float} Maximum width of a line. Defaults to 2.5.
41956      */
41957     max_width: 2.5,
41958     
41959     /**
41960      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
41961      */
41962     throttle: 16,
41963     
41964     /**
41965      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
41966      */
41967     min_distance: 5,
41968     
41969     /**
41970      * @cfg {string} Color used to clear the background. Can be any color format accepted by context.fillStyle. Defaults to "rgba(0,0,0,0)" (transparent black). Use a non-transparent color e.g. "rgb(255,255,255)" (opaque white) if you'd like to save signatures as JPEG images.
41971      */
41972     bg_color: 'rgba(0, 0, 0, 0)',
41973     
41974     /**
41975      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
41976      */
41977     dot_color: 'black',
41978     
41979     /**
41980      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
41981      */ 
41982     velocity_filter_weight: 0.7,
41983     
41984     /**
41985      * @cfg {function} Callback when stroke begin. 
41986      */
41987     onBegin: false,
41988     
41989     /**
41990      * @cfg {function} Callback when stroke end.
41991      */
41992     onEnd: false,
41993     
41994     getAutoCreate : function()
41995     {
41996         var cls = 'roo-signature column';
41997         
41998         if(this.cls){
41999             cls += ' ' + this.cls;
42000         }
42001         
42002         var col_sizes = [
42003             'lg',
42004             'md',
42005             'sm',
42006             'xs'
42007         ];
42008         
42009         for(var i = 0; i < col_sizes.length; i++) {
42010             if(this[col_sizes[i]]) {
42011                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
42012             }
42013         }
42014         
42015         var cfg = {
42016             tag: 'div',
42017             cls: cls,
42018             cn: [
42019                 {
42020                     tag: 'div',
42021                     cls: 'roo-signature-body',
42022                     cn: [
42023                         {
42024                             tag: 'canvas',
42025                             cls: 'roo-signature-body-canvas',
42026                             height: this.canvas_height,
42027                             width: this.canvas_width
42028                         }
42029                     ]
42030                 },
42031                 {
42032                     tag: 'input',
42033                     type: 'file',
42034                     style: 'display: none'
42035                 }
42036             ]
42037         };
42038         
42039         return cfg;
42040     },
42041     
42042     initEvents: function() 
42043     {
42044         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
42045         
42046         var canvas = this.canvasEl();
42047         
42048         // mouse && touch event swapping...
42049         canvas.dom.style.touchAction = 'none';
42050         canvas.dom.style.msTouchAction = 'none';
42051         
42052         this.mouse_btn_down = false;
42053         canvas.on('mousedown', this._handleMouseDown, this);
42054         canvas.on('mousemove', this._handleMouseMove, this);
42055         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
42056         
42057         if (window.PointerEvent) {
42058             canvas.on('pointerdown', this._handleMouseDown, this);
42059             canvas.on('pointermove', this._handleMouseMove, this);
42060             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
42061         }
42062         
42063         if ('ontouchstart' in window) {
42064             canvas.on('touchstart', this._handleTouchStart, this);
42065             canvas.on('touchmove', this._handleTouchMove, this);
42066             canvas.on('touchend', this._handleTouchEnd, this);
42067         }
42068         
42069         Roo.EventManager.onWindowResize(this.resize, this, true);
42070         
42071         // file input event
42072         this.fileEl().on('change', this.uploadImage, this);
42073         
42074         this.clear();
42075         
42076         this.resize();
42077     },
42078     
42079     resize: function(){
42080         
42081         var canvas = this.canvasEl().dom;
42082         var ctx = this.canvasElCtx();
42083         var img_data = false;
42084         
42085         if(canvas.width > 0) {
42086             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
42087         }
42088         // setting canvas width will clean img data
42089         canvas.width = 0;
42090         
42091         var style = window.getComputedStyle ? 
42092             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
42093             
42094         var padding_left = parseInt(style.paddingLeft) || 0;
42095         var padding_right = parseInt(style.paddingRight) || 0;
42096         
42097         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
42098         
42099         if(img_data) {
42100             ctx.putImageData(img_data, 0, 0);
42101         }
42102     },
42103     
42104     _handleMouseDown: function(e)
42105     {
42106         if (e.browserEvent.which === 1) {
42107             this.mouse_btn_down = true;
42108             this.strokeBegin(e);
42109         }
42110     },
42111     
42112     _handleMouseMove: function (e)
42113     {
42114         if (this.mouse_btn_down) {
42115             this.strokeMoveUpdate(e);
42116         }
42117     },
42118     
42119     _handleMouseUp: function (e)
42120     {
42121         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
42122             this.mouse_btn_down = false;
42123             this.strokeEnd(e);
42124         }
42125     },
42126     
42127     _handleTouchStart: function (e) {
42128         
42129         e.preventDefault();
42130         if (e.browserEvent.targetTouches.length === 1) {
42131             // var touch = e.browserEvent.changedTouches[0];
42132             // this.strokeBegin(touch);
42133             
42134              this.strokeBegin(e); // assume e catching the correct xy...
42135         }
42136     },
42137     
42138     _handleTouchMove: function (e) {
42139         e.preventDefault();
42140         // var touch = event.targetTouches[0];
42141         // _this._strokeMoveUpdate(touch);
42142         this.strokeMoveUpdate(e);
42143     },
42144     
42145     _handleTouchEnd: function (e) {
42146         var wasCanvasTouched = e.target === this.canvasEl().dom;
42147         if (wasCanvasTouched) {
42148             e.preventDefault();
42149             // var touch = event.changedTouches[0];
42150             // _this._strokeEnd(touch);
42151             this.strokeEnd(e);
42152         }
42153     },
42154     
42155     reset: function () {
42156         this._lastPoints = [];
42157         this._lastVelocity = 0;
42158         this._lastWidth = (this.min_width + this.max_width) / 2;
42159         this.canvasElCtx().fillStyle = this.dot_color;
42160     },
42161     
42162     strokeMoveUpdate: function(e)
42163     {
42164         this.strokeUpdate(e);
42165         
42166         if (this.throttle) {
42167             this.throttleStroke(this.strokeUpdate, this.throttle);
42168         }
42169         else {
42170             this.strokeUpdate(e);
42171         }
42172     },
42173     
42174     strokeBegin: function(e)
42175     {
42176         var newPointGroup = {
42177             color: this.dot_color,
42178             points: []
42179         };
42180         
42181         if (typeof this.onBegin === 'function') {
42182             this.onBegin(e);
42183         }
42184         
42185         this.curve_data.push(newPointGroup);
42186         this.reset();
42187         this.strokeUpdate(e);
42188     },
42189     
42190     strokeUpdate: function(e)
42191     {
42192         var rect = this.canvasEl().dom.getBoundingClientRect();
42193         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
42194         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
42195         var lastPoints = lastPointGroup.points;
42196         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
42197         var isLastPointTooClose = lastPoint
42198             ? point.distanceTo(lastPoint) <= this.min_distance
42199             : false;
42200         var color = lastPointGroup.color;
42201         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
42202             var curve = this.addPoint(point);
42203             if (!lastPoint) {
42204                 this.drawDot({color: color, point: point});
42205             }
42206             else if (curve) {
42207                 this.drawCurve({color: color, curve: curve});
42208             }
42209             lastPoints.push({
42210                 time: point.time,
42211                 x: point.x,
42212                 y: point.y
42213             });
42214         }
42215     },
42216     
42217     strokeEnd: function(e)
42218     {
42219         this.strokeUpdate(e);
42220         if (typeof this.onEnd === 'function') {
42221             this.onEnd(e);
42222         }
42223     },
42224     
42225     addPoint:  function (point) {
42226         var _lastPoints = this._lastPoints;
42227         _lastPoints.push(point);
42228         if (_lastPoints.length > 2) {
42229             if (_lastPoints.length === 3) {
42230                 _lastPoints.unshift(_lastPoints[0]);
42231             }
42232             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
42233             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
42234             _lastPoints.shift();
42235             return curve;
42236         }
42237         return null;
42238     },
42239     
42240     calculateCurveWidths: function (startPoint, endPoint) {
42241         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
42242             (1 - this.velocity_filter_weight) * this._lastVelocity;
42243
42244         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
42245         var widths = {
42246             end: newWidth,
42247             start: this._lastWidth
42248         };
42249         
42250         this._lastVelocity = velocity;
42251         this._lastWidth = newWidth;
42252         return widths;
42253     },
42254     
42255     drawDot: function (_a) {
42256         var color = _a.color, point = _a.point;
42257         var ctx = this.canvasElCtx();
42258         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
42259         ctx.beginPath();
42260         this.drawCurveSegment(point.x, point.y, width);
42261         ctx.closePath();
42262         ctx.fillStyle = color;
42263         ctx.fill();
42264     },
42265     
42266     drawCurve: function (_a) {
42267         var color = _a.color, curve = _a.curve;
42268         var ctx = this.canvasElCtx();
42269         var widthDelta = curve.endWidth - curve.startWidth;
42270         var drawSteps = Math.floor(curve.length()) * 2;
42271         ctx.beginPath();
42272         ctx.fillStyle = color;
42273         for (var i = 0; i < drawSteps; i += 1) {
42274         var t = i / drawSteps;
42275         var tt = t * t;
42276         var ttt = tt * t;
42277         var u = 1 - t;
42278         var uu = u * u;
42279         var uuu = uu * u;
42280         var x = uuu * curve.startPoint.x;
42281         x += 3 * uu * t * curve.control1.x;
42282         x += 3 * u * tt * curve.control2.x;
42283         x += ttt * curve.endPoint.x;
42284         var y = uuu * curve.startPoint.y;
42285         y += 3 * uu * t * curve.control1.y;
42286         y += 3 * u * tt * curve.control2.y;
42287         y += ttt * curve.endPoint.y;
42288         var width = curve.startWidth + ttt * widthDelta;
42289         this.drawCurveSegment(x, y, width);
42290         }
42291         ctx.closePath();
42292         ctx.fill();
42293     },
42294     
42295     drawCurveSegment: function (x, y, width) {
42296         var ctx = this.canvasElCtx();
42297         ctx.moveTo(x, y);
42298         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
42299         this.is_empty = false;
42300     },
42301     
42302     clear: function()
42303     {
42304         var ctx = this.canvasElCtx();
42305         var canvas = this.canvasEl().dom;
42306         ctx.fillStyle = this.bg_color;
42307         ctx.clearRect(0, 0, canvas.width, canvas.height);
42308         ctx.fillRect(0, 0, canvas.width, canvas.height);
42309         this.curve_data = [];
42310         this.reset();
42311         this.is_empty = true;
42312     },
42313     
42314     fileEl: function()
42315     {
42316         return  this.el.select('input',true).first();
42317     },
42318     
42319     canvasEl: function()
42320     {
42321         return this.el.select('canvas',true).first();
42322     },
42323     
42324     canvasElCtx: function()
42325     {
42326         return this.el.select('canvas',true).first().dom.getContext('2d');
42327     },
42328     
42329     getImage: function(type)
42330     {
42331         if(this.is_empty) {
42332             return false;
42333         }
42334         
42335         // encryption ?
42336         return this.canvasEl().dom.toDataURL('image/'+type, 1);
42337     },
42338     
42339     drawFromImage: function(img_src)
42340     {
42341         var img = new Image();
42342         
42343         img.onload = function(){
42344             this.canvasElCtx().drawImage(img, 0, 0);
42345         }.bind(this);
42346         
42347         img.src = img_src;
42348         
42349         this.is_empty = false;
42350     },
42351     
42352     selectImage: function()
42353     {
42354         this.fileEl().dom.click();
42355     },
42356     
42357     uploadImage: function(e)
42358     {
42359         var reader = new FileReader();
42360         
42361         reader.onload = function(e){
42362             var img = new Image();
42363             img.onload = function(){
42364                 this.reset();
42365                 this.canvasElCtx().drawImage(img, 0, 0);
42366             }.bind(this);
42367             img.src = e.target.result;
42368         }.bind(this);
42369         
42370         reader.readAsDataURL(e.target.files[0]);
42371     },
42372     
42373     // Bezier Point Constructor
42374     Point: (function () {
42375         function Point(x, y, time) {
42376             this.x = x;
42377             this.y = y;
42378             this.time = time || Date.now();
42379         }
42380         Point.prototype.distanceTo = function (start) {
42381             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42382         };
42383         Point.prototype.equals = function (other) {
42384             return this.x === other.x && this.y === other.y && this.time === other.time;
42385         };
42386         Point.prototype.velocityFrom = function (start) {
42387             return this.time !== start.time
42388             ? this.distanceTo(start) / (this.time - start.time)
42389             : 0;
42390         };
42391         return Point;
42392     }()),
42393     
42394     
42395     // Bezier Constructor
42396     Bezier: (function () {
42397         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42398             this.startPoint = startPoint;
42399             this.control2 = control2;
42400             this.control1 = control1;
42401             this.endPoint = endPoint;
42402             this.startWidth = startWidth;
42403             this.endWidth = endWidth;
42404         }
42405         Bezier.fromPoints = function (points, widths, scope) {
42406             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42407             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42408             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42409         };
42410         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42411             var dx1 = s1.x - s2.x;
42412             var dy1 = s1.y - s2.y;
42413             var dx2 = s2.x - s3.x;
42414             var dy2 = s2.y - s3.y;
42415             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42416             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42417             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42418             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42419             var dxm = m1.x - m2.x;
42420             var dym = m1.y - m2.y;
42421             var k = l2 / (l1 + l2);
42422             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42423             var tx = s2.x - cm.x;
42424             var ty = s2.y - cm.y;
42425             return {
42426                 c1: new scope.Point(m1.x + tx, m1.y + ty),
42427                 c2: new scope.Point(m2.x + tx, m2.y + ty)
42428             };
42429         };
42430         Bezier.prototype.length = function () {
42431             var steps = 10;
42432             var length = 0;
42433             var px;
42434             var py;
42435             for (var i = 0; i <= steps; i += 1) {
42436                 var t = i / steps;
42437                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42438                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42439                 if (i > 0) {
42440                     var xdiff = cx - px;
42441                     var ydiff = cy - py;
42442                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42443                 }
42444                 px = cx;
42445                 py = cy;
42446             }
42447             return length;
42448         };
42449         Bezier.prototype.point = function (t, start, c1, c2, end) {
42450             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42451             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42452             + (3.0 * c2 * (1.0 - t) * t * t)
42453             + (end * t * t * t);
42454         };
42455         return Bezier;
42456     }()),
42457     
42458     throttleStroke: function(fn, wait) {
42459       if (wait === void 0) { wait = 250; }
42460       var previous = 0;
42461       var timeout = null;
42462       var result;
42463       var storedContext;
42464       var storedArgs;
42465       var later = function () {
42466           previous = Date.now();
42467           timeout = null;
42468           result = fn.apply(storedContext, storedArgs);
42469           if (!timeout) {
42470               storedContext = null;
42471               storedArgs = [];
42472           }
42473       };
42474       return function wrapper() {
42475           var args = [];
42476           for (var _i = 0; _i < arguments.length; _i++) {
42477               args[_i] = arguments[_i];
42478           }
42479           var now = Date.now();
42480           var remaining = wait - (now - previous);
42481           storedContext = this;
42482           storedArgs = args;
42483           if (remaining <= 0 || remaining > wait) {
42484               if (timeout) {
42485                   clearTimeout(timeout);
42486                   timeout = null;
42487               }
42488               previous = now;
42489               result = fn.apply(storedContext, storedArgs);
42490               if (!timeout) {
42491                   storedContext = null;
42492                   storedArgs = [];
42493               }
42494           }
42495           else if (!timeout) {
42496               timeout = window.setTimeout(later, remaining);
42497           }
42498           return result;
42499       };
42500   }
42501   
42502 });
42503
42504  
42505
42506