docs/src/Roo_bootstrap_Card.js.html
[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     footer: '',
1584     
1585     childContainer : false,
1586
1587     layoutCls : function()
1588     {
1589         var cls = '';
1590         var t = this;
1591         Roo.log(this.margin_bottom.length);
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]
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.cn.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         if (this.html.length) {
1702             body.cn.push({
1703                 tag: 'div',
1704                 html : this.html
1705             });
1706         }
1707         // fixme ? handle objects?
1708         if (this.footer.length) {
1709             body.cn.push({
1710                 tag : 'div',
1711                 cls : 'card-footer',
1712                 html: this.footer // escape?
1713             });
1714         }
1715         // footer...
1716         
1717         return cfg;
1718     },
1719     
1720     
1721     getChildContainer : function()
1722     {
1723         
1724         if(!this.el){
1725             return false;
1726         }
1727         return this.el.select('.roo-card-body-ctr',true).first();    
1728     }
1729     
1730 });
1731
1732 /*
1733  * - LGPL
1734  *
1735  * image
1736  * 
1737  */
1738
1739
1740 /**
1741  * @class Roo.bootstrap.Img
1742  * @extends Roo.bootstrap.Component
1743  * Bootstrap Img class
1744  * @cfg {Boolean} imgResponsive false | true
1745  * @cfg {String} border rounded | circle | thumbnail
1746  * @cfg {String} src image source
1747  * @cfg {String} alt image alternative text
1748  * @cfg {String} href a tag href
1749  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1750  * @cfg {String} xsUrl xs image source
1751  * @cfg {String} smUrl sm image source
1752  * @cfg {String} mdUrl md image source
1753  * @cfg {String} lgUrl lg image source
1754  * 
1755  * @constructor
1756  * Create a new Input
1757  * @param {Object} config The config object
1758  */
1759
1760 Roo.bootstrap.Img = function(config){
1761     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1762     
1763     this.addEvents({
1764         // img events
1765         /**
1766          * @event click
1767          * The img click event for the img.
1768          * @param {Roo.EventObject} e
1769          */
1770         "click" : true
1771     });
1772 };
1773
1774 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1775     
1776     imgResponsive: true,
1777     border: '',
1778     src: 'about:blank',
1779     href: false,
1780     target: false,
1781     xsUrl: '',
1782     smUrl: '',
1783     mdUrl: '',
1784     lgUrl: '',
1785
1786     getAutoCreate : function()
1787     {   
1788         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1789             return this.createSingleImg();
1790         }
1791         
1792         var cfg = {
1793             tag: 'div',
1794             cls: 'roo-image-responsive-group',
1795             cn: []
1796         };
1797         var _this = this;
1798         
1799         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1800             
1801             if(!_this[size + 'Url']){
1802                 return;
1803             }
1804             
1805             var img = {
1806                 tag: 'img',
1807                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1808                 html: _this.html || cfg.html,
1809                 src: _this[size + 'Url']
1810             };
1811             
1812             img.cls += ' roo-image-responsive-' + size;
1813             
1814             var s = ['xs', 'sm', 'md', 'lg'];
1815             
1816             s.splice(s.indexOf(size), 1);
1817             
1818             Roo.each(s, function(ss){
1819                 img.cls += ' hidden-' + ss;
1820             });
1821             
1822             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1823                 cfg.cls += ' img-' + _this.border;
1824             }
1825             
1826             if(_this.alt){
1827                 cfg.alt = _this.alt;
1828             }
1829             
1830             if(_this.href){
1831                 var a = {
1832                     tag: 'a',
1833                     href: _this.href,
1834                     cn: [
1835                         img
1836                     ]
1837                 };
1838
1839                 if(this.target){
1840                     a.target = _this.target;
1841                 }
1842             }
1843             
1844             cfg.cn.push((_this.href) ? a : img);
1845             
1846         });
1847         
1848         return cfg;
1849     },
1850     
1851     createSingleImg : function()
1852     {
1853         var cfg = {
1854             tag: 'img',
1855             cls: (this.imgResponsive) ? 'img-responsive' : '',
1856             html : null,
1857             src : 'about:blank'  // just incase src get's set to undefined?!?
1858         };
1859         
1860         cfg.html = this.html || cfg.html;
1861         
1862         cfg.src = this.src || cfg.src;
1863         
1864         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1865             cfg.cls += ' img-' + this.border;
1866         }
1867         
1868         if(this.alt){
1869             cfg.alt = this.alt;
1870         }
1871         
1872         if(this.href){
1873             var a = {
1874                 tag: 'a',
1875                 href: this.href,
1876                 cn: [
1877                     cfg
1878                 ]
1879             };
1880             
1881             if(this.target){
1882                 a.target = this.target;
1883             }
1884             
1885         }
1886         
1887         return (this.href) ? a : cfg;
1888     },
1889     
1890     initEvents: function() 
1891     {
1892         if(!this.href){
1893             this.el.on('click', this.onClick, this);
1894         }
1895         
1896     },
1897     
1898     onClick : function(e)
1899     {
1900         Roo.log('img onclick');
1901         this.fireEvent('click', this, e);
1902     },
1903     /**
1904      * Sets the url of the image - used to update it
1905      * @param {String} url the url of the image
1906      */
1907     
1908     setSrc : function(url)
1909     {
1910         this.src =  url;
1911         
1912         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1913             this.el.dom.src =  url;
1914             return;
1915         }
1916         
1917         this.el.select('img', true).first().dom.src =  url;
1918     }
1919     
1920     
1921    
1922 });
1923
1924  /*
1925  * - LGPL
1926  *
1927  * image
1928  * 
1929  */
1930
1931
1932 /**
1933  * @class Roo.bootstrap.Link
1934  * @extends Roo.bootstrap.Component
1935  * Bootstrap Link Class
1936  * @cfg {String} alt image alternative text
1937  * @cfg {String} href a tag href
1938  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1939  * @cfg {String} html the content of the link.
1940  * @cfg {String} anchor name for the anchor link
1941  * @cfg {String} fa - favicon
1942
1943  * @cfg {Boolean} preventDefault (true | false) default false
1944
1945  * 
1946  * @constructor
1947  * Create a new Input
1948  * @param {Object} config The config object
1949  */
1950
1951 Roo.bootstrap.Link = function(config){
1952     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1953     
1954     this.addEvents({
1955         // img events
1956         /**
1957          * @event click
1958          * The img click event for the img.
1959          * @param {Roo.EventObject} e
1960          */
1961         "click" : true
1962     });
1963 };
1964
1965 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1966     
1967     href: false,
1968     target: false,
1969     preventDefault: false,
1970     anchor : false,
1971     alt : false,
1972     fa: false,
1973
1974
1975     getAutoCreate : function()
1976     {
1977         var html = this.html || '';
1978         
1979         if (this.fa !== false) {
1980             html = '<i class="fa fa-' + this.fa + '"></i>';
1981         }
1982         var cfg = {
1983             tag: 'a'
1984         };
1985         // anchor's do not require html/href...
1986         if (this.anchor === false) {
1987             cfg.html = html;
1988             cfg.href = this.href || '#';
1989         } else {
1990             cfg.name = this.anchor;
1991             if (this.html !== false || this.fa !== false) {
1992                 cfg.html = html;
1993             }
1994             if (this.href !== false) {
1995                 cfg.href = this.href;
1996             }
1997         }
1998         
1999         if(this.alt !== false){
2000             cfg.alt = this.alt;
2001         }
2002         
2003         
2004         if(this.target !== false) {
2005             cfg.target = this.target;
2006         }
2007         
2008         return cfg;
2009     },
2010     
2011     initEvents: function() {
2012         
2013         if(!this.href || this.preventDefault){
2014             this.el.on('click', this.onClick, this);
2015         }
2016     },
2017     
2018     onClick : function(e)
2019     {
2020         if(this.preventDefault){
2021             e.preventDefault();
2022         }
2023         //Roo.log('img onclick');
2024         this.fireEvent('click', this, e);
2025     }
2026    
2027 });
2028
2029  /*
2030  * - LGPL
2031  *
2032  * header
2033  * 
2034  */
2035
2036 /**
2037  * @class Roo.bootstrap.Header
2038  * @extends Roo.bootstrap.Component
2039  * Bootstrap Header class
2040  * @cfg {String} html content of header
2041  * @cfg {Number} level (1|2|3|4|5|6) default 1
2042  * 
2043  * @constructor
2044  * Create a new Header
2045  * @param {Object} config The config object
2046  */
2047
2048
2049 Roo.bootstrap.Header  = function(config){
2050     Roo.bootstrap.Header.superclass.constructor.call(this, config);
2051 };
2052
2053 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
2054     
2055     //href : false,
2056     html : false,
2057     level : 1,
2058     
2059     
2060     
2061     getAutoCreate : function(){
2062         
2063         
2064         
2065         var cfg = {
2066             tag: 'h' + (1 *this.level),
2067             html: this.html || ''
2068         } ;
2069         
2070         return cfg;
2071     }
2072    
2073 });
2074
2075  
2076
2077  /*
2078  * Based on:
2079  * Ext JS Library 1.1.1
2080  * Copyright(c) 2006-2007, Ext JS, LLC.
2081  *
2082  * Originally Released Under LGPL - original licence link has changed is not relivant.
2083  *
2084  * Fork - LGPL
2085  * <script type="text/javascript">
2086  */
2087  
2088 /**
2089  * @class Roo.bootstrap.MenuMgr
2090  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
2091  * @singleton
2092  */
2093 Roo.bootstrap.MenuMgr = function(){
2094    var menus, active, groups = {}, attached = false, lastShow = new Date();
2095
2096    // private - called when first menu is created
2097    function init(){
2098        menus = {};
2099        active = new Roo.util.MixedCollection();
2100        Roo.get(document).addKeyListener(27, function(){
2101            if(active.length > 0){
2102                hideAll();
2103            }
2104        });
2105    }
2106
2107    // private
2108    function hideAll(){
2109        if(active && active.length > 0){
2110            var c = active.clone();
2111            c.each(function(m){
2112                m.hide();
2113            });
2114        }
2115    }
2116
2117    // private
2118    function onHide(m){
2119        active.remove(m);
2120        if(active.length < 1){
2121            Roo.get(document).un("mouseup", onMouseDown);
2122             
2123            attached = false;
2124        }
2125    }
2126
2127    // private
2128    function onShow(m){
2129        var last = active.last();
2130        lastShow = new Date();
2131        active.add(m);
2132        if(!attached){
2133           Roo.get(document).on("mouseup", onMouseDown);
2134            
2135            attached = true;
2136        }
2137        if(m.parentMenu){
2138           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
2139           m.parentMenu.activeChild = m;
2140        }else if(last && last.isVisible()){
2141           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
2142        }
2143    }
2144
2145    // private
2146    function onBeforeHide(m){
2147        if(m.activeChild){
2148            m.activeChild.hide();
2149        }
2150        if(m.autoHideTimer){
2151            clearTimeout(m.autoHideTimer);
2152            delete m.autoHideTimer;
2153        }
2154    }
2155
2156    // private
2157    function onBeforeShow(m){
2158        var pm = m.parentMenu;
2159        if(!pm && !m.allowOtherMenus){
2160            hideAll();
2161        }else if(pm && pm.activeChild && active != m){
2162            pm.activeChild.hide();
2163        }
2164    }
2165
2166    // private this should really trigger on mouseup..
2167    function onMouseDown(e){
2168         Roo.log("on Mouse Up");
2169         
2170         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
2171             Roo.log("MenuManager hideAll");
2172             hideAll();
2173             e.stopEvent();
2174         }
2175         
2176         
2177    }
2178
2179    // private
2180    function onBeforeCheck(mi, state){
2181        if(state){
2182            var g = groups[mi.group];
2183            for(var i = 0, l = g.length; i < l; i++){
2184                if(g[i] != mi){
2185                    g[i].setChecked(false);
2186                }
2187            }
2188        }
2189    }
2190
2191    return {
2192
2193        /**
2194         * Hides all menus that are currently visible
2195         */
2196        hideAll : function(){
2197             hideAll();  
2198        },
2199
2200        // private
2201        register : function(menu){
2202            if(!menus){
2203                init();
2204            }
2205            menus[menu.id] = menu;
2206            menu.on("beforehide", onBeforeHide);
2207            menu.on("hide", onHide);
2208            menu.on("beforeshow", onBeforeShow);
2209            menu.on("show", onShow);
2210            var g = menu.group;
2211            if(g && menu.events["checkchange"]){
2212                if(!groups[g]){
2213                    groups[g] = [];
2214                }
2215                groups[g].push(menu);
2216                menu.on("checkchange", onCheck);
2217            }
2218        },
2219
2220         /**
2221          * Returns a {@link Roo.menu.Menu} object
2222          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
2223          * be used to generate and return a new Menu instance.
2224          */
2225        get : function(menu){
2226            if(typeof menu == "string"){ // menu id
2227                return menus[menu];
2228            }else if(menu.events){  // menu instance
2229                return menu;
2230            }
2231            /*else if(typeof menu.length == 'number'){ // array of menu items?
2232                return new Roo.bootstrap.Menu({items:menu});
2233            }else{ // otherwise, must be a config
2234                return new Roo.bootstrap.Menu(menu);
2235            }
2236            */
2237            return false;
2238        },
2239
2240        // private
2241        unregister : function(menu){
2242            delete menus[menu.id];
2243            menu.un("beforehide", onBeforeHide);
2244            menu.un("hide", onHide);
2245            menu.un("beforeshow", onBeforeShow);
2246            menu.un("show", onShow);
2247            var g = menu.group;
2248            if(g && menu.events["checkchange"]){
2249                groups[g].remove(menu);
2250                menu.un("checkchange", onCheck);
2251            }
2252        },
2253
2254        // private
2255        registerCheckable : function(menuItem){
2256            var g = menuItem.group;
2257            if(g){
2258                if(!groups[g]){
2259                    groups[g] = [];
2260                }
2261                groups[g].push(menuItem);
2262                menuItem.on("beforecheckchange", onBeforeCheck);
2263            }
2264        },
2265
2266        // private
2267        unregisterCheckable : function(menuItem){
2268            var g = menuItem.group;
2269            if(g){
2270                groups[g].remove(menuItem);
2271                menuItem.un("beforecheckchange", onBeforeCheck);
2272            }
2273        }
2274    };
2275 }();/*
2276  * - LGPL
2277  *
2278  * menu
2279  * 
2280  */
2281
2282 /**
2283  * @class Roo.bootstrap.Menu
2284  * @extends Roo.bootstrap.Component
2285  * Bootstrap Menu class - container for MenuItems
2286  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2287  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2288  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2289  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2290  * 
2291  * @constructor
2292  * Create a new Menu
2293  * @param {Object} config The config object
2294  */
2295
2296
2297 Roo.bootstrap.Menu = function(config){
2298     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2299     if (this.registerMenu && this.type != 'treeview')  {
2300         Roo.bootstrap.MenuMgr.register(this);
2301     }
2302     
2303     
2304     this.addEvents({
2305         /**
2306          * @event beforeshow
2307          * Fires before this menu is displayed (return false to block)
2308          * @param {Roo.menu.Menu} this
2309          */
2310         beforeshow : true,
2311         /**
2312          * @event beforehide
2313          * Fires before this menu is hidden (return false to block)
2314          * @param {Roo.menu.Menu} this
2315          */
2316         beforehide : true,
2317         /**
2318          * @event show
2319          * Fires after this menu is displayed
2320          * @param {Roo.menu.Menu} this
2321          */
2322         show : true,
2323         /**
2324          * @event hide
2325          * Fires after this menu is hidden
2326          * @param {Roo.menu.Menu} this
2327          */
2328         hide : true,
2329         /**
2330          * @event click
2331          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2332          * @param {Roo.menu.Menu} this
2333          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2334          * @param {Roo.EventObject} e
2335          */
2336         click : true,
2337         /**
2338          * @event mouseover
2339          * Fires when the mouse is hovering over 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         mouseover : true,
2345         /**
2346          * @event mouseout
2347          * Fires when the mouse exits this menu
2348          * @param {Roo.menu.Menu} this
2349          * @param {Roo.EventObject} e
2350          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2351          */
2352         mouseout : true,
2353         /**
2354          * @event itemclick
2355          * Fires when a menu item contained in this menu is clicked
2356          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2357          * @param {Roo.EventObject} e
2358          */
2359         itemclick: true
2360     });
2361     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2362 };
2363
2364 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2365     
2366    /// html : false,
2367     //align : '',
2368     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2369     type: false,
2370     /**
2371      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2372      */
2373     registerMenu : true,
2374     
2375     menuItems :false, // stores the menu items..
2376     
2377     hidden:true,
2378         
2379     parentMenu : false,
2380     
2381     stopEvent : true,
2382     
2383     isLink : false,
2384     
2385     getChildContainer : function() {
2386         return this.el;  
2387     },
2388     
2389     getAutoCreate : function(){
2390          
2391         //if (['right'].indexOf(this.align)!==-1) {
2392         //    cfg.cn[1].cls += ' pull-right'
2393         //}
2394         
2395         
2396         var cfg = {
2397             tag : 'ul',
2398             cls : 'dropdown-menu' ,
2399             style : 'z-index:1000'
2400             
2401         };
2402         
2403         if (this.type === 'submenu') {
2404             cfg.cls = 'submenu active';
2405         }
2406         if (this.type === 'treeview') {
2407             cfg.cls = 'treeview-menu';
2408         }
2409         
2410         return cfg;
2411     },
2412     initEvents : function() {
2413         
2414        // Roo.log("ADD event");
2415        // Roo.log(this.triggerEl.dom);
2416         
2417         this.triggerEl.on('click', this.onTriggerClick, this);
2418         
2419         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2420         
2421         
2422         if (this.triggerEl.hasClass('nav-item')) {
2423             // dropdown toggle on the 'a' in BS4?
2424             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2425         } else {
2426             this.triggerEl.addClass('dropdown-toggle');
2427         }
2428         if (Roo.isTouch) {
2429             this.el.on('touchstart'  , this.onTouch, this);
2430         }
2431         this.el.on('click' , this.onClick, this);
2432
2433         this.el.on("mouseover", this.onMouseOver, this);
2434         this.el.on("mouseout", this.onMouseOut, this);
2435         
2436     },
2437     
2438     findTargetItem : function(e)
2439     {
2440         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2441         if(!t){
2442             return false;
2443         }
2444         //Roo.log(t);         Roo.log(t.id);
2445         if(t && t.id){
2446             //Roo.log(this.menuitems);
2447             return this.menuitems.get(t.id);
2448             
2449             //return this.items.get(t.menuItemId);
2450         }
2451         
2452         return false;
2453     },
2454     
2455     onTouch : function(e) 
2456     {
2457         Roo.log("menu.onTouch");
2458         //e.stopEvent(); this make the user popdown broken
2459         this.onClick(e);
2460     },
2461     
2462     onClick : function(e)
2463     {
2464         Roo.log("menu.onClick");
2465         
2466         var t = this.findTargetItem(e);
2467         if(!t || t.isContainer){
2468             return;
2469         }
2470         Roo.log(e);
2471         /*
2472         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2473             if(t == this.activeItem && t.shouldDeactivate(e)){
2474                 this.activeItem.deactivate();
2475                 delete this.activeItem;
2476                 return;
2477             }
2478             if(t.canActivate){
2479                 this.setActiveItem(t, true);
2480             }
2481             return;
2482             
2483             
2484         }
2485         */
2486        
2487         Roo.log('pass click event');
2488         
2489         t.onClick(e);
2490         
2491         this.fireEvent("click", this, t, e);
2492         
2493         var _this = this;
2494         
2495         if(!t.href.length || t.href == '#'){
2496             (function() { _this.hide(); }).defer(100);
2497         }
2498         
2499     },
2500     
2501     onMouseOver : function(e){
2502         var t  = this.findTargetItem(e);
2503         //Roo.log(t);
2504         //if(t){
2505         //    if(t.canActivate && !t.disabled){
2506         //        this.setActiveItem(t, true);
2507         //    }
2508         //}
2509         
2510         this.fireEvent("mouseover", this, e, t);
2511     },
2512     isVisible : function(){
2513         return !this.hidden;
2514     },
2515     onMouseOut : function(e){
2516         var t  = this.findTargetItem(e);
2517         
2518         //if(t ){
2519         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2520         //        this.activeItem.deactivate();
2521         //        delete this.activeItem;
2522         //    }
2523         //}
2524         this.fireEvent("mouseout", this, e, t);
2525     },
2526     
2527     
2528     /**
2529      * Displays this menu relative to another element
2530      * @param {String/HTMLElement/Roo.Element} element The element to align to
2531      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2532      * the element (defaults to this.defaultAlign)
2533      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2534      */
2535     show : function(el, pos, parentMenu)
2536     {
2537         if (false === this.fireEvent("beforeshow", this)) {
2538             Roo.log("show canceled");
2539             return;
2540         }
2541         this.parentMenu = parentMenu;
2542         if(!this.el){
2543             this.render();
2544         }
2545         
2546         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2547     },
2548      /**
2549      * Displays this menu at a specific xy position
2550      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2551      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2552      */
2553     showAt : function(xy, parentMenu, /* private: */_e){
2554         this.parentMenu = parentMenu;
2555         if(!this.el){
2556             this.render();
2557         }
2558         if(_e !== false){
2559             this.fireEvent("beforeshow", this);
2560             //xy = this.el.adjustForConstraints(xy);
2561         }
2562         
2563         //this.el.show();
2564         this.hideMenuItems();
2565         this.hidden = false;
2566         this.triggerEl.addClass('open');
2567         this.el.addClass('show');
2568         
2569         // reassign x when hitting right
2570         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2571             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2572         }
2573         
2574         // reassign y when hitting bottom
2575         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2576             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2577         }
2578         
2579         // but the list may align on trigger left or trigger top... should it be a properity?
2580         
2581         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2582             this.el.setXY(xy);
2583         }
2584         
2585         this.focus();
2586         this.fireEvent("show", this);
2587     },
2588     
2589     focus : function(){
2590         return;
2591         if(!this.hidden){
2592             this.doFocus.defer(50, this);
2593         }
2594     },
2595
2596     doFocus : function(){
2597         if(!this.hidden){
2598             this.focusEl.focus();
2599         }
2600     },
2601
2602     /**
2603      * Hides this menu and optionally all parent menus
2604      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2605      */
2606     hide : function(deep)
2607     {
2608         if (false === this.fireEvent("beforehide", this)) {
2609             Roo.log("hide canceled");
2610             return;
2611         }
2612         this.hideMenuItems();
2613         if(this.el && this.isVisible()){
2614            
2615             if(this.activeItem){
2616                 this.activeItem.deactivate();
2617                 this.activeItem = null;
2618             }
2619             this.triggerEl.removeClass('open');;
2620             this.el.removeClass('show');
2621             this.hidden = true;
2622             this.fireEvent("hide", this);
2623         }
2624         if(deep === true && this.parentMenu){
2625             this.parentMenu.hide(true);
2626         }
2627     },
2628     
2629     onTriggerClick : function(e)
2630     {
2631         Roo.log('trigger click');
2632         
2633         var target = e.getTarget();
2634         
2635         Roo.log(target.nodeName.toLowerCase());
2636         
2637         if(target.nodeName.toLowerCase() === 'i'){
2638             e.preventDefault();
2639         }
2640         
2641     },
2642     
2643     onTriggerPress  : function(e)
2644     {
2645         Roo.log('trigger press');
2646         //Roo.log(e.getTarget());
2647        // Roo.log(this.triggerEl.dom);
2648        
2649         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2650         var pel = Roo.get(e.getTarget());
2651         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2652             Roo.log('is treeview or dropdown?');
2653             return;
2654         }
2655         
2656         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2657             return;
2658         }
2659         
2660         if (this.isVisible()) {
2661             Roo.log('hide');
2662             this.hide();
2663         } else {
2664             Roo.log('show');
2665             this.show(this.triggerEl, '?', false);
2666         }
2667         
2668         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2669             e.stopEvent();
2670         }
2671         
2672     },
2673        
2674     
2675     hideMenuItems : function()
2676     {
2677         Roo.log("hide Menu Items");
2678         if (!this.el) { 
2679             return;
2680         }
2681         
2682         this.el.select('.open',true).each(function(aa) {
2683             
2684             aa.removeClass('open');
2685          
2686         });
2687     },
2688     addxtypeChild : function (tree, cntr) {
2689         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2690           
2691         this.menuitems.add(comp);
2692         return comp;
2693
2694     },
2695     getEl : function()
2696     {
2697         Roo.log(this.el);
2698         return this.el;
2699     },
2700     
2701     clear : function()
2702     {
2703         this.getEl().dom.innerHTML = '';
2704         this.menuitems.clear();
2705     }
2706 });
2707
2708  
2709  /*
2710  * - LGPL
2711  *
2712  * menu item
2713  * 
2714  */
2715
2716
2717 /**
2718  * @class Roo.bootstrap.MenuItem
2719  * @extends Roo.bootstrap.Component
2720  * Bootstrap MenuItem class
2721  * @cfg {String} html the menu label
2722  * @cfg {String} href the link
2723  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2724  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2725  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2726  * @cfg {String} fa favicon to show on left of menu item.
2727  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2728  * 
2729  * 
2730  * @constructor
2731  * Create a new MenuItem
2732  * @param {Object} config The config object
2733  */
2734
2735
2736 Roo.bootstrap.MenuItem = function(config){
2737     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2738     this.addEvents({
2739         // raw events
2740         /**
2741          * @event click
2742          * The raw click event for the entire grid.
2743          * @param {Roo.bootstrap.MenuItem} this
2744          * @param {Roo.EventObject} e
2745          */
2746         "click" : true
2747     });
2748 };
2749
2750 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2751     
2752     href : false,
2753     html : false,
2754     preventDefault: false,
2755     isContainer : false,
2756     active : false,
2757     fa: false,
2758     
2759     getAutoCreate : function(){
2760         
2761         if(this.isContainer){
2762             return {
2763                 tag: 'li',
2764                 cls: 'dropdown-menu-item '
2765             };
2766         }
2767         var ctag = {
2768             tag: 'span',
2769             html: 'Link'
2770         };
2771         
2772         var anc = {
2773             tag : 'a',
2774             cls : 'dropdown-item',
2775             href : '#',
2776             cn : [  ]
2777         };
2778         
2779         if (this.fa !== false) {
2780             anc.cn.push({
2781                 tag : 'i',
2782                 cls : 'fa fa-' + this.fa
2783             });
2784         }
2785         
2786         anc.cn.push(ctag);
2787         
2788         
2789         var cfg= {
2790             tag: 'li',
2791             cls: 'dropdown-menu-item',
2792             cn: [ anc ]
2793         };
2794         if (this.parent().type == 'treeview') {
2795             cfg.cls = 'treeview-menu';
2796         }
2797         if (this.active) {
2798             cfg.cls += ' active';
2799         }
2800         
2801         
2802         
2803         anc.href = this.href || cfg.cn[0].href ;
2804         ctag.html = this.html || cfg.cn[0].html ;
2805         return cfg;
2806     },
2807     
2808     initEvents: function()
2809     {
2810         if (this.parent().type == 'treeview') {
2811             this.el.select('a').on('click', this.onClick, this);
2812         }
2813         
2814         if (this.menu) {
2815             this.menu.parentType = this.xtype;
2816             this.menu.triggerEl = this.el;
2817             this.menu = this.addxtype(Roo.apply({}, this.menu));
2818         }
2819         
2820     },
2821     onClick : function(e)
2822     {
2823         Roo.log('item on click ');
2824         
2825         if(this.preventDefault){
2826             e.preventDefault();
2827         }
2828         //this.parent().hideMenuItems();
2829         
2830         this.fireEvent('click', this, e);
2831     },
2832     getEl : function()
2833     {
2834         return this.el;
2835     } 
2836 });
2837
2838  
2839
2840  /*
2841  * - LGPL
2842  *
2843  * menu separator
2844  * 
2845  */
2846
2847
2848 /**
2849  * @class Roo.bootstrap.MenuSeparator
2850  * @extends Roo.bootstrap.Component
2851  * Bootstrap MenuSeparator class
2852  * 
2853  * @constructor
2854  * Create a new MenuItem
2855  * @param {Object} config The config object
2856  */
2857
2858
2859 Roo.bootstrap.MenuSeparator = function(config){
2860     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2861 };
2862
2863 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2864     
2865     getAutoCreate : function(){
2866         var cfg = {
2867             cls: 'divider',
2868             tag : 'li'
2869         };
2870         
2871         return cfg;
2872     }
2873    
2874 });
2875
2876  
2877
2878  
2879 /*
2880 * Licence: LGPL
2881 */
2882
2883 /**
2884  * @class Roo.bootstrap.Modal
2885  * @extends Roo.bootstrap.Component
2886  * Bootstrap Modal class
2887  * @cfg {String} title Title of dialog
2888  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2889  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2890  * @cfg {Boolean} specificTitle default false
2891  * @cfg {Array} buttons Array of buttons or standard button set..
2892  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2893  * @cfg {Boolean} animate default true
2894  * @cfg {Boolean} allow_close default true
2895  * @cfg {Boolean} fitwindow default false
2896  * @cfg {Number} width fixed width - usefull for chrome extension only really.
2897  * @cfg {Number} height fixed height - usefull for chrome extension only really.
2898  * @cfg {String} size (sm|lg) default empty
2899  * @cfg {Number} max_width set the max width of modal
2900  *
2901  *
2902  * @constructor
2903  * Create a new Modal Dialog
2904  * @param {Object} config The config object
2905  */
2906
2907 Roo.bootstrap.Modal = function(config){
2908     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2909     this.addEvents({
2910         // raw events
2911         /**
2912          * @event btnclick
2913          * The raw btnclick event for the button
2914          * @param {Roo.EventObject} e
2915          */
2916         "btnclick" : true,
2917         /**
2918          * @event resize
2919          * Fire when dialog resize
2920          * @param {Roo.bootstrap.Modal} this
2921          * @param {Roo.EventObject} e
2922          */
2923         "resize" : true
2924     });
2925     this.buttons = this.buttons || [];
2926
2927     if (this.tmpl) {
2928         this.tmpl = Roo.factory(this.tmpl);
2929     }
2930
2931 };
2932
2933 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2934
2935     title : 'test dialog',
2936
2937     buttons : false,
2938
2939     // set on load...
2940
2941     html: false,
2942
2943     tmp: false,
2944
2945     specificTitle: false,
2946
2947     buttonPosition: 'right',
2948
2949     allow_close : true,
2950
2951     animate : true,
2952
2953     fitwindow: false,
2954     
2955      // private
2956     dialogEl: false,
2957     bodyEl:  false,
2958     footerEl:  false,
2959     titleEl:  false,
2960     closeEl:  false,
2961
2962     size: '',
2963     
2964     max_width: 0,
2965     
2966     max_height: 0,
2967     
2968     fit_content: false,
2969
2970     onRender : function(ct, position)
2971     {
2972         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2973
2974         if(!this.el){
2975             var cfg = Roo.apply({},  this.getAutoCreate());
2976             cfg.id = Roo.id();
2977             //if(!cfg.name){
2978             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2979             //}
2980             //if (!cfg.name.length) {
2981             //    delete cfg.name;
2982            // }
2983             if (this.cls) {
2984                 cfg.cls += ' ' + this.cls;
2985             }
2986             if (this.style) {
2987                 cfg.style = this.style;
2988             }
2989             this.el = Roo.get(document.body).createChild(cfg, position);
2990         }
2991         //var type = this.el.dom.type;
2992
2993
2994         if(this.tabIndex !== undefined){
2995             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2996         }
2997
2998         this.dialogEl = this.el.select('.modal-dialog',true).first();
2999         this.bodyEl = this.el.select('.modal-body',true).first();
3000         this.closeEl = this.el.select('.modal-header .close', true).first();
3001         this.headerEl = this.el.select('.modal-header',true).first();
3002         this.titleEl = this.el.select('.modal-title',true).first();
3003         this.footerEl = this.el.select('.modal-footer',true).first();
3004
3005         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
3006         
3007         //this.el.addClass("x-dlg-modal");
3008
3009         if (this.buttons.length) {
3010             Roo.each(this.buttons, function(bb) {
3011                 var b = Roo.apply({}, bb);
3012                 b.xns = b.xns || Roo.bootstrap;
3013                 b.xtype = b.xtype || 'Button';
3014                 if (typeof(b.listeners) == 'undefined') {
3015                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
3016                 }
3017
3018                 var btn = Roo.factory(b);
3019
3020                 btn.render(this.getButtonContainer());
3021
3022             },this);
3023         }
3024         // render the children.
3025         var nitems = [];
3026
3027         if(typeof(this.items) != 'undefined'){
3028             var items = this.items;
3029             delete this.items;
3030
3031             for(var i =0;i < items.length;i++) {
3032                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
3033             }
3034         }
3035
3036         this.items = nitems;
3037
3038         // where are these used - they used to be body/close/footer
3039
3040
3041         this.initEvents();
3042         //this.el.addClass([this.fieldClass, this.cls]);
3043
3044     },
3045
3046     getAutoCreate : function()
3047     {
3048         // we will default to modal-body-overflow - might need to remove or make optional later.
3049         var bdy = {
3050                 cls : 'modal-body enable-modal-body-overflow ', 
3051                 html : this.html || ''
3052         };
3053
3054         var title = {
3055             tag: 'h4',
3056             cls : 'modal-title',
3057             html : this.title
3058         };
3059
3060         if(this.specificTitle){
3061             title = this.title;
3062
3063         }
3064
3065         var header = [];
3066         if (this.allow_close && Roo.bootstrap.version == 3) {
3067             header.push({
3068                 tag: 'button',
3069                 cls : 'close',
3070                 html : '&times'
3071             });
3072         }
3073
3074         header.push(title);
3075
3076         if (this.allow_close && Roo.bootstrap.version == 4) {
3077             header.push({
3078                 tag: 'button',
3079                 cls : 'close',
3080                 html : '&times'
3081             });
3082         }
3083         
3084         var size = '';
3085
3086         if(this.size.length){
3087             size = 'modal-' + this.size;
3088         }
3089         
3090         var footer = Roo.bootstrap.version == 3 ?
3091             {
3092                 cls : 'modal-footer',
3093                 cn : [
3094                     {
3095                         tag: 'div',
3096                         cls: 'btn-' + this.buttonPosition
3097                     }
3098                 ]
3099
3100             } :
3101             {  // BS4 uses mr-auto on left buttons....
3102                 cls : 'modal-footer'
3103             };
3104
3105             
3106
3107         
3108         
3109         var modal = {
3110             cls: "modal",
3111              cn : [
3112                 {
3113                     cls: "modal-dialog " + size,
3114                     cn : [
3115                         {
3116                             cls : "modal-content",
3117                             cn : [
3118                                 {
3119                                     cls : 'modal-header',
3120                                     cn : header
3121                                 },
3122                                 bdy,
3123                                 footer
3124                             ]
3125
3126                         }
3127                     ]
3128
3129                 }
3130             ]
3131         };
3132
3133         if(this.animate){
3134             modal.cls += ' fade';
3135         }
3136
3137         return modal;
3138
3139     },
3140     getChildContainer : function() {
3141
3142          return this.bodyEl;
3143
3144     },
3145     getButtonContainer : function() {
3146         
3147          return Roo.bootstrap.version == 4 ?
3148             this.el.select('.modal-footer',true).first()
3149             : this.el.select('.modal-footer div',true).first();
3150
3151     },
3152     initEvents : function()
3153     {
3154         if (this.allow_close) {
3155             this.closeEl.on('click', this.hide, this);
3156         }
3157         Roo.EventManager.onWindowResize(this.resize, this, true);
3158
3159
3160     },
3161   
3162
3163     resize : function()
3164     {
3165         this.maskEl.setSize(
3166             Roo.lib.Dom.getViewWidth(true),
3167             Roo.lib.Dom.getViewHeight(true)
3168         );
3169         
3170         if (this.fitwindow) {
3171             
3172            
3173             this.setSize(
3174                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
3175                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
3176             );
3177             return;
3178         }
3179         
3180         if(this.max_width !== 0) {
3181             
3182             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
3183             
3184             if(this.height) {
3185                 this.setSize(w, this.height);
3186                 return;
3187             }
3188             
3189             if(this.max_height) {
3190                 this.setSize(w,Math.min(
3191                     this.max_height,
3192                     Roo.lib.Dom.getViewportHeight(true) - 60
3193                 ));
3194                 
3195                 return;
3196             }
3197             
3198             if(!this.fit_content) {
3199                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
3200                 return;
3201             }
3202             
3203             this.setSize(w, Math.min(
3204                 60 +
3205                 this.headerEl.getHeight() + 
3206                 this.footerEl.getHeight() + 
3207                 this.getChildHeight(this.bodyEl.dom.childNodes),
3208                 Roo.lib.Dom.getViewportHeight(true) - 60)
3209             );
3210         }
3211         
3212     },
3213
3214     setSize : function(w,h)
3215     {
3216         if (!w && !h) {
3217             return;
3218         }
3219         
3220         this.resizeTo(w,h);
3221     },
3222
3223     show : function() {
3224
3225         if (!this.rendered) {
3226             this.render();
3227         }
3228
3229         //this.el.setStyle('display', 'block');
3230         this.el.removeClass('hideing');
3231         this.el.dom.style.display='block';
3232         
3233         Roo.get(document.body).addClass('modal-open');
3234  
3235         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
3236             
3237             (function(){
3238                 this.el.addClass('show');
3239                 this.el.addClass('in');
3240             }).defer(50, this);
3241         }else{
3242             this.el.addClass('show');
3243             this.el.addClass('in');
3244         }
3245
3246         // not sure how we can show data in here..
3247         //if (this.tmpl) {
3248         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3249         //}
3250
3251         Roo.get(document.body).addClass("x-body-masked");
3252         
3253         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
3254         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3255         this.maskEl.dom.style.display = 'block';
3256         this.maskEl.addClass('show');
3257         
3258         
3259         this.resize();
3260         
3261         this.fireEvent('show', this);
3262
3263         // set zindex here - otherwise it appears to be ignored...
3264         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3265
3266         (function () {
3267             this.items.forEach( function(e) {
3268                 e.layout ? e.layout() : false;
3269
3270             });
3271         }).defer(100,this);
3272
3273     },
3274     hide : function()
3275     {
3276         if(this.fireEvent("beforehide", this) !== false){
3277             
3278             this.maskEl.removeClass('show');
3279             
3280             this.maskEl.dom.style.display = '';
3281             Roo.get(document.body).removeClass("x-body-masked");
3282             this.el.removeClass('in');
3283             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3284
3285             if(this.animate){ // why
3286                 this.el.addClass('hideing');
3287                 this.el.removeClass('show');
3288                 (function(){
3289                     if (!this.el.hasClass('hideing')) {
3290                         return; // it's been shown again...
3291                     }
3292                     
3293                     this.el.dom.style.display='';
3294
3295                     Roo.get(document.body).removeClass('modal-open');
3296                     this.el.removeClass('hideing');
3297                 }).defer(150,this);
3298                 
3299             }else{
3300                 this.el.removeClass('show');
3301                 this.el.dom.style.display='';
3302                 Roo.get(document.body).removeClass('modal-open');
3303
3304             }
3305             this.fireEvent('hide', this);
3306         }
3307     },
3308     isVisible : function()
3309     {
3310         
3311         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3312         
3313     },
3314
3315     addButton : function(str, cb)
3316     {
3317
3318
3319         var b = Roo.apply({}, { html : str } );
3320         b.xns = b.xns || Roo.bootstrap;
3321         b.xtype = b.xtype || 'Button';
3322         if (typeof(b.listeners) == 'undefined') {
3323             b.listeners = { click : cb.createDelegate(this)  };
3324         }
3325
3326         var btn = Roo.factory(b);
3327
3328         btn.render(this.getButtonContainer());
3329
3330         return btn;
3331
3332     },
3333
3334     setDefaultButton : function(btn)
3335     {
3336         //this.el.select('.modal-footer').()
3337     },
3338
3339     resizeTo: function(w,h)
3340     {
3341         this.dialogEl.setWidth(w);
3342         
3343         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
3344
3345         this.bodyEl.setHeight(h - diff);
3346         
3347         this.fireEvent('resize', this);
3348     },
3349     
3350     setContentSize  : function(w, h)
3351     {
3352
3353     },
3354     onButtonClick: function(btn,e)
3355     {
3356         //Roo.log([a,b,c]);
3357         this.fireEvent('btnclick', btn.name, e);
3358     },
3359      /**
3360      * Set the title of the Dialog
3361      * @param {String} str new Title
3362      */
3363     setTitle: function(str) {
3364         this.titleEl.dom.innerHTML = str;
3365     },
3366     /**
3367      * Set the body of the Dialog
3368      * @param {String} str new Title
3369      */
3370     setBody: function(str) {
3371         this.bodyEl.dom.innerHTML = str;
3372     },
3373     /**
3374      * Set the body of the Dialog using the template
3375      * @param {Obj} data - apply this data to the template and replace the body contents.
3376      */
3377     applyBody: function(obj)
3378     {
3379         if (!this.tmpl) {
3380             Roo.log("Error - using apply Body without a template");
3381             //code
3382         }
3383         this.tmpl.overwrite(this.bodyEl, obj);
3384     },
3385     
3386     getChildHeight : function(child_nodes)
3387     {
3388         if(
3389             !child_nodes ||
3390             child_nodes.length == 0
3391         ) {
3392             return;
3393         }
3394         
3395         var child_height = 0;
3396         
3397         for(var i = 0; i < child_nodes.length; i++) {
3398             
3399             /*
3400             * for modal with tabs...
3401             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3402                 
3403                 var layout_childs = child_nodes[i].childNodes;
3404                 
3405                 for(var j = 0; j < layout_childs.length; j++) {
3406                     
3407                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3408                         
3409                         var layout_body_childs = layout_childs[j].childNodes;
3410                         
3411                         for(var k = 0; k < layout_body_childs.length; k++) {
3412                             
3413                             if(layout_body_childs[k].classList.contains('navbar')) {
3414                                 child_height += layout_body_childs[k].offsetHeight;
3415                                 continue;
3416                             }
3417                             
3418                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3419                                 
3420                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3421                                 
3422                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3423                                     
3424                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3425                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3426                                         continue;
3427                                     }
3428                                     
3429                                 }
3430                                 
3431                             }
3432                             
3433                         }
3434                     }
3435                 }
3436                 continue;
3437             }
3438             */
3439             
3440             child_height += child_nodes[i].offsetHeight;
3441             // Roo.log(child_nodes[i].offsetHeight);
3442         }
3443         
3444         return child_height;
3445     }
3446
3447 });
3448
3449
3450 Roo.apply(Roo.bootstrap.Modal,  {
3451     /**
3452          * Button config that displays a single OK button
3453          * @type Object
3454          */
3455         OK :  [{
3456             name : 'ok',
3457             weight : 'primary',
3458             html : 'OK'
3459         }],
3460         /**
3461          * Button config that displays Yes and No buttons
3462          * @type Object
3463          */
3464         YESNO : [
3465             {
3466                 name  : 'no',
3467                 html : 'No'
3468             },
3469             {
3470                 name  :'yes',
3471                 weight : 'primary',
3472                 html : 'Yes'
3473             }
3474         ],
3475
3476         /**
3477          * Button config that displays OK and Cancel buttons
3478          * @type Object
3479          */
3480         OKCANCEL : [
3481             {
3482                name : 'cancel',
3483                 html : 'Cancel'
3484             },
3485             {
3486                 name : 'ok',
3487                 weight : 'primary',
3488                 html : 'OK'
3489             }
3490         ],
3491         /**
3492          * Button config that displays Yes, No and Cancel buttons
3493          * @type Object
3494          */
3495         YESNOCANCEL : [
3496             {
3497                 name : 'yes',
3498                 weight : 'primary',
3499                 html : 'Yes'
3500             },
3501             {
3502                 name : 'no',
3503                 html : 'No'
3504             },
3505             {
3506                 name : 'cancel',
3507                 html : 'Cancel'
3508             }
3509         ],
3510         
3511         zIndex : 10001
3512 });
3513 /*
3514  * - LGPL
3515  *
3516  * messagebox - can be used as a replace
3517  * 
3518  */
3519 /**
3520  * @class Roo.MessageBox
3521  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3522  * Example usage:
3523  *<pre><code>
3524 // Basic alert:
3525 Roo.Msg.alert('Status', 'Changes saved successfully.');
3526
3527 // Prompt for user data:
3528 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3529     if (btn == 'ok'){
3530         // process text value...
3531     }
3532 });
3533
3534 // Show a dialog using config options:
3535 Roo.Msg.show({
3536    title:'Save Changes?',
3537    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3538    buttons: Roo.Msg.YESNOCANCEL,
3539    fn: processResult,
3540    animEl: 'elId'
3541 });
3542 </code></pre>
3543  * @singleton
3544  */
3545 Roo.bootstrap.MessageBox = function(){
3546     var dlg, opt, mask, waitTimer;
3547     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3548     var buttons, activeTextEl, bwidth;
3549
3550     
3551     // private
3552     var handleButton = function(button){
3553         dlg.hide();
3554         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3555     };
3556
3557     // private
3558     var handleHide = function(){
3559         if(opt && opt.cls){
3560             dlg.el.removeClass(opt.cls);
3561         }
3562         //if(waitTimer){
3563         //    Roo.TaskMgr.stop(waitTimer);
3564         //    waitTimer = null;
3565         //}
3566     };
3567
3568     // private
3569     var updateButtons = function(b){
3570         var width = 0;
3571         if(!b){
3572             buttons["ok"].hide();
3573             buttons["cancel"].hide();
3574             buttons["yes"].hide();
3575             buttons["no"].hide();
3576             dlg.footerEl.hide();
3577             
3578             return width;
3579         }
3580         dlg.footerEl.show();
3581         for(var k in buttons){
3582             if(typeof buttons[k] != "function"){
3583                 if(b[k]){
3584                     buttons[k].show();
3585                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3586                     width += buttons[k].el.getWidth()+15;
3587                 }else{
3588                     buttons[k].hide();
3589                 }
3590             }
3591         }
3592         return width;
3593     };
3594
3595     // private
3596     var handleEsc = function(d, k, e){
3597         if(opt && opt.closable !== false){
3598             dlg.hide();
3599         }
3600         if(e){
3601             e.stopEvent();
3602         }
3603     };
3604
3605     return {
3606         /**
3607          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3608          * @return {Roo.BasicDialog} The BasicDialog element
3609          */
3610         getDialog : function(){
3611            if(!dlg){
3612                 dlg = new Roo.bootstrap.Modal( {
3613                     //draggable: true,
3614                     //resizable:false,
3615                     //constraintoviewport:false,
3616                     //fixedcenter:true,
3617                     //collapsible : false,
3618                     //shim:true,
3619                     //modal: true,
3620                 //    width: 'auto',
3621                   //  height:100,
3622                     //buttonAlign:"center",
3623                     closeClick : function(){
3624                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3625                             handleButton("no");
3626                         }else{
3627                             handleButton("cancel");
3628                         }
3629                     }
3630                 });
3631                 dlg.render();
3632                 dlg.on("hide", handleHide);
3633                 mask = dlg.mask;
3634                 //dlg.addKeyListener(27, handleEsc);
3635                 buttons = {};
3636                 this.buttons = buttons;
3637                 var bt = this.buttonText;
3638                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3639                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3640                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3641                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3642                 //Roo.log(buttons);
3643                 bodyEl = dlg.bodyEl.createChild({
3644
3645                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3646                         '<textarea class="roo-mb-textarea"></textarea>' +
3647                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3648                 });
3649                 msgEl = bodyEl.dom.firstChild;
3650                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3651                 textboxEl.enableDisplayMode();
3652                 textboxEl.addKeyListener([10,13], function(){
3653                     if(dlg.isVisible() && opt && opt.buttons){
3654                         if(opt.buttons.ok){
3655                             handleButton("ok");
3656                         }else if(opt.buttons.yes){
3657                             handleButton("yes");
3658                         }
3659                     }
3660                 });
3661                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3662                 textareaEl.enableDisplayMode();
3663                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3664                 progressEl.enableDisplayMode();
3665                 
3666                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3667                 var pf = progressEl.dom.firstChild;
3668                 if (pf) {
3669                     pp = Roo.get(pf.firstChild);
3670                     pp.setHeight(pf.offsetHeight);
3671                 }
3672                 
3673             }
3674             return dlg;
3675         },
3676
3677         /**
3678          * Updates the message box body text
3679          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3680          * the XHTML-compliant non-breaking space character '&amp;#160;')
3681          * @return {Roo.MessageBox} This message box
3682          */
3683         updateText : function(text)
3684         {
3685             if(!dlg.isVisible() && !opt.width){
3686                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3687                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3688             }
3689             msgEl.innerHTML = text || '&#160;';
3690       
3691             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3692             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3693             var w = Math.max(
3694                     Math.min(opt.width || cw , this.maxWidth), 
3695                     Math.max(opt.minWidth || this.minWidth, bwidth)
3696             );
3697             if(opt.prompt){
3698                 activeTextEl.setWidth(w);
3699             }
3700             if(dlg.isVisible()){
3701                 dlg.fixedcenter = false;
3702             }
3703             // to big, make it scroll. = But as usual stupid IE does not support
3704             // !important..
3705             
3706             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3707                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3708                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3709             } else {
3710                 bodyEl.dom.style.height = '';
3711                 bodyEl.dom.style.overflowY = '';
3712             }
3713             if (cw > w) {
3714                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3715             } else {
3716                 bodyEl.dom.style.overflowX = '';
3717             }
3718             
3719             dlg.setContentSize(w, bodyEl.getHeight());
3720             if(dlg.isVisible()){
3721                 dlg.fixedcenter = true;
3722             }
3723             return this;
3724         },
3725
3726         /**
3727          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3728          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3729          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3730          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3731          * @return {Roo.MessageBox} This message box
3732          */
3733         updateProgress : function(value, text){
3734             if(text){
3735                 this.updateText(text);
3736             }
3737             
3738             if (pp) { // weird bug on my firefox - for some reason this is not defined
3739                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3740                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3741             }
3742             return this;
3743         },        
3744
3745         /**
3746          * Returns true if the message box is currently displayed
3747          * @return {Boolean} True if the message box is visible, else false
3748          */
3749         isVisible : function(){
3750             return dlg && dlg.isVisible();  
3751         },
3752
3753         /**
3754          * Hides the message box if it is displayed
3755          */
3756         hide : function(){
3757             if(this.isVisible()){
3758                 dlg.hide();
3759             }  
3760         },
3761
3762         /**
3763          * Displays a new message box, or reinitializes an existing message box, based on the config options
3764          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3765          * The following config object properties are supported:
3766          * <pre>
3767 Property    Type             Description
3768 ----------  ---------------  ------------------------------------------------------------------------------------
3769 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3770                                    closes (defaults to undefined)
3771 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3772                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3773 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3774                                    progress and wait dialogs will ignore this property and always hide the
3775                                    close button as they can only be closed programmatically.
3776 cls               String           A custom CSS class to apply to the message box element
3777 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3778                                    displayed (defaults to 75)
3779 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3780                                    function will be btn (the name of the button that was clicked, if applicable,
3781                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3782                                    Progress and wait dialogs will ignore this option since they do not respond to
3783                                    user actions and can only be closed programmatically, so any required function
3784                                    should be called by the same code after it closes the dialog.
3785 icon              String           A CSS class that provides a background image to be used as an icon for
3786                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3787 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3788 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3789 modal             Boolean          False to allow user interaction with the page while the message box is
3790                                    displayed (defaults to true)
3791 msg               String           A string that will replace the existing message box body text (defaults
3792                                    to the XHTML-compliant non-breaking space character '&#160;')
3793 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3794 progress          Boolean          True to display a progress bar (defaults to false)
3795 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3796 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3797 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3798 title             String           The title text
3799 value             String           The string value to set into the active textbox element if displayed
3800 wait              Boolean          True to display a progress bar (defaults to false)
3801 width             Number           The width of the dialog in pixels
3802 </pre>
3803          *
3804          * Example usage:
3805          * <pre><code>
3806 Roo.Msg.show({
3807    title: 'Address',
3808    msg: 'Please enter your address:',
3809    width: 300,
3810    buttons: Roo.MessageBox.OKCANCEL,
3811    multiline: true,
3812    fn: saveAddress,
3813    animEl: 'addAddressBtn'
3814 });
3815 </code></pre>
3816          * @param {Object} config Configuration options
3817          * @return {Roo.MessageBox} This message box
3818          */
3819         show : function(options)
3820         {
3821             
3822             // this causes nightmares if you show one dialog after another
3823             // especially on callbacks..
3824              
3825             if(this.isVisible()){
3826                 
3827                 this.hide();
3828                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3829                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3830                 Roo.log("New Dialog Message:" +  options.msg )
3831                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3832                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3833                 
3834             }
3835             var d = this.getDialog();
3836             opt = options;
3837             d.setTitle(opt.title || "&#160;");
3838             d.closeEl.setDisplayed(opt.closable !== false);
3839             activeTextEl = textboxEl;
3840             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3841             if(opt.prompt){
3842                 if(opt.multiline){
3843                     textboxEl.hide();
3844                     textareaEl.show();
3845                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3846                         opt.multiline : this.defaultTextHeight);
3847                     activeTextEl = textareaEl;
3848                 }else{
3849                     textboxEl.show();
3850                     textareaEl.hide();
3851                 }
3852             }else{
3853                 textboxEl.hide();
3854                 textareaEl.hide();
3855             }
3856             progressEl.setDisplayed(opt.progress === true);
3857             if (opt.progress) {
3858                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3859             }
3860             this.updateProgress(0);
3861             activeTextEl.dom.value = opt.value || "";
3862             if(opt.prompt){
3863                 dlg.setDefaultButton(activeTextEl);
3864             }else{
3865                 var bs = opt.buttons;
3866                 var db = null;
3867                 if(bs && bs.ok){
3868                     db = buttons["ok"];
3869                 }else if(bs && bs.yes){
3870                     db = buttons["yes"];
3871                 }
3872                 dlg.setDefaultButton(db);
3873             }
3874             bwidth = updateButtons(opt.buttons);
3875             this.updateText(opt.msg);
3876             if(opt.cls){
3877                 d.el.addClass(opt.cls);
3878             }
3879             d.proxyDrag = opt.proxyDrag === true;
3880             d.modal = opt.modal !== false;
3881             d.mask = opt.modal !== false ? mask : false;
3882             if(!d.isVisible()){
3883                 // force it to the end of the z-index stack so it gets a cursor in FF
3884                 document.body.appendChild(dlg.el.dom);
3885                 d.animateTarget = null;
3886                 d.show(options.animEl);
3887             }
3888             return this;
3889         },
3890
3891         /**
3892          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3893          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3894          * and closing the message box when the process is complete.
3895          * @param {String} title The title bar text
3896          * @param {String} msg The message box body text
3897          * @return {Roo.MessageBox} This message box
3898          */
3899         progress : function(title, msg){
3900             this.show({
3901                 title : title,
3902                 msg : msg,
3903                 buttons: false,
3904                 progress:true,
3905                 closable:false,
3906                 minWidth: this.minProgressWidth,
3907                 modal : true
3908             });
3909             return this;
3910         },
3911
3912         /**
3913          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3914          * If a callback function is passed it will be called after the user clicks the button, and the
3915          * id of the button that was clicked will be passed as the only parameter to the callback
3916          * (could also be the top-right close button).
3917          * @param {String} title The title bar text
3918          * @param {String} msg The message box body text
3919          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3920          * @param {Object} scope (optional) The scope of the callback function
3921          * @return {Roo.MessageBox} This message box
3922          */
3923         alert : function(title, msg, fn, scope)
3924         {
3925             this.show({
3926                 title : title,
3927                 msg : msg,
3928                 buttons: this.OK,
3929                 fn: fn,
3930                 closable : false,
3931                 scope : scope,
3932                 modal : true
3933             });
3934             return this;
3935         },
3936
3937         /**
3938          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3939          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3940          * You are responsible for closing the message box when the process is complete.
3941          * @param {String} msg The message box body text
3942          * @param {String} title (optional) The title bar text
3943          * @return {Roo.MessageBox} This message box
3944          */
3945         wait : function(msg, title){
3946             this.show({
3947                 title : title,
3948                 msg : msg,
3949                 buttons: false,
3950                 closable:false,
3951                 progress:true,
3952                 modal:true,
3953                 width:300,
3954                 wait:true
3955             });
3956             waitTimer = Roo.TaskMgr.start({
3957                 run: function(i){
3958                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3959                 },
3960                 interval: 1000
3961             });
3962             return this;
3963         },
3964
3965         /**
3966          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3967          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3968          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3969          * @param {String} title The title bar text
3970          * @param {String} msg The message box body text
3971          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3972          * @param {Object} scope (optional) The scope of the callback function
3973          * @return {Roo.MessageBox} This message box
3974          */
3975         confirm : function(title, msg, fn, scope){
3976             this.show({
3977                 title : title,
3978                 msg : msg,
3979                 buttons: this.YESNO,
3980                 fn: fn,
3981                 scope : scope,
3982                 modal : true
3983             });
3984             return this;
3985         },
3986
3987         /**
3988          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3989          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3990          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3991          * (could also be the top-right close button) and the text that was entered will be passed as the two
3992          * parameters to the callback.
3993          * @param {String} title The title bar text
3994          * @param {String} msg The message box body text
3995          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3996          * @param {Object} scope (optional) The scope of the callback function
3997          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3998          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3999          * @return {Roo.MessageBox} This message box
4000          */
4001         prompt : function(title, msg, fn, scope, multiline){
4002             this.show({
4003                 title : title,
4004                 msg : msg,
4005                 buttons: this.OKCANCEL,
4006                 fn: fn,
4007                 minWidth:250,
4008                 scope : scope,
4009                 prompt:true,
4010                 multiline: multiline,
4011                 modal : true
4012             });
4013             return this;
4014         },
4015
4016         /**
4017          * Button config that displays a single OK button
4018          * @type Object
4019          */
4020         OK : {ok:true},
4021         /**
4022          * Button config that displays Yes and No buttons
4023          * @type Object
4024          */
4025         YESNO : {yes:true, no:true},
4026         /**
4027          * Button config that displays OK and Cancel buttons
4028          * @type Object
4029          */
4030         OKCANCEL : {ok:true, cancel:true},
4031         /**
4032          * Button config that displays Yes, No and Cancel buttons
4033          * @type Object
4034          */
4035         YESNOCANCEL : {yes:true, no:true, cancel:true},
4036
4037         /**
4038          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
4039          * @type Number
4040          */
4041         defaultTextHeight : 75,
4042         /**
4043          * The maximum width in pixels of the message box (defaults to 600)
4044          * @type Number
4045          */
4046         maxWidth : 600,
4047         /**
4048          * The minimum width in pixels of the message box (defaults to 100)
4049          * @type Number
4050          */
4051         minWidth : 100,
4052         /**
4053          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
4054          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
4055          * @type Number
4056          */
4057         minProgressWidth : 250,
4058         /**
4059          * An object containing the default button text strings that can be overriden for localized language support.
4060          * Supported properties are: ok, cancel, yes and no.
4061          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
4062          * @type Object
4063          */
4064         buttonText : {
4065             ok : "OK",
4066             cancel : "Cancel",
4067             yes : "Yes",
4068             no : "No"
4069         }
4070     };
4071 }();
4072
4073 /**
4074  * Shorthand for {@link Roo.MessageBox}
4075  */
4076 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
4077 Roo.Msg = Roo.Msg || Roo.MessageBox;
4078 /*
4079  * - LGPL
4080  *
4081  * navbar
4082  * 
4083  */
4084
4085 /**
4086  * @class Roo.bootstrap.Navbar
4087  * @extends Roo.bootstrap.Component
4088  * Bootstrap Navbar class
4089
4090  * @constructor
4091  * Create a new Navbar
4092  * @param {Object} config The config object
4093  */
4094
4095
4096 Roo.bootstrap.Navbar = function(config){
4097     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
4098     this.addEvents({
4099         // raw events
4100         /**
4101          * @event beforetoggle
4102          * Fire before toggle the menu
4103          * @param {Roo.EventObject} e
4104          */
4105         "beforetoggle" : true
4106     });
4107 };
4108
4109 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
4110     
4111     
4112    
4113     // private
4114     navItems : false,
4115     loadMask : false,
4116     
4117     
4118     getAutoCreate : function(){
4119         
4120         
4121         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
4122         
4123     },
4124     
4125     initEvents :function ()
4126     {
4127         //Roo.log(this.el.select('.navbar-toggle',true));
4128         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
4129         
4130         var mark = {
4131             tag: "div",
4132             cls:"x-dlg-mask"
4133         };
4134         
4135         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
4136         
4137         var size = this.el.getSize();
4138         this.maskEl.setSize(size.width, size.height);
4139         this.maskEl.enableDisplayMode("block");
4140         this.maskEl.hide();
4141         
4142         if(this.loadMask){
4143             this.maskEl.show();
4144         }
4145     },
4146     
4147     
4148     getChildContainer : function()
4149     {
4150         if (this.el && this.el.select('.collapse').getCount()) {
4151             return this.el.select('.collapse',true).first();
4152         }
4153         
4154         return this.el;
4155     },
4156     
4157     mask : function()
4158     {
4159         this.maskEl.show();
4160     },
4161     
4162     unmask : function()
4163     {
4164         this.maskEl.hide();
4165     },
4166     onToggle : function()
4167     {
4168         
4169         if(this.fireEvent('beforetoggle', this) === false){
4170             return;
4171         }
4172         var ce = this.el.select('.navbar-collapse',true).first();
4173       
4174         if (!ce.hasClass('show')) {
4175            this.expand();
4176         } else {
4177             this.collapse();
4178         }
4179         
4180         
4181     
4182     },
4183     /**
4184      * Expand the navbar pulldown 
4185      */
4186     expand : function ()
4187     {
4188        
4189         var ce = this.el.select('.navbar-collapse',true).first();
4190         if (ce.hasClass('collapsing')) {
4191             return;
4192         }
4193         ce.dom.style.height = '';
4194                // show it...
4195         ce.addClass('in'); // old...
4196         ce.removeClass('collapse');
4197         ce.addClass('show');
4198         var h = ce.getHeight();
4199         Roo.log(h);
4200         ce.removeClass('show');
4201         // at this point we should be able to see it..
4202         ce.addClass('collapsing');
4203         
4204         ce.setHeight(0); // resize it ...
4205         ce.on('transitionend', function() {
4206             //Roo.log('done transition');
4207             ce.removeClass('collapsing');
4208             ce.addClass('show');
4209             ce.removeClass('collapse');
4210
4211             ce.dom.style.height = '';
4212         }, this, { single: true} );
4213         ce.setHeight(h);
4214         ce.dom.scrollTop = 0;
4215     },
4216     /**
4217      * Collapse the navbar pulldown 
4218      */
4219     collapse : function()
4220     {
4221          var ce = this.el.select('.navbar-collapse',true).first();
4222        
4223         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
4224             // it's collapsed or collapsing..
4225             return;
4226         }
4227         ce.removeClass('in'); // old...
4228         ce.setHeight(ce.getHeight());
4229         ce.removeClass('show');
4230         ce.addClass('collapsing');
4231         
4232         ce.on('transitionend', function() {
4233             ce.dom.style.height = '';
4234             ce.removeClass('collapsing');
4235             ce.addClass('collapse');
4236         }, this, { single: true} );
4237         ce.setHeight(0);
4238     }
4239     
4240     
4241     
4242 });
4243
4244
4245
4246  
4247
4248  /*
4249  * - LGPL
4250  *
4251  * navbar
4252  * 
4253  */
4254
4255 /**
4256  * @class Roo.bootstrap.NavSimplebar
4257  * @extends Roo.bootstrap.Navbar
4258  * Bootstrap Sidebar class
4259  *
4260  * @cfg {Boolean} inverse is inverted color
4261  * 
4262  * @cfg {String} type (nav | pills | tabs)
4263  * @cfg {Boolean} arrangement stacked | justified
4264  * @cfg {String} align (left | right) alignment
4265  * 
4266  * @cfg {Boolean} main (true|false) main nav bar? default false
4267  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
4268  * 
4269  * @cfg {String} tag (header|footer|nav|div) default is nav 
4270
4271  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4272  * 
4273  * 
4274  * @constructor
4275  * Create a new Sidebar
4276  * @param {Object} config The config object
4277  */
4278
4279
4280 Roo.bootstrap.NavSimplebar = function(config){
4281     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4282 };
4283
4284 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
4285     
4286     inverse: false,
4287     
4288     type: false,
4289     arrangement: '',
4290     align : false,
4291     
4292     weight : 'light',
4293     
4294     main : false,
4295     
4296     
4297     tag : false,
4298     
4299     
4300     getAutoCreate : function(){
4301         
4302         
4303         var cfg = {
4304             tag : this.tag || 'div',
4305             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
4306         };
4307         if (['light','white'].indexOf(this.weight) > -1) {
4308             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4309         }
4310         cfg.cls += ' bg-' + this.weight;
4311         
4312         if (this.inverse) {
4313             cfg.cls += ' navbar-inverse';
4314             
4315         }
4316         
4317         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4318         
4319         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
4320             return cfg;
4321         }
4322         
4323         
4324     
4325         
4326         cfg.cn = [
4327             {
4328                 cls: 'nav nav-' + this.xtype,
4329                 tag : 'ul'
4330             }
4331         ];
4332         
4333          
4334         this.type = this.type || 'nav';
4335         if (['tabs','pills'].indexOf(this.type) != -1) {
4336             cfg.cn[0].cls += ' nav-' + this.type
4337         
4338         
4339         } else {
4340             if (this.type!=='nav') {
4341                 Roo.log('nav type must be nav/tabs/pills')
4342             }
4343             cfg.cn[0].cls += ' navbar-nav'
4344         }
4345         
4346         
4347         
4348         
4349         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4350             cfg.cn[0].cls += ' nav-' + this.arrangement;
4351         }
4352         
4353         
4354         if (this.align === 'right') {
4355             cfg.cn[0].cls += ' navbar-right';
4356         }
4357         
4358         
4359         
4360         
4361         return cfg;
4362     
4363         
4364     }
4365     
4366     
4367     
4368 });
4369
4370
4371
4372  
4373
4374  
4375        /*
4376  * - LGPL
4377  *
4378  * navbar
4379  * navbar-fixed-top
4380  * navbar-expand-md  fixed-top 
4381  */
4382
4383 /**
4384  * @class Roo.bootstrap.NavHeaderbar
4385  * @extends Roo.bootstrap.NavSimplebar
4386  * Bootstrap Sidebar class
4387  *
4388  * @cfg {String} brand what is brand
4389  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4390  * @cfg {String} brand_href href of the brand
4391  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4392  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4393  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4394  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4395  * 
4396  * @constructor
4397  * Create a new Sidebar
4398  * @param {Object} config The config object
4399  */
4400
4401
4402 Roo.bootstrap.NavHeaderbar = function(config){
4403     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4404       
4405 };
4406
4407 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4408     
4409     position: '',
4410     brand: '',
4411     brand_href: false,
4412     srButton : true,
4413     autohide : false,
4414     desktopCenter : false,
4415    
4416     
4417     getAutoCreate : function(){
4418         
4419         var   cfg = {
4420             tag: this.nav || 'nav',
4421             cls: 'navbar navbar-expand-md',
4422             role: 'navigation',
4423             cn: []
4424         };
4425         
4426         var cn = cfg.cn;
4427         if (this.desktopCenter) {
4428             cn.push({cls : 'container', cn : []});
4429             cn = cn[0].cn;
4430         }
4431         
4432         if(this.srButton){
4433             var btn = {
4434                 tag: 'button',
4435                 type: 'button',
4436                 cls: 'navbar-toggle navbar-toggler',
4437                 'data-toggle': 'collapse',
4438                 cn: [
4439                     {
4440                         tag: 'span',
4441                         cls: 'sr-only',
4442                         html: 'Toggle navigation'
4443                     },
4444                     {
4445                         tag: 'span',
4446                         cls: 'icon-bar navbar-toggler-icon'
4447                     },
4448                     {
4449                         tag: 'span',
4450                         cls: 'icon-bar'
4451                     },
4452                     {
4453                         tag: 'span',
4454                         cls: 'icon-bar'
4455                     }
4456                 ]
4457             };
4458             
4459             cn.push( Roo.bootstrap.version == 4 ? btn : {
4460                 tag: 'div',
4461                 cls: 'navbar-header',
4462                 cn: [
4463                     btn
4464                 ]
4465             });
4466         }
4467         
4468         cn.push({
4469             tag: 'div',
4470             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
4471             cn : []
4472         });
4473         
4474         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4475         
4476         if (['light','white'].indexOf(this.weight) > -1) {
4477             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4478         }
4479         cfg.cls += ' bg-' + this.weight;
4480         
4481         
4482         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4483             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4484             
4485             // tag can override this..
4486             
4487             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4488         }
4489         
4490         if (this.brand !== '') {
4491             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4492             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4493                 tag: 'a',
4494                 href: this.brand_href ? this.brand_href : '#',
4495                 cls: 'navbar-brand',
4496                 cn: [
4497                 this.brand
4498                 ]
4499             });
4500         }
4501         
4502         if(this.main){
4503             cfg.cls += ' main-nav';
4504         }
4505         
4506         
4507         return cfg;
4508
4509         
4510     },
4511     getHeaderChildContainer : function()
4512     {
4513         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4514             return this.el.select('.navbar-header',true).first();
4515         }
4516         
4517         return this.getChildContainer();
4518     },
4519     
4520     
4521     initEvents : function()
4522     {
4523         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4524         
4525         if (this.autohide) {
4526             
4527             var prevScroll = 0;
4528             var ft = this.el;
4529             
4530             Roo.get(document).on('scroll',function(e) {
4531                 var ns = Roo.get(document).getScroll().top;
4532                 var os = prevScroll;
4533                 prevScroll = ns;
4534                 
4535                 if(ns > os){
4536                     ft.removeClass('slideDown');
4537                     ft.addClass('slideUp');
4538                     return;
4539                 }
4540                 ft.removeClass('slideUp');
4541                 ft.addClass('slideDown');
4542                  
4543               
4544           },this);
4545         }
4546     }    
4547     
4548 });
4549
4550
4551
4552  
4553
4554  /*
4555  * - LGPL
4556  *
4557  * navbar
4558  * 
4559  */
4560
4561 /**
4562  * @class Roo.bootstrap.NavSidebar
4563  * @extends Roo.bootstrap.Navbar
4564  * Bootstrap Sidebar class
4565  * 
4566  * @constructor
4567  * Create a new Sidebar
4568  * @param {Object} config The config object
4569  */
4570
4571
4572 Roo.bootstrap.NavSidebar = function(config){
4573     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4574 };
4575
4576 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4577     
4578     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4579     
4580     getAutoCreate : function(){
4581         
4582         
4583         return  {
4584             tag: 'div',
4585             cls: 'sidebar sidebar-nav'
4586         };
4587     
4588         
4589     }
4590     
4591     
4592     
4593 });
4594
4595
4596
4597  
4598
4599  /*
4600  * - LGPL
4601  *
4602  * nav group
4603  * 
4604  */
4605
4606 /**
4607  * @class Roo.bootstrap.NavGroup
4608  * @extends Roo.bootstrap.Component
4609  * Bootstrap NavGroup class
4610  * @cfg {String} align (left|right)
4611  * @cfg {Boolean} inverse
4612  * @cfg {String} type (nav|pills|tab) default nav
4613  * @cfg {String} navId - reference Id for navbar.
4614
4615  * 
4616  * @constructor
4617  * Create a new nav group
4618  * @param {Object} config The config object
4619  */
4620
4621 Roo.bootstrap.NavGroup = function(config){
4622     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4623     this.navItems = [];
4624    
4625     Roo.bootstrap.NavGroup.register(this);
4626      this.addEvents({
4627         /**
4628              * @event changed
4629              * Fires when the active item changes
4630              * @param {Roo.bootstrap.NavGroup} this
4631              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4632              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4633          */
4634         'changed': true
4635      });
4636     
4637 };
4638
4639 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4640     
4641     align: '',
4642     inverse: false,
4643     form: false,
4644     type: 'nav',
4645     navId : '',
4646     // private
4647     
4648     navItems : false, 
4649     
4650     getAutoCreate : function()
4651     {
4652         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4653         
4654         cfg = {
4655             tag : 'ul',
4656             cls: 'nav' 
4657         };
4658         if (Roo.bootstrap.version == 4) {
4659             if (['tabs','pills'].indexOf(this.type) != -1) {
4660                 cfg.cls += ' nav-' + this.type; 
4661             } else {
4662                 // trying to remove so header bar can right align top?
4663                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
4664                     // do not use on header bar... 
4665                     cfg.cls += ' navbar-nav';
4666                 }
4667             }
4668             
4669         } else {
4670             if (['tabs','pills'].indexOf(this.type) != -1) {
4671                 cfg.cls += ' nav-' + this.type
4672             } else {
4673                 if (this.type !== 'nav') {
4674                     Roo.log('nav type must be nav/tabs/pills')
4675                 }
4676                 cfg.cls += ' navbar-nav'
4677             }
4678         }
4679         
4680         if (this.parent() && this.parent().sidebar) {
4681             cfg = {
4682                 tag: 'ul',
4683                 cls: 'dashboard-menu sidebar-menu'
4684             };
4685             
4686             return cfg;
4687         }
4688         
4689         if (this.form === true) {
4690             cfg = {
4691                 tag: 'form',
4692                 cls: 'navbar-form form-inline'
4693             };
4694             //nav navbar-right ml-md-auto
4695             if (this.align === 'right') {
4696                 cfg.cls += ' navbar-right ml-md-auto';
4697             } else {
4698                 cfg.cls += ' navbar-left';
4699             }
4700         }
4701         
4702         if (this.align === 'right') {
4703             cfg.cls += ' navbar-right ml-md-auto';
4704         } else {
4705             cfg.cls += ' mr-auto';
4706         }
4707         
4708         if (this.inverse) {
4709             cfg.cls += ' navbar-inverse';
4710             
4711         }
4712         
4713         
4714         return cfg;
4715     },
4716     /**
4717     * sets the active Navigation item
4718     * @param {Roo.bootstrap.NavItem} the new current navitem
4719     */
4720     setActiveItem : function(item)
4721     {
4722         var prev = false;
4723         Roo.each(this.navItems, function(v){
4724             if (v == item) {
4725                 return ;
4726             }
4727             if (v.isActive()) {
4728                 v.setActive(false, true);
4729                 prev = v;
4730                 
4731             }
4732             
4733         });
4734
4735         item.setActive(true, true);
4736         this.fireEvent('changed', this, item, prev);
4737         
4738         
4739     },
4740     /**
4741     * gets the active Navigation item
4742     * @return {Roo.bootstrap.NavItem} the current navitem
4743     */
4744     getActive : function()
4745     {
4746         
4747         var prev = false;
4748         Roo.each(this.navItems, function(v){
4749             
4750             if (v.isActive()) {
4751                 prev = v;
4752                 
4753             }
4754             
4755         });
4756         return prev;
4757     },
4758     
4759     indexOfNav : function()
4760     {
4761         
4762         var prev = false;
4763         Roo.each(this.navItems, function(v,i){
4764             
4765             if (v.isActive()) {
4766                 prev = i;
4767                 
4768             }
4769             
4770         });
4771         return prev;
4772     },
4773     /**
4774     * adds a Navigation item
4775     * @param {Roo.bootstrap.NavItem} the navitem to add
4776     */
4777     addItem : function(cfg)
4778     {
4779         if (this.form && Roo.bootstrap.version == 4) {
4780             cfg.tag = 'div';
4781         }
4782         var cn = new Roo.bootstrap.NavItem(cfg);
4783         this.register(cn);
4784         cn.parentId = this.id;
4785         cn.onRender(this.el, null);
4786         return cn;
4787     },
4788     /**
4789     * register a Navigation item
4790     * @param {Roo.bootstrap.NavItem} the navitem to add
4791     */
4792     register : function(item)
4793     {
4794         this.navItems.push( item);
4795         item.navId = this.navId;
4796     
4797     },
4798     
4799     /**
4800     * clear all the Navigation item
4801     */
4802    
4803     clearAll : function()
4804     {
4805         this.navItems = [];
4806         this.el.dom.innerHTML = '';
4807     },
4808     
4809     getNavItem: function(tabId)
4810     {
4811         var ret = false;
4812         Roo.each(this.navItems, function(e) {
4813             if (e.tabId == tabId) {
4814                ret =  e;
4815                return false;
4816             }
4817             return true;
4818             
4819         });
4820         return ret;
4821     },
4822     
4823     setActiveNext : function()
4824     {
4825         var i = this.indexOfNav(this.getActive());
4826         if (i > this.navItems.length) {
4827             return;
4828         }
4829         this.setActiveItem(this.navItems[i+1]);
4830     },
4831     setActivePrev : function()
4832     {
4833         var i = this.indexOfNav(this.getActive());
4834         if (i  < 1) {
4835             return;
4836         }
4837         this.setActiveItem(this.navItems[i-1]);
4838     },
4839     clearWasActive : function(except) {
4840         Roo.each(this.navItems, function(e) {
4841             if (e.tabId != except.tabId && e.was_active) {
4842                e.was_active = false;
4843                return false;
4844             }
4845             return true;
4846             
4847         });
4848     },
4849     getWasActive : function ()
4850     {
4851         var r = false;
4852         Roo.each(this.navItems, function(e) {
4853             if (e.was_active) {
4854                r = e;
4855                return false;
4856             }
4857             return true;
4858             
4859         });
4860         return r;
4861     }
4862     
4863     
4864 });
4865
4866  
4867 Roo.apply(Roo.bootstrap.NavGroup, {
4868     
4869     groups: {},
4870      /**
4871     * register a Navigation Group
4872     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4873     */
4874     register : function(navgrp)
4875     {
4876         this.groups[navgrp.navId] = navgrp;
4877         
4878     },
4879     /**
4880     * fetch a Navigation Group based on the navigation ID
4881     * @param {string} the navgroup to add
4882     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4883     */
4884     get: function(navId) {
4885         if (typeof(this.groups[navId]) == 'undefined') {
4886             return false;
4887             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4888         }
4889         return this.groups[navId] ;
4890     }
4891     
4892     
4893     
4894 });
4895
4896  /*
4897  * - LGPL
4898  *
4899  * row
4900  * 
4901  */
4902
4903 /**
4904  * @class Roo.bootstrap.NavItem
4905  * @extends Roo.bootstrap.Component
4906  * Bootstrap Navbar.NavItem class
4907  * @cfg {String} href  link to
4908  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
4909
4910  * @cfg {String} html content of button
4911  * @cfg {String} badge text inside badge
4912  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4913  * @cfg {String} glyphicon DEPRICATED - use fa
4914  * @cfg {String} icon DEPRICATED - use fa
4915  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4916  * @cfg {Boolean} active Is item active
4917  * @cfg {Boolean} disabled Is item disabled
4918  
4919  * @cfg {Boolean} preventDefault (true | false) default false
4920  * @cfg {String} tabId the tab that this item activates.
4921  * @cfg {String} tagtype (a|span) render as a href or span?
4922  * @cfg {Boolean} animateRef (true|false) link to element default false  
4923   
4924  * @constructor
4925  * Create a new Navbar Item
4926  * @param {Object} config The config object
4927  */
4928 Roo.bootstrap.NavItem = function(config){
4929     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4930     this.addEvents({
4931         // raw events
4932         /**
4933          * @event click
4934          * The raw click event for the entire grid.
4935          * @param {Roo.EventObject} e
4936          */
4937         "click" : true,
4938          /**
4939             * @event changed
4940             * Fires when the active item active state changes
4941             * @param {Roo.bootstrap.NavItem} this
4942             * @param {boolean} state the new state
4943              
4944          */
4945         'changed': true,
4946         /**
4947             * @event scrollto
4948             * Fires when scroll to element
4949             * @param {Roo.bootstrap.NavItem} this
4950             * @param {Object} options
4951             * @param {Roo.EventObject} e
4952              
4953          */
4954         'scrollto': true
4955     });
4956    
4957 };
4958
4959 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4960     
4961     href: false,
4962     html: '',
4963     badge: '',
4964     icon: false,
4965     fa : false,
4966     glyphicon: false,
4967     active: false,
4968     preventDefault : false,
4969     tabId : false,
4970     tagtype : 'a',
4971     tag: 'li',
4972     disabled : false,
4973     animateRef : false,
4974     was_active : false,
4975     button_weight : '',
4976     button_outline : false,
4977     
4978     navLink: false,
4979     
4980     getAutoCreate : function(){
4981          
4982         var cfg = {
4983             tag: this.tag,
4984             cls: 'nav-item'
4985         };
4986         
4987         if (this.active) {
4988             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4989         }
4990         if (this.disabled) {
4991             cfg.cls += ' disabled';
4992         }
4993         
4994         // BS4 only?
4995         if (this.button_weight.length) {
4996             cfg.tag = this.href ? 'a' : 'button';
4997             cfg.html = this.html || '';
4998             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
4999             if (this.href) {
5000                 cfg.href = this.href;
5001             }
5002             if (this.fa) {
5003                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
5004             }
5005             
5006             // menu .. should add dropdown-menu class - so no need for carat..
5007             
5008             if (this.badge !== '') {
5009                  
5010                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5011             }
5012             return cfg;
5013         }
5014         
5015         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
5016             cfg.cn = [
5017                 {
5018                     tag: this.tagtype,
5019                     href : this.href || "#",
5020                     html: this.html || ''
5021                 }
5022             ];
5023             if (this.tagtype == 'a') {
5024                 cfg.cn[0].cls = 'nav-link';
5025             }
5026             if (this.icon) {
5027                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
5028             }
5029             if (this.fa) {
5030                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
5031             }
5032             if(this.glyphicon) {
5033                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
5034             }
5035             
5036             if (this.menu) {
5037                 
5038                 cfg.cn[0].html += " <span class='caret'></span>";
5039              
5040             }
5041             
5042             if (this.badge !== '') {
5043                  
5044                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5045             }
5046         }
5047         
5048         
5049         
5050         return cfg;
5051     },
5052     onRender : function(ct, position)
5053     {
5054        // Roo.log("Call onRender: " + this.xtype);
5055         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
5056             this.tag = 'div';
5057         }
5058         
5059         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
5060         this.navLink = this.el.select('.nav-link',true).first();
5061         return ret;
5062     },
5063       
5064     
5065     initEvents: function() 
5066     {
5067         if (typeof (this.menu) != 'undefined') {
5068             this.menu.parentType = this.xtype;
5069             this.menu.triggerEl = this.el;
5070             this.menu = this.addxtype(Roo.apply({}, this.menu));
5071         }
5072         
5073         this.el.select('a',true).on('click', this.onClick, this);
5074         
5075         if(this.tagtype == 'span'){
5076             this.el.select('span',true).on('click', this.onClick, this);
5077         }
5078        
5079         // at this point parent should be available..
5080         this.parent().register(this);
5081     },
5082     
5083     onClick : function(e)
5084     {
5085         if (e.getTarget('.dropdown-menu-item')) {
5086             // did you click on a menu itemm.... - then don't trigger onclick..
5087             return;
5088         }
5089         
5090         if(
5091                 this.preventDefault || 
5092                 this.href == '#' 
5093         ){
5094             Roo.log("NavItem - prevent Default?");
5095             e.preventDefault();
5096         }
5097         
5098         if (this.disabled) {
5099             return;
5100         }
5101         
5102         var tg = Roo.bootstrap.TabGroup.get(this.navId);
5103         if (tg && tg.transition) {
5104             Roo.log("waiting for the transitionend");
5105             return;
5106         }
5107         
5108         
5109         
5110         //Roo.log("fire event clicked");
5111         if(this.fireEvent('click', this, e) === false){
5112             return;
5113         };
5114         
5115         if(this.tagtype == 'span'){
5116             return;
5117         }
5118         
5119         //Roo.log(this.href);
5120         var ael = this.el.select('a',true).first();
5121         //Roo.log(ael);
5122         
5123         if(ael && this.animateRef && this.href.indexOf('#') > -1){
5124             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
5125             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
5126                 return; // ignore... - it's a 'hash' to another page.
5127             }
5128             Roo.log("NavItem - prevent Default?");
5129             e.preventDefault();
5130             this.scrollToElement(e);
5131         }
5132         
5133         
5134         var p =  this.parent();
5135    
5136         if (['tabs','pills'].indexOf(p.type)!==-1) {
5137             if (typeof(p.setActiveItem) !== 'undefined') {
5138                 p.setActiveItem(this);
5139             }
5140         }
5141         
5142         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
5143         if (p.parentType == 'NavHeaderbar' && !this.menu) {
5144             // remove the collapsed menu expand...
5145             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
5146         }
5147     },
5148     
5149     isActive: function () {
5150         return this.active
5151     },
5152     setActive : function(state, fire, is_was_active)
5153     {
5154         if (this.active && !state && this.navId) {
5155             this.was_active = true;
5156             var nv = Roo.bootstrap.NavGroup.get(this.navId);
5157             if (nv) {
5158                 nv.clearWasActive(this);
5159             }
5160             
5161         }
5162         this.active = state;
5163         
5164         if (!state ) {
5165             this.el.removeClass('active');
5166             this.navLink ? this.navLink.removeClass('active') : false;
5167         } else if (!this.el.hasClass('active')) {
5168             
5169             this.el.addClass('active');
5170             if (Roo.bootstrap.version == 4 && this.navLink ) {
5171                 this.navLink.addClass('active');
5172             }
5173             
5174         }
5175         if (fire) {
5176             this.fireEvent('changed', this, state);
5177         }
5178         
5179         // show a panel if it's registered and related..
5180         
5181         if (!this.navId || !this.tabId || !state || is_was_active) {
5182             return;
5183         }
5184         
5185         var tg = Roo.bootstrap.TabGroup.get(this.navId);
5186         if (!tg) {
5187             return;
5188         }
5189         var pan = tg.getPanelByName(this.tabId);
5190         if (!pan) {
5191             return;
5192         }
5193         // if we can not flip to new panel - go back to old nav highlight..
5194         if (false == tg.showPanel(pan)) {
5195             var nv = Roo.bootstrap.NavGroup.get(this.navId);
5196             if (nv) {
5197                 var onav = nv.getWasActive();
5198                 if (onav) {
5199                     onav.setActive(true, false, true);
5200                 }
5201             }
5202             
5203         }
5204         
5205         
5206         
5207     },
5208      // this should not be here...
5209     setDisabled : function(state)
5210     {
5211         this.disabled = state;
5212         if (!state ) {
5213             this.el.removeClass('disabled');
5214         } else if (!this.el.hasClass('disabled')) {
5215             this.el.addClass('disabled');
5216         }
5217         
5218     },
5219     
5220     /**
5221      * Fetch the element to display the tooltip on.
5222      * @return {Roo.Element} defaults to this.el
5223      */
5224     tooltipEl : function()
5225     {
5226         return this.el.select('' + this.tagtype + '', true).first();
5227     },
5228     
5229     scrollToElement : function(e)
5230     {
5231         var c = document.body;
5232         
5233         /*
5234          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
5235          */
5236         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
5237             c = document.documentElement;
5238         }
5239         
5240         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
5241         
5242         if(!target){
5243             return;
5244         }
5245
5246         var o = target.calcOffsetsTo(c);
5247         
5248         var options = {
5249             target : target,
5250             value : o[1]
5251         };
5252         
5253         this.fireEvent('scrollto', this, options, e);
5254         
5255         Roo.get(c).scrollTo('top', options.value, true);
5256         
5257         return;
5258     }
5259 });
5260  
5261
5262  /*
5263  * - LGPL
5264  *
5265  * sidebar item
5266  *
5267  *  li
5268  *    <span> icon </span>
5269  *    <span> text </span>
5270  *    <span>badge </span>
5271  */
5272
5273 /**
5274  * @class Roo.bootstrap.NavSidebarItem
5275  * @extends Roo.bootstrap.NavItem
5276  * Bootstrap Navbar.NavSidebarItem class
5277  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
5278  * {Boolean} open is the menu open
5279  * {Boolean} buttonView use button as the tigger el rather that a (default false)
5280  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5281  * {String} buttonSize (sm|md|lg)the extra classes for the button
5282  * {Boolean} showArrow show arrow next to the text (default true)
5283  * @constructor
5284  * Create a new Navbar Button
5285  * @param {Object} config The config object
5286  */
5287 Roo.bootstrap.NavSidebarItem = function(config){
5288     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5289     this.addEvents({
5290         // raw events
5291         /**
5292          * @event click
5293          * The raw click event for the entire grid.
5294          * @param {Roo.EventObject} e
5295          */
5296         "click" : true,
5297          /**
5298             * @event changed
5299             * Fires when the active item active state changes
5300             * @param {Roo.bootstrap.NavSidebarItem} this
5301             * @param {boolean} state the new state
5302              
5303          */
5304         'changed': true
5305     });
5306    
5307 };
5308
5309 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
5310     
5311     badgeWeight : 'default',
5312     
5313     open: false,
5314     
5315     buttonView : false,
5316     
5317     buttonWeight : 'default',
5318     
5319     buttonSize : 'md',
5320     
5321     showArrow : true,
5322     
5323     getAutoCreate : function(){
5324         
5325         
5326         var a = {
5327                 tag: 'a',
5328                 href : this.href || '#',
5329                 cls: '',
5330                 html : '',
5331                 cn : []
5332         };
5333         
5334         if(this.buttonView){
5335             a = {
5336                 tag: 'button',
5337                 href : this.href || '#',
5338                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5339                 html : this.html,
5340                 cn : []
5341             };
5342         }
5343         
5344         var cfg = {
5345             tag: 'li',
5346             cls: '',
5347             cn: [ a ]
5348         };
5349         
5350         if (this.active) {
5351             cfg.cls += ' active';
5352         }
5353         
5354         if (this.disabled) {
5355             cfg.cls += ' disabled';
5356         }
5357         if (this.open) {
5358             cfg.cls += ' open x-open';
5359         }
5360         // left icon..
5361         if (this.glyphicon || this.icon) {
5362             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5363             a.cn.push({ tag : 'i', cls : c }) ;
5364         }
5365         
5366         if(!this.buttonView){
5367             var span = {
5368                 tag: 'span',
5369                 html : this.html || ''
5370             };
5371
5372             a.cn.push(span);
5373             
5374         }
5375         
5376         if (this.badge !== '') {
5377             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5378         }
5379         
5380         if (this.menu) {
5381             
5382             if(this.showArrow){
5383                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5384             }
5385             
5386             a.cls += ' dropdown-toggle treeview' ;
5387         }
5388         
5389         return cfg;
5390     },
5391     
5392     initEvents : function()
5393     { 
5394         if (typeof (this.menu) != 'undefined') {
5395             this.menu.parentType = this.xtype;
5396             this.menu.triggerEl = this.el;
5397             this.menu = this.addxtype(Roo.apply({}, this.menu));
5398         }
5399         
5400         this.el.on('click', this.onClick, this);
5401         
5402         if(this.badge !== ''){
5403             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5404         }
5405         
5406     },
5407     
5408     onClick : function(e)
5409     {
5410         if(this.disabled){
5411             e.preventDefault();
5412             return;
5413         }
5414         
5415         if(this.preventDefault){
5416             e.preventDefault();
5417         }
5418         
5419         this.fireEvent('click', this, e);
5420     },
5421     
5422     disable : function()
5423     {
5424         this.setDisabled(true);
5425     },
5426     
5427     enable : function()
5428     {
5429         this.setDisabled(false);
5430     },
5431     
5432     setDisabled : function(state)
5433     {
5434         if(this.disabled == state){
5435             return;
5436         }
5437         
5438         this.disabled = state;
5439         
5440         if (state) {
5441             this.el.addClass('disabled');
5442             return;
5443         }
5444         
5445         this.el.removeClass('disabled');
5446         
5447         return;
5448     },
5449     
5450     setActive : function(state)
5451     {
5452         if(this.active == state){
5453             return;
5454         }
5455         
5456         this.active = state;
5457         
5458         if (state) {
5459             this.el.addClass('active');
5460             return;
5461         }
5462         
5463         this.el.removeClass('active');
5464         
5465         return;
5466     },
5467     
5468     isActive: function () 
5469     {
5470         return this.active;
5471     },
5472     
5473     setBadge : function(str)
5474     {
5475         if(!this.badgeEl){
5476             return;
5477         }
5478         
5479         this.badgeEl.dom.innerHTML = str;
5480     }
5481     
5482    
5483      
5484  
5485 });
5486  
5487
5488  /*
5489  * - LGPL
5490  *
5491  * row
5492  * 
5493  */
5494
5495 /**
5496  * @class Roo.bootstrap.Row
5497  * @extends Roo.bootstrap.Component
5498  * Bootstrap Row class (contains columns...)
5499  * 
5500  * @constructor
5501  * Create a new Row
5502  * @param {Object} config The config object
5503  */
5504
5505 Roo.bootstrap.Row = function(config){
5506     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5507 };
5508
5509 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5510     
5511     getAutoCreate : function(){
5512        return {
5513             cls: 'row clearfix'
5514        };
5515     }
5516     
5517     
5518 });
5519
5520  
5521
5522  /*
5523  * - LGPL
5524  *
5525  * element
5526  * 
5527  */
5528
5529 /**
5530  * @class Roo.bootstrap.Element
5531  * @extends Roo.bootstrap.Component
5532  * Bootstrap Element class
5533  * @cfg {String} html contents of the element
5534  * @cfg {String} tag tag of the element
5535  * @cfg {String} cls class of the element
5536  * @cfg {Boolean} preventDefault (true|false) default false
5537  * @cfg {Boolean} clickable (true|false) default false
5538  * 
5539  * @constructor
5540  * Create a new Element
5541  * @param {Object} config The config object
5542  */
5543
5544 Roo.bootstrap.Element = function(config){
5545     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5546     
5547     this.addEvents({
5548         // raw events
5549         /**
5550          * @event click
5551          * When a element is chick
5552          * @param {Roo.bootstrap.Element} this
5553          * @param {Roo.EventObject} e
5554          */
5555         "click" : true
5556     });
5557 };
5558
5559 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5560     
5561     tag: 'div',
5562     cls: '',
5563     html: '',
5564     preventDefault: false, 
5565     clickable: false,
5566     
5567     getAutoCreate : function(){
5568         
5569         var cfg = {
5570             tag: this.tag,
5571             // cls: this.cls, double assign in parent class Component.js :: onRender
5572             html: this.html
5573         };
5574         
5575         return cfg;
5576     },
5577     
5578     initEvents: function() 
5579     {
5580         Roo.bootstrap.Element.superclass.initEvents.call(this);
5581         
5582         if(this.clickable){
5583             this.el.on('click', this.onClick, this);
5584         }
5585         
5586     },
5587     
5588     onClick : function(e)
5589     {
5590         if(this.preventDefault){
5591             e.preventDefault();
5592         }
5593         
5594         this.fireEvent('click', this, e);
5595     },
5596     
5597     getValue : function()
5598     {
5599         return this.el.dom.innerHTML;
5600     },
5601     
5602     setValue : function(value)
5603     {
5604         this.el.dom.innerHTML = value;
5605     }
5606    
5607 });
5608
5609  
5610
5611  /*
5612  * - LGPL
5613  *
5614  * pagination
5615  * 
5616  */
5617
5618 /**
5619  * @class Roo.bootstrap.Pagination
5620  * @extends Roo.bootstrap.Component
5621  * Bootstrap Pagination class
5622  * @cfg {String} size xs | sm | md | lg
5623  * @cfg {Boolean} inverse false | true
5624  * 
5625  * @constructor
5626  * Create a new Pagination
5627  * @param {Object} config The config object
5628  */
5629
5630 Roo.bootstrap.Pagination = function(config){
5631     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5632 };
5633
5634 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5635     
5636     cls: false,
5637     size: false,
5638     inverse: false,
5639     
5640     getAutoCreate : function(){
5641         var cfg = {
5642             tag: 'ul',
5643                 cls: 'pagination'
5644         };
5645         if (this.inverse) {
5646             cfg.cls += ' inverse';
5647         }
5648         if (this.html) {
5649             cfg.html=this.html;
5650         }
5651         if (this.cls) {
5652             cfg.cls += " " + this.cls;
5653         }
5654         return cfg;
5655     }
5656    
5657 });
5658
5659  
5660
5661  /*
5662  * - LGPL
5663  *
5664  * Pagination item
5665  * 
5666  */
5667
5668
5669 /**
5670  * @class Roo.bootstrap.PaginationItem
5671  * @extends Roo.bootstrap.Component
5672  * Bootstrap PaginationItem class
5673  * @cfg {String} html text
5674  * @cfg {String} href the link
5675  * @cfg {Boolean} preventDefault (true | false) default true
5676  * @cfg {Boolean} active (true | false) default false
5677  * @cfg {Boolean} disabled default false
5678  * 
5679  * 
5680  * @constructor
5681  * Create a new PaginationItem
5682  * @param {Object} config The config object
5683  */
5684
5685
5686 Roo.bootstrap.PaginationItem = function(config){
5687     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5688     this.addEvents({
5689         // raw events
5690         /**
5691          * @event click
5692          * The raw click event for the entire grid.
5693          * @param {Roo.EventObject} e
5694          */
5695         "click" : true
5696     });
5697 };
5698
5699 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5700     
5701     href : false,
5702     html : false,
5703     preventDefault: true,
5704     active : false,
5705     cls : false,
5706     disabled: false,
5707     
5708     getAutoCreate : function(){
5709         var cfg= {
5710             tag: 'li',
5711             cn: [
5712                 {
5713                     tag : 'a',
5714                     href : this.href ? this.href : '#',
5715                     html : this.html ? this.html : ''
5716                 }
5717             ]
5718         };
5719         
5720         if(this.cls){
5721             cfg.cls = this.cls;
5722         }
5723         
5724         if(this.disabled){
5725             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5726         }
5727         
5728         if(this.active){
5729             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5730         }
5731         
5732         return cfg;
5733     },
5734     
5735     initEvents: function() {
5736         
5737         this.el.on('click', this.onClick, this);
5738         
5739     },
5740     onClick : function(e)
5741     {
5742         Roo.log('PaginationItem on click ');
5743         if(this.preventDefault){
5744             e.preventDefault();
5745         }
5746         
5747         if(this.disabled){
5748             return;
5749         }
5750         
5751         this.fireEvent('click', this, e);
5752     }
5753    
5754 });
5755
5756  
5757
5758  /*
5759  * - LGPL
5760  *
5761  * slider
5762  * 
5763  */
5764
5765
5766 /**
5767  * @class Roo.bootstrap.Slider
5768  * @extends Roo.bootstrap.Component
5769  * Bootstrap Slider class
5770  *    
5771  * @constructor
5772  * Create a new Slider
5773  * @param {Object} config The config object
5774  */
5775
5776 Roo.bootstrap.Slider = function(config){
5777     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5778 };
5779
5780 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5781     
5782     getAutoCreate : function(){
5783         
5784         var cfg = {
5785             tag: 'div',
5786             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5787             cn: [
5788                 {
5789                     tag: 'a',
5790                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5791                 }
5792             ]
5793         };
5794         
5795         return cfg;
5796     }
5797    
5798 });
5799
5800  /*
5801  * Based on:
5802  * Ext JS Library 1.1.1
5803  * Copyright(c) 2006-2007, Ext JS, LLC.
5804  *
5805  * Originally Released Under LGPL - original licence link has changed is not relivant.
5806  *
5807  * Fork - LGPL
5808  * <script type="text/javascript">
5809  */
5810  
5811
5812 /**
5813  * @class Roo.grid.ColumnModel
5814  * @extends Roo.util.Observable
5815  * This is the default implementation of a ColumnModel used by the Grid. It defines
5816  * the columns in the grid.
5817  * <br>Usage:<br>
5818  <pre><code>
5819  var colModel = new Roo.grid.ColumnModel([
5820         {header: "Ticker", width: 60, sortable: true, locked: true},
5821         {header: "Company Name", width: 150, sortable: true},
5822         {header: "Market Cap.", width: 100, sortable: true},
5823         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5824         {header: "Employees", width: 100, sortable: true, resizable: false}
5825  ]);
5826  </code></pre>
5827  * <p>
5828  
5829  * The config options listed for this class are options which may appear in each
5830  * individual column definition.
5831  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5832  * @constructor
5833  * @param {Object} config An Array of column config objects. See this class's
5834  * config objects for details.
5835 */
5836 Roo.grid.ColumnModel = function(config){
5837         /**
5838      * The config passed into the constructor
5839      */
5840     this.config = config;
5841     this.lookup = {};
5842
5843     // if no id, create one
5844     // if the column does not have a dataIndex mapping,
5845     // map it to the order it is in the config
5846     for(var i = 0, len = config.length; i < len; i++){
5847         var c = config[i];
5848         if(typeof c.dataIndex == "undefined"){
5849             c.dataIndex = i;
5850         }
5851         if(typeof c.renderer == "string"){
5852             c.renderer = Roo.util.Format[c.renderer];
5853         }
5854         if(typeof c.id == "undefined"){
5855             c.id = Roo.id();
5856         }
5857         if(c.editor && c.editor.xtype){
5858             c.editor  = Roo.factory(c.editor, Roo.grid);
5859         }
5860         if(c.editor && c.editor.isFormField){
5861             c.editor = new Roo.grid.GridEditor(c.editor);
5862         }
5863         this.lookup[c.id] = c;
5864     }
5865
5866     /**
5867      * The width of columns which have no width specified (defaults to 100)
5868      * @type Number
5869      */
5870     this.defaultWidth = 100;
5871
5872     /**
5873      * Default sortable of columns which have no sortable specified (defaults to false)
5874      * @type Boolean
5875      */
5876     this.defaultSortable = false;
5877
5878     this.addEvents({
5879         /**
5880              * @event widthchange
5881              * Fires when the width of a column changes.
5882              * @param {ColumnModel} this
5883              * @param {Number} columnIndex The column index
5884              * @param {Number} newWidth The new width
5885              */
5886             "widthchange": true,
5887         /**
5888              * @event headerchange
5889              * Fires when the text of a header changes.
5890              * @param {ColumnModel} this
5891              * @param {Number} columnIndex The column index
5892              * @param {Number} newText The new header text
5893              */
5894             "headerchange": true,
5895         /**
5896              * @event hiddenchange
5897              * Fires when a column is hidden or "unhidden".
5898              * @param {ColumnModel} this
5899              * @param {Number} columnIndex The column index
5900              * @param {Boolean} hidden true if hidden, false otherwise
5901              */
5902             "hiddenchange": true,
5903             /**
5904          * @event columnmoved
5905          * Fires when a column is moved.
5906          * @param {ColumnModel} this
5907          * @param {Number} oldIndex
5908          * @param {Number} newIndex
5909          */
5910         "columnmoved" : true,
5911         /**
5912          * @event columlockchange
5913          * Fires when a column's locked state is changed
5914          * @param {ColumnModel} this
5915          * @param {Number} colIndex
5916          * @param {Boolean} locked true if locked
5917          */
5918         "columnlockchange" : true
5919     });
5920     Roo.grid.ColumnModel.superclass.constructor.call(this);
5921 };
5922 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5923     /**
5924      * @cfg {String} header The header text to display in the Grid view.
5925      */
5926     /**
5927      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5928      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5929      * specified, the column's index is used as an index into the Record's data Array.
5930      */
5931     /**
5932      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5933      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5934      */
5935     /**
5936      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5937      * Defaults to the value of the {@link #defaultSortable} property.
5938      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5939      */
5940     /**
5941      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5942      */
5943     /**
5944      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5945      */
5946     /**
5947      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5948      */
5949     /**
5950      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5951      */
5952     /**
5953      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5954      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5955      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5956      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5957      */
5958        /**
5959      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5960      */
5961     /**
5962      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5963      */
5964     /**
5965      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5966      */
5967     /**
5968      * @cfg {String} cursor (Optional)
5969      */
5970     /**
5971      * @cfg {String} tooltip (Optional)
5972      */
5973     /**
5974      * @cfg {Number} xs (Optional)
5975      */
5976     /**
5977      * @cfg {Number} sm (Optional)
5978      */
5979     /**
5980      * @cfg {Number} md (Optional)
5981      */
5982     /**
5983      * @cfg {Number} lg (Optional)
5984      */
5985     /**
5986      * Returns the id of the column at the specified index.
5987      * @param {Number} index The column index
5988      * @return {String} the id
5989      */
5990     getColumnId : function(index){
5991         return this.config[index].id;
5992     },
5993
5994     /**
5995      * Returns the column for a specified id.
5996      * @param {String} id The column id
5997      * @return {Object} the column
5998      */
5999     getColumnById : function(id){
6000         return this.lookup[id];
6001     },
6002
6003     
6004     /**
6005      * Returns the column for a specified dataIndex.
6006      * @param {String} dataIndex The column dataIndex
6007      * @return {Object|Boolean} the column or false if not found
6008      */
6009     getColumnByDataIndex: function(dataIndex){
6010         var index = this.findColumnIndex(dataIndex);
6011         return index > -1 ? this.config[index] : false;
6012     },
6013     
6014     /**
6015      * Returns the index for a specified column id.
6016      * @param {String} id The column id
6017      * @return {Number} the index, or -1 if not found
6018      */
6019     getIndexById : function(id){
6020         for(var i = 0, len = this.config.length; i < len; i++){
6021             if(this.config[i].id == id){
6022                 return i;
6023             }
6024         }
6025         return -1;
6026     },
6027     
6028     /**
6029      * Returns the index for a specified column dataIndex.
6030      * @param {String} dataIndex The column dataIndex
6031      * @return {Number} the index, or -1 if not found
6032      */
6033     
6034     findColumnIndex : function(dataIndex){
6035         for(var i = 0, len = this.config.length; i < len; i++){
6036             if(this.config[i].dataIndex == dataIndex){
6037                 return i;
6038             }
6039         }
6040         return -1;
6041     },
6042     
6043     
6044     moveColumn : function(oldIndex, newIndex){
6045         var c = this.config[oldIndex];
6046         this.config.splice(oldIndex, 1);
6047         this.config.splice(newIndex, 0, c);
6048         this.dataMap = null;
6049         this.fireEvent("columnmoved", this, oldIndex, newIndex);
6050     },
6051
6052     isLocked : function(colIndex){
6053         return this.config[colIndex].locked === true;
6054     },
6055
6056     setLocked : function(colIndex, value, suppressEvent){
6057         if(this.isLocked(colIndex) == value){
6058             return;
6059         }
6060         this.config[colIndex].locked = value;
6061         if(!suppressEvent){
6062             this.fireEvent("columnlockchange", this, colIndex, value);
6063         }
6064     },
6065
6066     getTotalLockedWidth : function(){
6067         var totalWidth = 0;
6068         for(var i = 0; i < this.config.length; i++){
6069             if(this.isLocked(i) && !this.isHidden(i)){
6070                 this.totalWidth += this.getColumnWidth(i);
6071             }
6072         }
6073         return totalWidth;
6074     },
6075
6076     getLockedCount : function(){
6077         for(var i = 0, len = this.config.length; i < len; i++){
6078             if(!this.isLocked(i)){
6079                 return i;
6080             }
6081         }
6082         
6083         return this.config.length;
6084     },
6085
6086     /**
6087      * Returns the number of columns.
6088      * @return {Number}
6089      */
6090     getColumnCount : function(visibleOnly){
6091         if(visibleOnly === true){
6092             var c = 0;
6093             for(var i = 0, len = this.config.length; i < len; i++){
6094                 if(!this.isHidden(i)){
6095                     c++;
6096                 }
6097             }
6098             return c;
6099         }
6100         return this.config.length;
6101     },
6102
6103     /**
6104      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
6105      * @param {Function} fn
6106      * @param {Object} scope (optional)
6107      * @return {Array} result
6108      */
6109     getColumnsBy : function(fn, scope){
6110         var r = [];
6111         for(var i = 0, len = this.config.length; i < len; i++){
6112             var c = this.config[i];
6113             if(fn.call(scope||this, c, i) === true){
6114                 r[r.length] = c;
6115             }
6116         }
6117         return r;
6118     },
6119
6120     /**
6121      * Returns true if the specified column is sortable.
6122      * @param {Number} col The column index
6123      * @return {Boolean}
6124      */
6125     isSortable : function(col){
6126         if(typeof this.config[col].sortable == "undefined"){
6127             return this.defaultSortable;
6128         }
6129         return this.config[col].sortable;
6130     },
6131
6132     /**
6133      * Returns the rendering (formatting) function defined for the column.
6134      * @param {Number} col The column index.
6135      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
6136      */
6137     getRenderer : function(col){
6138         if(!this.config[col].renderer){
6139             return Roo.grid.ColumnModel.defaultRenderer;
6140         }
6141         return this.config[col].renderer;
6142     },
6143
6144     /**
6145      * Sets the rendering (formatting) function for a column.
6146      * @param {Number} col The column index
6147      * @param {Function} fn The function to use to process the cell's raw data
6148      * to return HTML markup for the grid view. The render function is called with
6149      * the following parameters:<ul>
6150      * <li>Data value.</li>
6151      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
6152      * <li>css A CSS style string to apply to the table cell.</li>
6153      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
6154      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
6155      * <li>Row index</li>
6156      * <li>Column index</li>
6157      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
6158      */
6159     setRenderer : function(col, fn){
6160         this.config[col].renderer = fn;
6161     },
6162
6163     /**
6164      * Returns the width for the specified column.
6165      * @param {Number} col The column index
6166      * @return {Number}
6167      */
6168     getColumnWidth : function(col){
6169         return this.config[col].width * 1 || this.defaultWidth;
6170     },
6171
6172     /**
6173      * Sets the width for a column.
6174      * @param {Number} col The column index
6175      * @param {Number} width The new width
6176      */
6177     setColumnWidth : function(col, width, suppressEvent){
6178         this.config[col].width = width;
6179         this.totalWidth = null;
6180         if(!suppressEvent){
6181              this.fireEvent("widthchange", this, col, width);
6182         }
6183     },
6184
6185     /**
6186      * Returns the total width of all columns.
6187      * @param {Boolean} includeHidden True to include hidden column widths
6188      * @return {Number}
6189      */
6190     getTotalWidth : function(includeHidden){
6191         if(!this.totalWidth){
6192             this.totalWidth = 0;
6193             for(var i = 0, len = this.config.length; i < len; i++){
6194                 if(includeHidden || !this.isHidden(i)){
6195                     this.totalWidth += this.getColumnWidth(i);
6196                 }
6197             }
6198         }
6199         return this.totalWidth;
6200     },
6201
6202     /**
6203      * Returns the header for the specified column.
6204      * @param {Number} col The column index
6205      * @return {String}
6206      */
6207     getColumnHeader : function(col){
6208         return this.config[col].header;
6209     },
6210
6211     /**
6212      * Sets the header for a column.
6213      * @param {Number} col The column index
6214      * @param {String} header The new header
6215      */
6216     setColumnHeader : function(col, header){
6217         this.config[col].header = header;
6218         this.fireEvent("headerchange", this, col, header);
6219     },
6220
6221     /**
6222      * Returns the tooltip for the specified column.
6223      * @param {Number} col The column index
6224      * @return {String}
6225      */
6226     getColumnTooltip : function(col){
6227             return this.config[col].tooltip;
6228     },
6229     /**
6230      * Sets the tooltip for a column.
6231      * @param {Number} col The column index
6232      * @param {String} tooltip The new tooltip
6233      */
6234     setColumnTooltip : function(col, tooltip){
6235             this.config[col].tooltip = tooltip;
6236     },
6237
6238     /**
6239      * Returns the dataIndex for the specified column.
6240      * @param {Number} col The column index
6241      * @return {Number}
6242      */
6243     getDataIndex : function(col){
6244         return this.config[col].dataIndex;
6245     },
6246
6247     /**
6248      * Sets the dataIndex for a column.
6249      * @param {Number} col The column index
6250      * @param {Number} dataIndex The new dataIndex
6251      */
6252     setDataIndex : function(col, dataIndex){
6253         this.config[col].dataIndex = dataIndex;
6254     },
6255
6256     
6257     
6258     /**
6259      * Returns true if the cell is editable.
6260      * @param {Number} colIndex The column index
6261      * @param {Number} rowIndex The row index - this is nto actually used..?
6262      * @return {Boolean}
6263      */
6264     isCellEditable : function(colIndex, rowIndex){
6265         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6266     },
6267
6268     /**
6269      * Returns the editor defined for the cell/column.
6270      * return false or null to disable editing.
6271      * @param {Number} colIndex The column index
6272      * @param {Number} rowIndex The row index
6273      * @return {Object}
6274      */
6275     getCellEditor : function(colIndex, rowIndex){
6276         return this.config[colIndex].editor;
6277     },
6278
6279     /**
6280      * Sets if a column is editable.
6281      * @param {Number} col The column index
6282      * @param {Boolean} editable True if the column is editable
6283      */
6284     setEditable : function(col, editable){
6285         this.config[col].editable = editable;
6286     },
6287
6288
6289     /**
6290      * Returns true if the column is hidden.
6291      * @param {Number} colIndex The column index
6292      * @return {Boolean}
6293      */
6294     isHidden : function(colIndex){
6295         return this.config[colIndex].hidden;
6296     },
6297
6298
6299     /**
6300      * Returns true if the column width cannot be changed
6301      */
6302     isFixed : function(colIndex){
6303         return this.config[colIndex].fixed;
6304     },
6305
6306     /**
6307      * Returns true if the column can be resized
6308      * @return {Boolean}
6309      */
6310     isResizable : function(colIndex){
6311         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6312     },
6313     /**
6314      * Sets if a column is hidden.
6315      * @param {Number} colIndex The column index
6316      * @param {Boolean} hidden True if the column is hidden
6317      */
6318     setHidden : function(colIndex, hidden){
6319         this.config[colIndex].hidden = hidden;
6320         this.totalWidth = null;
6321         this.fireEvent("hiddenchange", this, colIndex, hidden);
6322     },
6323
6324     /**
6325      * Sets the editor for a column.
6326      * @param {Number} col The column index
6327      * @param {Object} editor The editor object
6328      */
6329     setEditor : function(col, editor){
6330         this.config[col].editor = editor;
6331     }
6332 });
6333
6334 Roo.grid.ColumnModel.defaultRenderer = function(value)
6335 {
6336     if(typeof value == "object") {
6337         return value;
6338     }
6339         if(typeof value == "string" && value.length < 1){
6340             return "&#160;";
6341         }
6342     
6343         return String.format("{0}", value);
6344 };
6345
6346 // Alias for backwards compatibility
6347 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6348 /*
6349  * Based on:
6350  * Ext JS Library 1.1.1
6351  * Copyright(c) 2006-2007, Ext JS, LLC.
6352  *
6353  * Originally Released Under LGPL - original licence link has changed is not relivant.
6354  *
6355  * Fork - LGPL
6356  * <script type="text/javascript">
6357  */
6358  
6359 /**
6360  * @class Roo.LoadMask
6361  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6362  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6363  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6364  * element's UpdateManager load indicator and will be destroyed after the initial load.
6365  * @constructor
6366  * Create a new LoadMask
6367  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6368  * @param {Object} config The config object
6369  */
6370 Roo.LoadMask = function(el, config){
6371     this.el = Roo.get(el);
6372     Roo.apply(this, config);
6373     if(this.store){
6374         this.store.on('beforeload', this.onBeforeLoad, this);
6375         this.store.on('load', this.onLoad, this);
6376         this.store.on('loadexception', this.onLoadException, this);
6377         this.removeMask = false;
6378     }else{
6379         var um = this.el.getUpdateManager();
6380         um.showLoadIndicator = false; // disable the default indicator
6381         um.on('beforeupdate', this.onBeforeLoad, this);
6382         um.on('update', this.onLoad, this);
6383         um.on('failure', this.onLoad, this);
6384         this.removeMask = true;
6385     }
6386 };
6387
6388 Roo.LoadMask.prototype = {
6389     /**
6390      * @cfg {Boolean} removeMask
6391      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6392      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6393      */
6394     /**
6395      * @cfg {String} msg
6396      * The text to display in a centered loading message box (defaults to 'Loading...')
6397      */
6398     msg : 'Loading...',
6399     /**
6400      * @cfg {String} msgCls
6401      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6402      */
6403     msgCls : 'x-mask-loading',
6404
6405     /**
6406      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6407      * @type Boolean
6408      */
6409     disabled: false,
6410
6411     /**
6412      * Disables the mask to prevent it from being displayed
6413      */
6414     disable : function(){
6415        this.disabled = true;
6416     },
6417
6418     /**
6419      * Enables the mask so that it can be displayed
6420      */
6421     enable : function(){
6422         this.disabled = false;
6423     },
6424     
6425     onLoadException : function()
6426     {
6427         Roo.log(arguments);
6428         
6429         if (typeof(arguments[3]) != 'undefined') {
6430             Roo.MessageBox.alert("Error loading",arguments[3]);
6431         } 
6432         /*
6433         try {
6434             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6435                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6436             }   
6437         } catch(e) {
6438             
6439         }
6440         */
6441     
6442         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6443     },
6444     // private
6445     onLoad : function()
6446     {
6447         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6448     },
6449
6450     // private
6451     onBeforeLoad : function(){
6452         if(!this.disabled){
6453             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6454         }
6455     },
6456
6457     // private
6458     destroy : function(){
6459         if(this.store){
6460             this.store.un('beforeload', this.onBeforeLoad, this);
6461             this.store.un('load', this.onLoad, this);
6462             this.store.un('loadexception', this.onLoadException, this);
6463         }else{
6464             var um = this.el.getUpdateManager();
6465             um.un('beforeupdate', this.onBeforeLoad, this);
6466             um.un('update', this.onLoad, this);
6467             um.un('failure', this.onLoad, this);
6468         }
6469     }
6470 };/*
6471  * - LGPL
6472  *
6473  * table
6474  * 
6475  */
6476
6477 /**
6478  * @class Roo.bootstrap.Table
6479  * @extends Roo.bootstrap.Component
6480  * Bootstrap Table class
6481  * @cfg {String} cls table class
6482  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6483  * @cfg {String} bgcolor Specifies the background color for a table
6484  * @cfg {Number} border Specifies whether the table cells should have borders or not
6485  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6486  * @cfg {Number} cellspacing Specifies the space between cells
6487  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6488  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6489  * @cfg {String} sortable Specifies that the table should be sortable
6490  * @cfg {String} summary Specifies a summary of the content of a table
6491  * @cfg {Number} width Specifies the width of a table
6492  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6493  * 
6494  * @cfg {boolean} striped Should the rows be alternative striped
6495  * @cfg {boolean} bordered Add borders to the table
6496  * @cfg {boolean} hover Add hover highlighting
6497  * @cfg {boolean} condensed Format condensed
6498  * @cfg {boolean} responsive Format condensed
6499  * @cfg {Boolean} loadMask (true|false) default false
6500  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6501  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6502  * @cfg {Boolean} rowSelection (true|false) default false
6503  * @cfg {Boolean} cellSelection (true|false) default false
6504  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6505  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6506  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6507  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6508  
6509  * 
6510  * @constructor
6511  * Create a new Table
6512  * @param {Object} config The config object
6513  */
6514
6515 Roo.bootstrap.Table = function(config){
6516     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6517     
6518   
6519     
6520     // BC...
6521     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6522     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6523     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6524     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6525     
6526     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6527     if (this.sm) {
6528         this.sm.grid = this;
6529         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6530         this.sm = this.selModel;
6531         this.sm.xmodule = this.xmodule || false;
6532     }
6533     
6534     if (this.cm && typeof(this.cm.config) == 'undefined') {
6535         this.colModel = new Roo.grid.ColumnModel(this.cm);
6536         this.cm = this.colModel;
6537         this.cm.xmodule = this.xmodule || false;
6538     }
6539     if (this.store) {
6540         this.store= Roo.factory(this.store, Roo.data);
6541         this.ds = this.store;
6542         this.ds.xmodule = this.xmodule || false;
6543          
6544     }
6545     if (this.footer && this.store) {
6546         this.footer.dataSource = this.ds;
6547         this.footer = Roo.factory(this.footer);
6548     }
6549     
6550     /** @private */
6551     this.addEvents({
6552         /**
6553          * @event cellclick
6554          * Fires when a cell is clicked
6555          * @param {Roo.bootstrap.Table} this
6556          * @param {Roo.Element} el
6557          * @param {Number} rowIndex
6558          * @param {Number} columnIndex
6559          * @param {Roo.EventObject} e
6560          */
6561         "cellclick" : true,
6562         /**
6563          * @event celldblclick
6564          * Fires when a cell is double clicked
6565          * @param {Roo.bootstrap.Table} this
6566          * @param {Roo.Element} el
6567          * @param {Number} rowIndex
6568          * @param {Number} columnIndex
6569          * @param {Roo.EventObject} e
6570          */
6571         "celldblclick" : true,
6572         /**
6573          * @event rowclick
6574          * Fires when a row is clicked
6575          * @param {Roo.bootstrap.Table} this
6576          * @param {Roo.Element} el
6577          * @param {Number} rowIndex
6578          * @param {Roo.EventObject} e
6579          */
6580         "rowclick" : true,
6581         /**
6582          * @event rowdblclick
6583          * Fires when a row is double clicked
6584          * @param {Roo.bootstrap.Table} this
6585          * @param {Roo.Element} el
6586          * @param {Number} rowIndex
6587          * @param {Roo.EventObject} e
6588          */
6589         "rowdblclick" : true,
6590         /**
6591          * @event mouseover
6592          * Fires when a mouseover occur
6593          * @param {Roo.bootstrap.Table} this
6594          * @param {Roo.Element} el
6595          * @param {Number} rowIndex
6596          * @param {Number} columnIndex
6597          * @param {Roo.EventObject} e
6598          */
6599         "mouseover" : true,
6600         /**
6601          * @event mouseout
6602          * Fires when a mouseout occur
6603          * @param {Roo.bootstrap.Table} this
6604          * @param {Roo.Element} el
6605          * @param {Number} rowIndex
6606          * @param {Number} columnIndex
6607          * @param {Roo.EventObject} e
6608          */
6609         "mouseout" : true,
6610         /**
6611          * @event rowclass
6612          * Fires when a row is rendered, so you can change add a style to it.
6613          * @param {Roo.bootstrap.Table} this
6614          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6615          */
6616         'rowclass' : true,
6617           /**
6618          * @event rowsrendered
6619          * Fires when all the  rows have been rendered
6620          * @param {Roo.bootstrap.Table} this
6621          */
6622         'rowsrendered' : true,
6623         /**
6624          * @event contextmenu
6625          * The raw contextmenu event for the entire grid.
6626          * @param {Roo.EventObject} e
6627          */
6628         "contextmenu" : true,
6629         /**
6630          * @event rowcontextmenu
6631          * Fires when a row is right clicked
6632          * @param {Roo.bootstrap.Table} this
6633          * @param {Number} rowIndex
6634          * @param {Roo.EventObject} e
6635          */
6636         "rowcontextmenu" : true,
6637         /**
6638          * @event cellcontextmenu
6639          * Fires when a cell is right clicked
6640          * @param {Roo.bootstrap.Table} this
6641          * @param {Number} rowIndex
6642          * @param {Number} cellIndex
6643          * @param {Roo.EventObject} e
6644          */
6645          "cellcontextmenu" : true,
6646          /**
6647          * @event headercontextmenu
6648          * Fires when a header is right clicked
6649          * @param {Roo.bootstrap.Table} this
6650          * @param {Number} columnIndex
6651          * @param {Roo.EventObject} e
6652          */
6653         "headercontextmenu" : true
6654     });
6655 };
6656
6657 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6658     
6659     cls: false,
6660     align: false,
6661     bgcolor: false,
6662     border: false,
6663     cellpadding: false,
6664     cellspacing: false,
6665     frame: false,
6666     rules: false,
6667     sortable: false,
6668     summary: false,
6669     width: false,
6670     striped : false,
6671     scrollBody : false,
6672     bordered: false,
6673     hover:  false,
6674     condensed : false,
6675     responsive : false,
6676     sm : false,
6677     cm : false,
6678     store : false,
6679     loadMask : false,
6680     footerShow : true,
6681     headerShow : true,
6682   
6683     rowSelection : false,
6684     cellSelection : false,
6685     layout : false,
6686     
6687     // Roo.Element - the tbody
6688     mainBody: false,
6689     // Roo.Element - thead element
6690     mainHead: false,
6691     
6692     container: false, // used by gridpanel...
6693     
6694     lazyLoad : false,
6695     
6696     CSS : Roo.util.CSS,
6697     
6698     auto_hide_footer : false,
6699     
6700     getAutoCreate : function()
6701     {
6702         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6703         
6704         cfg = {
6705             tag: 'table',
6706             cls : 'table',
6707             cn : []
6708         };
6709         if (this.scrollBody) {
6710             cfg.cls += ' table-body-fixed';
6711         }    
6712         if (this.striped) {
6713             cfg.cls += ' table-striped';
6714         }
6715         
6716         if (this.hover) {
6717             cfg.cls += ' table-hover';
6718         }
6719         if (this.bordered) {
6720             cfg.cls += ' table-bordered';
6721         }
6722         if (this.condensed) {
6723             cfg.cls += ' table-condensed';
6724         }
6725         if (this.responsive) {
6726             cfg.cls += ' table-responsive';
6727         }
6728         
6729         if (this.cls) {
6730             cfg.cls+=  ' ' +this.cls;
6731         }
6732         
6733         // this lot should be simplifed...
6734         var _t = this;
6735         var cp = [
6736             'align',
6737             'bgcolor',
6738             'border',
6739             'cellpadding',
6740             'cellspacing',
6741             'frame',
6742             'rules',
6743             'sortable',
6744             'summary',
6745             'width'
6746         ].forEach(function(k) {
6747             if (_t[k]) {
6748                 cfg[k] = _t[k];
6749             }
6750         });
6751         
6752         
6753         if (this.layout) {
6754             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6755         }
6756         
6757         if(this.store || this.cm){
6758             if(this.headerShow){
6759                 cfg.cn.push(this.renderHeader());
6760             }
6761             
6762             cfg.cn.push(this.renderBody());
6763             
6764             if(this.footerShow){
6765                 cfg.cn.push(this.renderFooter());
6766             }
6767             // where does this come from?
6768             //cfg.cls+=  ' TableGrid';
6769         }
6770         
6771         return { cn : [ cfg ] };
6772     },
6773     
6774     initEvents : function()
6775     {   
6776         if(!this.store || !this.cm){
6777             return;
6778         }
6779         if (this.selModel) {
6780             this.selModel.initEvents();
6781         }
6782         
6783         
6784         //Roo.log('initEvents with ds!!!!');
6785         
6786         this.mainBody = this.el.select('tbody', true).first();
6787         this.mainHead = this.el.select('thead', true).first();
6788         this.mainFoot = this.el.select('tfoot', true).first();
6789         
6790         
6791         
6792         var _this = this;
6793         
6794         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6795             e.on('click', _this.sort, _this);
6796         });
6797         
6798         this.mainBody.on("click", this.onClick, this);
6799         this.mainBody.on("dblclick", this.onDblClick, this);
6800         
6801         // why is this done????? = it breaks dialogs??
6802         //this.parent().el.setStyle('position', 'relative');
6803         
6804         
6805         if (this.footer) {
6806             this.footer.parentId = this.id;
6807             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6808             
6809             if(this.lazyLoad){
6810                 this.el.select('tfoot tr td').first().addClass('hide');
6811             }
6812         } 
6813         
6814         if(this.loadMask) {
6815             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6816         }
6817         
6818         this.store.on('load', this.onLoad, this);
6819         this.store.on('beforeload', this.onBeforeLoad, this);
6820         this.store.on('update', this.onUpdate, this);
6821         this.store.on('add', this.onAdd, this);
6822         this.store.on("clear", this.clear, this);
6823         
6824         this.el.on("contextmenu", this.onContextMenu, this);
6825         
6826         this.mainBody.on('scroll', this.onBodyScroll, this);
6827         
6828         this.cm.on("headerchange", this.onHeaderChange, this);
6829         
6830         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6831         
6832     },
6833     
6834     onContextMenu : function(e, t)
6835     {
6836         this.processEvent("contextmenu", e);
6837     },
6838     
6839     processEvent : function(name, e)
6840     {
6841         if (name != 'touchstart' ) {
6842             this.fireEvent(name, e);    
6843         }
6844         
6845         var t = e.getTarget();
6846         
6847         var cell = Roo.get(t);
6848         
6849         if(!cell){
6850             return;
6851         }
6852         
6853         if(cell.findParent('tfoot', false, true)){
6854             return;
6855         }
6856         
6857         if(cell.findParent('thead', false, true)){
6858             
6859             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6860                 cell = Roo.get(t).findParent('th', false, true);
6861                 if (!cell) {
6862                     Roo.log("failed to find th in thead?");
6863                     Roo.log(e.getTarget());
6864                     return;
6865                 }
6866             }
6867             
6868             var cellIndex = cell.dom.cellIndex;
6869             
6870             var ename = name == 'touchstart' ? 'click' : name;
6871             this.fireEvent("header" + ename, this, cellIndex, e);
6872             
6873             return;
6874         }
6875         
6876         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6877             cell = Roo.get(t).findParent('td', false, true);
6878             if (!cell) {
6879                 Roo.log("failed to find th in tbody?");
6880                 Roo.log(e.getTarget());
6881                 return;
6882             }
6883         }
6884         
6885         var row = cell.findParent('tr', false, true);
6886         var cellIndex = cell.dom.cellIndex;
6887         var rowIndex = row.dom.rowIndex - 1;
6888         
6889         if(row !== false){
6890             
6891             this.fireEvent("row" + name, this, rowIndex, e);
6892             
6893             if(cell !== false){
6894             
6895                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6896             }
6897         }
6898         
6899     },
6900     
6901     onMouseover : function(e, el)
6902     {
6903         var cell = Roo.get(el);
6904         
6905         if(!cell){
6906             return;
6907         }
6908         
6909         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6910             cell = cell.findParent('td', false, true);
6911         }
6912         
6913         var row = cell.findParent('tr', false, true);
6914         var cellIndex = cell.dom.cellIndex;
6915         var rowIndex = row.dom.rowIndex - 1; // start from 0
6916         
6917         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6918         
6919     },
6920     
6921     onMouseout : function(e, el)
6922     {
6923         var cell = Roo.get(el);
6924         
6925         if(!cell){
6926             return;
6927         }
6928         
6929         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6930             cell = cell.findParent('td', false, true);
6931         }
6932         
6933         var row = cell.findParent('tr', false, true);
6934         var cellIndex = cell.dom.cellIndex;
6935         var rowIndex = row.dom.rowIndex - 1; // start from 0
6936         
6937         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6938         
6939     },
6940     
6941     onClick : function(e, el)
6942     {
6943         var cell = Roo.get(el);
6944         
6945         if(!cell || (!this.cellSelection && !this.rowSelection)){
6946             return;
6947         }
6948         
6949         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6950             cell = cell.findParent('td', false, true);
6951         }
6952         
6953         if(!cell || typeof(cell) == 'undefined'){
6954             return;
6955         }
6956         
6957         var row = cell.findParent('tr', false, true);
6958         
6959         if(!row || typeof(row) == 'undefined'){
6960             return;
6961         }
6962         
6963         var cellIndex = cell.dom.cellIndex;
6964         var rowIndex = this.getRowIndex(row);
6965         
6966         // why??? - should these not be based on SelectionModel?
6967         if(this.cellSelection){
6968             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6969         }
6970         
6971         if(this.rowSelection){
6972             this.fireEvent('rowclick', this, row, rowIndex, e);
6973         }
6974         
6975         
6976     },
6977         
6978     onDblClick : function(e,el)
6979     {
6980         var cell = Roo.get(el);
6981         
6982         if(!cell || (!this.cellSelection && !this.rowSelection)){
6983             return;
6984         }
6985         
6986         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6987             cell = cell.findParent('td', false, true);
6988         }
6989         
6990         if(!cell || typeof(cell) == 'undefined'){
6991             return;
6992         }
6993         
6994         var row = cell.findParent('tr', false, true);
6995         
6996         if(!row || typeof(row) == 'undefined'){
6997             return;
6998         }
6999         
7000         var cellIndex = cell.dom.cellIndex;
7001         var rowIndex = this.getRowIndex(row);
7002         
7003         if(this.cellSelection){
7004             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
7005         }
7006         
7007         if(this.rowSelection){
7008             this.fireEvent('rowdblclick', this, row, rowIndex, e);
7009         }
7010     },
7011     
7012     sort : function(e,el)
7013     {
7014         var col = Roo.get(el);
7015         
7016         if(!col.hasClass('sortable')){
7017             return;
7018         }
7019         
7020         var sort = col.attr('sort');
7021         var dir = 'ASC';
7022         
7023         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
7024             dir = 'DESC';
7025         }
7026         
7027         this.store.sortInfo = {field : sort, direction : dir};
7028         
7029         if (this.footer) {
7030             Roo.log("calling footer first");
7031             this.footer.onClick('first');
7032         } else {
7033         
7034             this.store.load({ params : { start : 0 } });
7035         }
7036     },
7037     
7038     renderHeader : function()
7039     {
7040         var header = {
7041             tag: 'thead',
7042             cn : []
7043         };
7044         
7045         var cm = this.cm;
7046         this.totalWidth = 0;
7047         
7048         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7049             
7050             var config = cm.config[i];
7051             
7052             var c = {
7053                 tag: 'th',
7054                 cls : 'x-hcol-' + i,
7055                 style : '',
7056                 html: cm.getColumnHeader(i)
7057             };
7058             
7059             var hh = '';
7060             
7061             if(typeof(config.sortable) != 'undefined' && config.sortable){
7062                 c.cls = 'sortable';
7063                 c.html = '<i class="glyphicon"></i>' + c.html;
7064             }
7065             
7066             // could use BS4 hidden-..-down 
7067             
7068             if(typeof(config.lgHeader) != 'undefined'){
7069                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
7070             }
7071             
7072             if(typeof(config.mdHeader) != 'undefined'){
7073                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
7074             }
7075             
7076             if(typeof(config.smHeader) != 'undefined'){
7077                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
7078             }
7079             
7080             if(typeof(config.xsHeader) != 'undefined'){
7081                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
7082             }
7083             
7084             if(hh.length){
7085                 c.html = hh;
7086             }
7087             
7088             if(typeof(config.tooltip) != 'undefined'){
7089                 c.tooltip = config.tooltip;
7090             }
7091             
7092             if(typeof(config.colspan) != 'undefined'){
7093                 c.colspan = config.colspan;
7094             }
7095             
7096             if(typeof(config.hidden) != 'undefined' && config.hidden){
7097                 c.style += ' display:none;';
7098             }
7099             
7100             if(typeof(config.dataIndex) != 'undefined'){
7101                 c.sort = config.dataIndex;
7102             }
7103             
7104            
7105             
7106             if(typeof(config.align) != 'undefined' && config.align.length){
7107                 c.style += ' text-align:' + config.align + ';';
7108             }
7109             
7110             if(typeof(config.width) != 'undefined'){
7111                 c.style += ' width:' + config.width + 'px;';
7112                 this.totalWidth += config.width;
7113             } else {
7114                 this.totalWidth += 100; // assume minimum of 100 per column?
7115             }
7116             
7117             if(typeof(config.cls) != 'undefined'){
7118                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
7119             }
7120             
7121             ['xs','sm','md','lg'].map(function(size){
7122                 
7123                 if(typeof(config[size]) == 'undefined'){
7124                     return;
7125                 }
7126                  
7127                 if (!config[size]) { // 0 = hidden
7128                     // BS 4 '0' is treated as hide that column and below.
7129                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
7130                     return;
7131                 }
7132                 
7133                 c.cls += ' col-' + size + '-' + config[size] + (
7134                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
7135                 );
7136                 
7137                 
7138             });
7139             
7140             header.cn.push(c)
7141         }
7142         
7143         return header;
7144     },
7145     
7146     renderBody : function()
7147     {
7148         var body = {
7149             tag: 'tbody',
7150             cn : [
7151                 {
7152                     tag: 'tr',
7153                     cn : [
7154                         {
7155                             tag : 'td',
7156                             colspan :  this.cm.getColumnCount()
7157                         }
7158                     ]
7159                 }
7160             ]
7161         };
7162         
7163         return body;
7164     },
7165     
7166     renderFooter : function()
7167     {
7168         var footer = {
7169             tag: 'tfoot',
7170             cn : [
7171                 {
7172                     tag: 'tr',
7173                     cn : [
7174                         {
7175                             tag : 'td',
7176                             colspan :  this.cm.getColumnCount()
7177                         }
7178                     ]
7179                 }
7180             ]
7181         };
7182         
7183         return footer;
7184     },
7185     
7186     
7187     
7188     onLoad : function()
7189     {
7190 //        Roo.log('ds onload');
7191         this.clear();
7192         
7193         var _this = this;
7194         var cm = this.cm;
7195         var ds = this.store;
7196         
7197         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7198             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
7199             if (_this.store.sortInfo) {
7200                     
7201                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
7202                     e.select('i', true).addClass(['glyphicon-arrow-up']);
7203                 }
7204                 
7205                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
7206                     e.select('i', true).addClass(['glyphicon-arrow-down']);
7207                 }
7208             }
7209         });
7210         
7211         var tbody =  this.mainBody;
7212               
7213         if(ds.getCount() > 0){
7214             ds.data.each(function(d,rowIndex){
7215                 var row =  this.renderRow(cm, ds, rowIndex);
7216                 
7217                 tbody.createChild(row);
7218                 
7219                 var _this = this;
7220                 
7221                 if(row.cellObjects.length){
7222                     Roo.each(row.cellObjects, function(r){
7223                         _this.renderCellObject(r);
7224                     })
7225                 }
7226                 
7227             }, this);
7228         }
7229         
7230         var tfoot = this.el.select('tfoot', true).first();
7231         
7232         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
7233             
7234             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
7235             
7236             var total = this.ds.getTotalCount();
7237             
7238             if(this.footer.pageSize < total){
7239                 this.mainFoot.show();
7240             }
7241         }
7242         
7243         Roo.each(this.el.select('tbody td', true).elements, function(e){
7244             e.on('mouseover', _this.onMouseover, _this);
7245         });
7246         
7247         Roo.each(this.el.select('tbody td', true).elements, function(e){
7248             e.on('mouseout', _this.onMouseout, _this);
7249         });
7250         this.fireEvent('rowsrendered', this);
7251         
7252         this.autoSize();
7253     },
7254     
7255     
7256     onUpdate : function(ds,record)
7257     {
7258         this.refreshRow(record);
7259         this.autoSize();
7260     },
7261     
7262     onRemove : function(ds, record, index, isUpdate){
7263         if(isUpdate !== true){
7264             this.fireEvent("beforerowremoved", this, index, record);
7265         }
7266         var bt = this.mainBody.dom;
7267         
7268         var rows = this.el.select('tbody > tr', true).elements;
7269         
7270         if(typeof(rows[index]) != 'undefined'){
7271             bt.removeChild(rows[index].dom);
7272         }
7273         
7274 //        if(bt.rows[index]){
7275 //            bt.removeChild(bt.rows[index]);
7276 //        }
7277         
7278         if(isUpdate !== true){
7279             //this.stripeRows(index);
7280             //this.syncRowHeights(index, index);
7281             //this.layout();
7282             this.fireEvent("rowremoved", this, index, record);
7283         }
7284     },
7285     
7286     onAdd : function(ds, records, rowIndex)
7287     {
7288         //Roo.log('on Add called');
7289         // - note this does not handle multiple adding very well..
7290         var bt = this.mainBody.dom;
7291         for (var i =0 ; i < records.length;i++) {
7292             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7293             //Roo.log(records[i]);
7294             //Roo.log(this.store.getAt(rowIndex+i));
7295             this.insertRow(this.store, rowIndex + i, false);
7296             return;
7297         }
7298         
7299     },
7300     
7301     
7302     refreshRow : function(record){
7303         var ds = this.store, index;
7304         if(typeof record == 'number'){
7305             index = record;
7306             record = ds.getAt(index);
7307         }else{
7308             index = ds.indexOf(record);
7309         }
7310         this.insertRow(ds, index, true);
7311         this.autoSize();
7312         this.onRemove(ds, record, index+1, true);
7313         this.autoSize();
7314         //this.syncRowHeights(index, index);
7315         //this.layout();
7316         this.fireEvent("rowupdated", this, index, record);
7317     },
7318     
7319     insertRow : function(dm, rowIndex, isUpdate){
7320         
7321         if(!isUpdate){
7322             this.fireEvent("beforerowsinserted", this, rowIndex);
7323         }
7324             //var s = this.getScrollState();
7325         var row = this.renderRow(this.cm, this.store, rowIndex);
7326         // insert before rowIndex..
7327         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7328         
7329         var _this = this;
7330                 
7331         if(row.cellObjects.length){
7332             Roo.each(row.cellObjects, function(r){
7333                 _this.renderCellObject(r);
7334             })
7335         }
7336             
7337         if(!isUpdate){
7338             this.fireEvent("rowsinserted", this, rowIndex);
7339             //this.syncRowHeights(firstRow, lastRow);
7340             //this.stripeRows(firstRow);
7341             //this.layout();
7342         }
7343         
7344     },
7345     
7346     
7347     getRowDom : function(rowIndex)
7348     {
7349         var rows = this.el.select('tbody > tr', true).elements;
7350         
7351         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7352         
7353     },
7354     // returns the object tree for a tr..
7355   
7356     
7357     renderRow : function(cm, ds, rowIndex) 
7358     {
7359         var d = ds.getAt(rowIndex);
7360         
7361         var row = {
7362             tag : 'tr',
7363             cls : 'x-row-' + rowIndex,
7364             cn : []
7365         };
7366             
7367         var cellObjects = [];
7368         
7369         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7370             var config = cm.config[i];
7371             
7372             var renderer = cm.getRenderer(i);
7373             var value = '';
7374             var id = false;
7375             
7376             if(typeof(renderer) !== 'undefined'){
7377                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7378             }
7379             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7380             // and are rendered into the cells after the row is rendered - using the id for the element.
7381             
7382             if(typeof(value) === 'object'){
7383                 id = Roo.id();
7384                 cellObjects.push({
7385                     container : id,
7386                     cfg : value 
7387                 })
7388             }
7389             
7390             var rowcfg = {
7391                 record: d,
7392                 rowIndex : rowIndex,
7393                 colIndex : i,
7394                 rowClass : ''
7395             };
7396
7397             this.fireEvent('rowclass', this, rowcfg);
7398             
7399             var td = {
7400                 tag: 'td',
7401                 cls : rowcfg.rowClass + ' x-col-' + i,
7402                 style: '',
7403                 html: (typeof(value) === 'object') ? '' : value
7404             };
7405             
7406             if (id) {
7407                 td.id = id;
7408             }
7409             
7410             if(typeof(config.colspan) != 'undefined'){
7411                 td.colspan = config.colspan;
7412             }
7413             
7414             if(typeof(config.hidden) != 'undefined' && config.hidden){
7415                 td.style += ' display:none;';
7416             }
7417             
7418             if(typeof(config.align) != 'undefined' && config.align.length){
7419                 td.style += ' text-align:' + config.align + ';';
7420             }
7421             if(typeof(config.valign) != 'undefined' && config.valign.length){
7422                 td.style += ' vertical-align:' + config.valign + ';';
7423             }
7424             
7425             if(typeof(config.width) != 'undefined'){
7426                 td.style += ' width:' +  config.width + 'px;';
7427             }
7428             
7429             if(typeof(config.cursor) != 'undefined'){
7430                 td.style += ' cursor:' +  config.cursor + ';';
7431             }
7432             
7433             if(typeof(config.cls) != 'undefined'){
7434                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7435             }
7436             
7437             ['xs','sm','md','lg'].map(function(size){
7438                 
7439                 if(typeof(config[size]) == 'undefined'){
7440                     return;
7441                 }
7442                 
7443                 
7444                   
7445                 if (!config[size]) { // 0 = hidden
7446                     // BS 4 '0' is treated as hide that column and below.
7447                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
7448                     return;
7449                 }
7450                 
7451                 td.cls += ' col-' + size + '-' + config[size] + (
7452                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
7453                 );
7454                  
7455
7456             });
7457             
7458             row.cn.push(td);
7459            
7460         }
7461         
7462         row.cellObjects = cellObjects;
7463         
7464         return row;
7465           
7466     },
7467     
7468     
7469     
7470     onBeforeLoad : function()
7471     {
7472         
7473     },
7474      /**
7475      * Remove all rows
7476      */
7477     clear : function()
7478     {
7479         this.el.select('tbody', true).first().dom.innerHTML = '';
7480     },
7481     /**
7482      * Show or hide a row.
7483      * @param {Number} rowIndex to show or hide
7484      * @param {Boolean} state hide
7485      */
7486     setRowVisibility : function(rowIndex, state)
7487     {
7488         var bt = this.mainBody.dom;
7489         
7490         var rows = this.el.select('tbody > tr', true).elements;
7491         
7492         if(typeof(rows[rowIndex]) == 'undefined'){
7493             return;
7494         }
7495         rows[rowIndex].dom.style.display = state ? '' : 'none';
7496     },
7497     
7498     
7499     getSelectionModel : function(){
7500         if(!this.selModel){
7501             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7502         }
7503         return this.selModel;
7504     },
7505     /*
7506      * Render the Roo.bootstrap object from renderder
7507      */
7508     renderCellObject : function(r)
7509     {
7510         var _this = this;
7511         
7512         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7513         
7514         var t = r.cfg.render(r.container);
7515         
7516         if(r.cfg.cn){
7517             Roo.each(r.cfg.cn, function(c){
7518                 var child = {
7519                     container: t.getChildContainer(),
7520                     cfg: c
7521                 };
7522                 _this.renderCellObject(child);
7523             })
7524         }
7525     },
7526     
7527     getRowIndex : function(row)
7528     {
7529         var rowIndex = -1;
7530         
7531         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7532             if(el != row){
7533                 return;
7534             }
7535             
7536             rowIndex = index;
7537         });
7538         
7539         return rowIndex;
7540     },
7541      /**
7542      * Returns the grid's underlying element = used by panel.Grid
7543      * @return {Element} The element
7544      */
7545     getGridEl : function(){
7546         return this.el;
7547     },
7548      /**
7549      * Forces a resize - used by panel.Grid
7550      * @return {Element} The element
7551      */
7552     autoSize : function()
7553     {
7554         //var ctr = Roo.get(this.container.dom.parentElement);
7555         var ctr = Roo.get(this.el.dom);
7556         
7557         var thd = this.getGridEl().select('thead',true).first();
7558         var tbd = this.getGridEl().select('tbody', true).first();
7559         var tfd = this.getGridEl().select('tfoot', true).first();
7560         
7561         var cw = ctr.getWidth();
7562         
7563         if (tbd) {
7564             
7565             tbd.setWidth(ctr.getWidth());
7566             // if the body has a max height - and then scrolls - we should perhaps set up the height here
7567             // this needs fixing for various usage - currently only hydra job advers I think..
7568             //tdb.setHeight(
7569             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7570             //); 
7571             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7572             cw -= barsize;
7573         }
7574         cw = Math.max(cw, this.totalWidth);
7575         this.getGridEl().select('tr',true).setWidth(cw);
7576         // resize 'expandable coloumn?
7577         
7578         return; // we doe not have a view in this design..
7579         
7580     },
7581     onBodyScroll: function()
7582     {
7583         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7584         if(this.mainHead){
7585             this.mainHead.setStyle({
7586                 'position' : 'relative',
7587                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7588             });
7589         }
7590         
7591         if(this.lazyLoad){
7592             
7593             var scrollHeight = this.mainBody.dom.scrollHeight;
7594             
7595             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7596             
7597             var height = this.mainBody.getHeight();
7598             
7599             if(scrollHeight - height == scrollTop) {
7600                 
7601                 var total = this.ds.getTotalCount();
7602                 
7603                 if(this.footer.cursor + this.footer.pageSize < total){
7604                     
7605                     this.footer.ds.load({
7606                         params : {
7607                             start : this.footer.cursor + this.footer.pageSize,
7608                             limit : this.footer.pageSize
7609                         },
7610                         add : true
7611                     });
7612                 }
7613             }
7614             
7615         }
7616     },
7617     
7618     onHeaderChange : function()
7619     {
7620         var header = this.renderHeader();
7621         var table = this.el.select('table', true).first();
7622         
7623         this.mainHead.remove();
7624         this.mainHead = table.createChild(header, this.mainBody, false);
7625     },
7626     
7627     onHiddenChange : function(colModel, colIndex, hidden)
7628     {
7629         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7630         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7631         
7632         this.CSS.updateRule(thSelector, "display", "");
7633         this.CSS.updateRule(tdSelector, "display", "");
7634         
7635         if(hidden){
7636             this.CSS.updateRule(thSelector, "display", "none");
7637             this.CSS.updateRule(tdSelector, "display", "none");
7638         }
7639         
7640         this.onHeaderChange();
7641         this.onLoad();
7642     },
7643     
7644     setColumnWidth: function(col_index, width)
7645     {
7646         // width = "md-2 xs-2..."
7647         if(!this.colModel.config[col_index]) {
7648             return;
7649         }
7650         
7651         var w = width.split(" ");
7652         
7653         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7654         
7655         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7656         
7657         
7658         for(var j = 0; j < w.length; j++) {
7659             
7660             if(!w[j]) {
7661                 continue;
7662             }
7663             
7664             var size_cls = w[j].split("-");
7665             
7666             if(!Number.isInteger(size_cls[1] * 1)) {
7667                 continue;
7668             }
7669             
7670             if(!this.colModel.config[col_index][size_cls[0]]) {
7671                 continue;
7672             }
7673             
7674             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7675                 continue;
7676             }
7677             
7678             h_row[0].classList.replace(
7679                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7680                 "col-"+size_cls[0]+"-"+size_cls[1]
7681             );
7682             
7683             for(var i = 0; i < rows.length; i++) {
7684                 
7685                 var size_cls = w[j].split("-");
7686                 
7687                 if(!Number.isInteger(size_cls[1] * 1)) {
7688                     continue;
7689                 }
7690                 
7691                 if(!this.colModel.config[col_index][size_cls[0]]) {
7692                     continue;
7693                 }
7694                 
7695                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7696                     continue;
7697                 }
7698                 
7699                 rows[i].classList.replace(
7700                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7701                     "col-"+size_cls[0]+"-"+size_cls[1]
7702                 );
7703             }
7704             
7705             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7706         }
7707     }
7708 });
7709
7710  
7711
7712  /*
7713  * - LGPL
7714  *
7715  * table cell
7716  * 
7717  */
7718
7719 /**
7720  * @class Roo.bootstrap.TableCell
7721  * @extends Roo.bootstrap.Component
7722  * Bootstrap TableCell class
7723  * @cfg {String} html cell contain text
7724  * @cfg {String} cls cell class
7725  * @cfg {String} tag cell tag (td|th) default td
7726  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7727  * @cfg {String} align Aligns the content in a cell
7728  * @cfg {String} axis Categorizes cells
7729  * @cfg {String} bgcolor Specifies the background color of a cell
7730  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7731  * @cfg {Number} colspan Specifies the number of columns a cell should span
7732  * @cfg {String} headers Specifies one or more header cells a cell is related to
7733  * @cfg {Number} height Sets the height of a cell
7734  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7735  * @cfg {Number} rowspan Sets the number of rows a cell should span
7736  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7737  * @cfg {String} valign Vertical aligns the content in a cell
7738  * @cfg {Number} width Specifies the width of a cell
7739  * 
7740  * @constructor
7741  * Create a new TableCell
7742  * @param {Object} config The config object
7743  */
7744
7745 Roo.bootstrap.TableCell = function(config){
7746     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7747 };
7748
7749 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7750     
7751     html: false,
7752     cls: false,
7753     tag: false,
7754     abbr: false,
7755     align: false,
7756     axis: false,
7757     bgcolor: false,
7758     charoff: false,
7759     colspan: false,
7760     headers: false,
7761     height: false,
7762     nowrap: false,
7763     rowspan: false,
7764     scope: false,
7765     valign: false,
7766     width: false,
7767     
7768     
7769     getAutoCreate : function(){
7770         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7771         
7772         cfg = {
7773             tag: 'td'
7774         };
7775         
7776         if(this.tag){
7777             cfg.tag = this.tag;
7778         }
7779         
7780         if (this.html) {
7781             cfg.html=this.html
7782         }
7783         if (this.cls) {
7784             cfg.cls=this.cls
7785         }
7786         if (this.abbr) {
7787             cfg.abbr=this.abbr
7788         }
7789         if (this.align) {
7790             cfg.align=this.align
7791         }
7792         if (this.axis) {
7793             cfg.axis=this.axis
7794         }
7795         if (this.bgcolor) {
7796             cfg.bgcolor=this.bgcolor
7797         }
7798         if (this.charoff) {
7799             cfg.charoff=this.charoff
7800         }
7801         if (this.colspan) {
7802             cfg.colspan=this.colspan
7803         }
7804         if (this.headers) {
7805             cfg.headers=this.headers
7806         }
7807         if (this.height) {
7808             cfg.height=this.height
7809         }
7810         if (this.nowrap) {
7811             cfg.nowrap=this.nowrap
7812         }
7813         if (this.rowspan) {
7814             cfg.rowspan=this.rowspan
7815         }
7816         if (this.scope) {
7817             cfg.scope=this.scope
7818         }
7819         if (this.valign) {
7820             cfg.valign=this.valign
7821         }
7822         if (this.width) {
7823             cfg.width=this.width
7824         }
7825         
7826         
7827         return cfg;
7828     }
7829    
7830 });
7831
7832  
7833
7834  /*
7835  * - LGPL
7836  *
7837  * table row
7838  * 
7839  */
7840
7841 /**
7842  * @class Roo.bootstrap.TableRow
7843  * @extends Roo.bootstrap.Component
7844  * Bootstrap TableRow class
7845  * @cfg {String} cls row class
7846  * @cfg {String} align Aligns the content in a table row
7847  * @cfg {String} bgcolor Specifies a background color for a table row
7848  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7849  * @cfg {String} valign Vertical aligns the content in a table row
7850  * 
7851  * @constructor
7852  * Create a new TableRow
7853  * @param {Object} config The config object
7854  */
7855
7856 Roo.bootstrap.TableRow = function(config){
7857     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7858 };
7859
7860 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7861     
7862     cls: false,
7863     align: false,
7864     bgcolor: false,
7865     charoff: false,
7866     valign: false,
7867     
7868     getAutoCreate : function(){
7869         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7870         
7871         cfg = {
7872             tag: 'tr'
7873         };
7874             
7875         if(this.cls){
7876             cfg.cls = this.cls;
7877         }
7878         if(this.align){
7879             cfg.align = this.align;
7880         }
7881         if(this.bgcolor){
7882             cfg.bgcolor = this.bgcolor;
7883         }
7884         if(this.charoff){
7885             cfg.charoff = this.charoff;
7886         }
7887         if(this.valign){
7888             cfg.valign = this.valign;
7889         }
7890         
7891         return cfg;
7892     }
7893    
7894 });
7895
7896  
7897
7898  /*
7899  * - LGPL
7900  *
7901  * table body
7902  * 
7903  */
7904
7905 /**
7906  * @class Roo.bootstrap.TableBody
7907  * @extends Roo.bootstrap.Component
7908  * Bootstrap TableBody class
7909  * @cfg {String} cls element class
7910  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7911  * @cfg {String} align Aligns the content inside the element
7912  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7913  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7914  * 
7915  * @constructor
7916  * Create a new TableBody
7917  * @param {Object} config The config object
7918  */
7919
7920 Roo.bootstrap.TableBody = function(config){
7921     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7922 };
7923
7924 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7925     
7926     cls: false,
7927     tag: false,
7928     align: false,
7929     charoff: false,
7930     valign: false,
7931     
7932     getAutoCreate : function(){
7933         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7934         
7935         cfg = {
7936             tag: 'tbody'
7937         };
7938             
7939         if (this.cls) {
7940             cfg.cls=this.cls
7941         }
7942         if(this.tag){
7943             cfg.tag = this.tag;
7944         }
7945         
7946         if(this.align){
7947             cfg.align = this.align;
7948         }
7949         if(this.charoff){
7950             cfg.charoff = this.charoff;
7951         }
7952         if(this.valign){
7953             cfg.valign = this.valign;
7954         }
7955         
7956         return cfg;
7957     }
7958     
7959     
7960 //    initEvents : function()
7961 //    {
7962 //        
7963 //        if(!this.store){
7964 //            return;
7965 //        }
7966 //        
7967 //        this.store = Roo.factory(this.store, Roo.data);
7968 //        this.store.on('load', this.onLoad, this);
7969 //        
7970 //        this.store.load();
7971 //        
7972 //    },
7973 //    
7974 //    onLoad: function () 
7975 //    {   
7976 //        this.fireEvent('load', this);
7977 //    }
7978 //    
7979 //   
7980 });
7981
7982  
7983
7984  /*
7985  * Based on:
7986  * Ext JS Library 1.1.1
7987  * Copyright(c) 2006-2007, Ext JS, LLC.
7988  *
7989  * Originally Released Under LGPL - original licence link has changed is not relivant.
7990  *
7991  * Fork - LGPL
7992  * <script type="text/javascript">
7993  */
7994
7995 // as we use this in bootstrap.
7996 Roo.namespace('Roo.form');
7997  /**
7998  * @class Roo.form.Action
7999  * Internal Class used to handle form actions
8000  * @constructor
8001  * @param {Roo.form.BasicForm} el The form element or its id
8002  * @param {Object} config Configuration options
8003  */
8004
8005  
8006  
8007 // define the action interface
8008 Roo.form.Action = function(form, options){
8009     this.form = form;
8010     this.options = options || {};
8011 };
8012 /**
8013  * Client Validation Failed
8014  * @const 
8015  */
8016 Roo.form.Action.CLIENT_INVALID = 'client';
8017 /**
8018  * Server Validation Failed
8019  * @const 
8020  */
8021 Roo.form.Action.SERVER_INVALID = 'server';
8022  /**
8023  * Connect to Server Failed
8024  * @const 
8025  */
8026 Roo.form.Action.CONNECT_FAILURE = 'connect';
8027 /**
8028  * Reading Data from Server Failed
8029  * @const 
8030  */
8031 Roo.form.Action.LOAD_FAILURE = 'load';
8032
8033 Roo.form.Action.prototype = {
8034     type : 'default',
8035     failureType : undefined,
8036     response : undefined,
8037     result : undefined,
8038
8039     // interface method
8040     run : function(options){
8041
8042     },
8043
8044     // interface method
8045     success : function(response){
8046
8047     },
8048
8049     // interface method
8050     handleResponse : function(response){
8051
8052     },
8053
8054     // default connection failure
8055     failure : function(response){
8056         
8057         this.response = response;
8058         this.failureType = Roo.form.Action.CONNECT_FAILURE;
8059         this.form.afterAction(this, false);
8060     },
8061
8062     processResponse : function(response){
8063         this.response = response;
8064         if(!response.responseText){
8065             return true;
8066         }
8067         this.result = this.handleResponse(response);
8068         return this.result;
8069     },
8070
8071     // utility functions used internally
8072     getUrl : function(appendParams){
8073         var url = this.options.url || this.form.url || this.form.el.dom.action;
8074         if(appendParams){
8075             var p = this.getParams();
8076             if(p){
8077                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
8078             }
8079         }
8080         return url;
8081     },
8082
8083     getMethod : function(){
8084         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
8085     },
8086
8087     getParams : function(){
8088         var bp = this.form.baseParams;
8089         var p = this.options.params;
8090         if(p){
8091             if(typeof p == "object"){
8092                 p = Roo.urlEncode(Roo.applyIf(p, bp));
8093             }else if(typeof p == 'string' && bp){
8094                 p += '&' + Roo.urlEncode(bp);
8095             }
8096         }else if(bp){
8097             p = Roo.urlEncode(bp);
8098         }
8099         return p;
8100     },
8101
8102     createCallback : function(){
8103         return {
8104             success: this.success,
8105             failure: this.failure,
8106             scope: this,
8107             timeout: (this.form.timeout*1000),
8108             upload: this.form.fileUpload ? this.success : undefined
8109         };
8110     }
8111 };
8112
8113 Roo.form.Action.Submit = function(form, options){
8114     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
8115 };
8116
8117 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
8118     type : 'submit',
8119
8120     haveProgress : false,
8121     uploadComplete : false,
8122     
8123     // uploadProgress indicator.
8124     uploadProgress : function()
8125     {
8126         if (!this.form.progressUrl) {
8127             return;
8128         }
8129         
8130         if (!this.haveProgress) {
8131             Roo.MessageBox.progress("Uploading", "Uploading");
8132         }
8133         if (this.uploadComplete) {
8134            Roo.MessageBox.hide();
8135            return;
8136         }
8137         
8138         this.haveProgress = true;
8139    
8140         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
8141         
8142         var c = new Roo.data.Connection();
8143         c.request({
8144             url : this.form.progressUrl,
8145             params: {
8146                 id : uid
8147             },
8148             method: 'GET',
8149             success : function(req){
8150                //console.log(data);
8151                 var rdata = false;
8152                 var edata;
8153                 try  {
8154                    rdata = Roo.decode(req.responseText)
8155                 } catch (e) {
8156                     Roo.log("Invalid data from server..");
8157                     Roo.log(edata);
8158                     return;
8159                 }
8160                 if (!rdata || !rdata.success) {
8161                     Roo.log(rdata);
8162                     Roo.MessageBox.alert(Roo.encode(rdata));
8163                     return;
8164                 }
8165                 var data = rdata.data;
8166                 
8167                 if (this.uploadComplete) {
8168                    Roo.MessageBox.hide();
8169                    return;
8170                 }
8171                    
8172                 if (data){
8173                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
8174                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
8175                     );
8176                 }
8177                 this.uploadProgress.defer(2000,this);
8178             },
8179        
8180             failure: function(data) {
8181                 Roo.log('progress url failed ');
8182                 Roo.log(data);
8183             },
8184             scope : this
8185         });
8186            
8187     },
8188     
8189     
8190     run : function()
8191     {
8192         // run get Values on the form, so it syncs any secondary forms.
8193         this.form.getValues();
8194         
8195         var o = this.options;
8196         var method = this.getMethod();
8197         var isPost = method == 'POST';
8198         if(o.clientValidation === false || this.form.isValid()){
8199             
8200             if (this.form.progressUrl) {
8201                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
8202                     (new Date() * 1) + '' + Math.random());
8203                     
8204             } 
8205             
8206             
8207             Roo.Ajax.request(Roo.apply(this.createCallback(), {
8208                 form:this.form.el.dom,
8209                 url:this.getUrl(!isPost),
8210                 method: method,
8211                 params:isPost ? this.getParams() : null,
8212                 isUpload: this.form.fileUpload,
8213                 formData : this.form.formData
8214             }));
8215             
8216             this.uploadProgress();
8217
8218         }else if (o.clientValidation !== false){ // client validation failed
8219             this.failureType = Roo.form.Action.CLIENT_INVALID;
8220             this.form.afterAction(this, false);
8221         }
8222     },
8223
8224     success : function(response)
8225     {
8226         this.uploadComplete= true;
8227         if (this.haveProgress) {
8228             Roo.MessageBox.hide();
8229         }
8230         
8231         
8232         var result = this.processResponse(response);
8233         if(result === true || result.success){
8234             this.form.afterAction(this, true);
8235             return;
8236         }
8237         if(result.errors){
8238             this.form.markInvalid(result.errors);
8239             this.failureType = Roo.form.Action.SERVER_INVALID;
8240         }
8241         this.form.afterAction(this, false);
8242     },
8243     failure : function(response)
8244     {
8245         this.uploadComplete= true;
8246         if (this.haveProgress) {
8247             Roo.MessageBox.hide();
8248         }
8249         
8250         this.response = response;
8251         this.failureType = Roo.form.Action.CONNECT_FAILURE;
8252         this.form.afterAction(this, false);
8253     },
8254     
8255     handleResponse : function(response){
8256         if(this.form.errorReader){
8257             var rs = this.form.errorReader.read(response);
8258             var errors = [];
8259             if(rs.records){
8260                 for(var i = 0, len = rs.records.length; i < len; i++) {
8261                     var r = rs.records[i];
8262                     errors[i] = r.data;
8263                 }
8264             }
8265             if(errors.length < 1){
8266                 errors = null;
8267             }
8268             return {
8269                 success : rs.success,
8270                 errors : errors
8271             };
8272         }
8273         var ret = false;
8274         try {
8275             ret = Roo.decode(response.responseText);
8276         } catch (e) {
8277             ret = {
8278                 success: false,
8279                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8280                 errors : []
8281             };
8282         }
8283         return ret;
8284         
8285     }
8286 });
8287
8288
8289 Roo.form.Action.Load = function(form, options){
8290     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8291     this.reader = this.form.reader;
8292 };
8293
8294 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8295     type : 'load',
8296
8297     run : function(){
8298         
8299         Roo.Ajax.request(Roo.apply(
8300                 this.createCallback(), {
8301                     method:this.getMethod(),
8302                     url:this.getUrl(false),
8303                     params:this.getParams()
8304         }));
8305     },
8306
8307     success : function(response){
8308         
8309         var result = this.processResponse(response);
8310         if(result === true || !result.success || !result.data){
8311             this.failureType = Roo.form.Action.LOAD_FAILURE;
8312             this.form.afterAction(this, false);
8313             return;
8314         }
8315         this.form.clearInvalid();
8316         this.form.setValues(result.data);
8317         this.form.afterAction(this, true);
8318     },
8319
8320     handleResponse : function(response){
8321         if(this.form.reader){
8322             var rs = this.form.reader.read(response);
8323             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8324             return {
8325                 success : rs.success,
8326                 data : data
8327             };
8328         }
8329         return Roo.decode(response.responseText);
8330     }
8331 });
8332
8333 Roo.form.Action.ACTION_TYPES = {
8334     'load' : Roo.form.Action.Load,
8335     'submit' : Roo.form.Action.Submit
8336 };/*
8337  * - LGPL
8338  *
8339  * form
8340  *
8341  */
8342
8343 /**
8344  * @class Roo.bootstrap.Form
8345  * @extends Roo.bootstrap.Component
8346  * Bootstrap Form class
8347  * @cfg {String} method  GET | POST (default POST)
8348  * @cfg {String} labelAlign top | left (default top)
8349  * @cfg {String} align left  | right - for navbars
8350  * @cfg {Boolean} loadMask load mask when submit (default true)
8351
8352  *
8353  * @constructor
8354  * Create a new Form
8355  * @param {Object} config The config object
8356  */
8357
8358
8359 Roo.bootstrap.Form = function(config){
8360     
8361     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8362     
8363     Roo.bootstrap.Form.popover.apply();
8364     
8365     this.addEvents({
8366         /**
8367          * @event clientvalidation
8368          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8369          * @param {Form} this
8370          * @param {Boolean} valid true if the form has passed client-side validation
8371          */
8372         clientvalidation: true,
8373         /**
8374          * @event beforeaction
8375          * Fires before any action is performed. Return false to cancel the action.
8376          * @param {Form} this
8377          * @param {Action} action The action to be performed
8378          */
8379         beforeaction: true,
8380         /**
8381          * @event actionfailed
8382          * Fires when an action fails.
8383          * @param {Form} this
8384          * @param {Action} action The action that failed
8385          */
8386         actionfailed : true,
8387         /**
8388          * @event actioncomplete
8389          * Fires when an action is completed.
8390          * @param {Form} this
8391          * @param {Action} action The action that completed
8392          */
8393         actioncomplete : true
8394     });
8395 };
8396
8397 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8398
8399      /**
8400      * @cfg {String} method
8401      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8402      */
8403     method : 'POST',
8404     /**
8405      * @cfg {String} url
8406      * The URL to use for form actions if one isn't supplied in the action options.
8407      */
8408     /**
8409      * @cfg {Boolean} fileUpload
8410      * Set to true if this form is a file upload.
8411      */
8412
8413     /**
8414      * @cfg {Object} baseParams
8415      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8416      */
8417
8418     /**
8419      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8420      */
8421     timeout: 30,
8422     /**
8423      * @cfg {Sting} align (left|right) for navbar forms
8424      */
8425     align : 'left',
8426
8427     // private
8428     activeAction : null,
8429
8430     /**
8431      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8432      * element by passing it or its id or mask the form itself by passing in true.
8433      * @type Mixed
8434      */
8435     waitMsgTarget : false,
8436
8437     loadMask : true,
8438     
8439     /**
8440      * @cfg {Boolean} errorMask (true|false) default false
8441      */
8442     errorMask : false,
8443     
8444     /**
8445      * @cfg {Number} maskOffset Default 100
8446      */
8447     maskOffset : 100,
8448     
8449     /**
8450      * @cfg {Boolean} maskBody
8451      */
8452     maskBody : false,
8453
8454     getAutoCreate : function(){
8455
8456         var cfg = {
8457             tag: 'form',
8458             method : this.method || 'POST',
8459             id : this.id || Roo.id(),
8460             cls : ''
8461         };
8462         if (this.parent().xtype.match(/^Nav/)) {
8463             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8464
8465         }
8466
8467         if (this.labelAlign == 'left' ) {
8468             cfg.cls += ' form-horizontal';
8469         }
8470
8471
8472         return cfg;
8473     },
8474     initEvents : function()
8475     {
8476         this.el.on('submit', this.onSubmit, this);
8477         // this was added as random key presses on the form where triggering form submit.
8478         this.el.on('keypress', function(e) {
8479             if (e.getCharCode() != 13) {
8480                 return true;
8481             }
8482             // we might need to allow it for textareas.. and some other items.
8483             // check e.getTarget().
8484
8485             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8486                 return true;
8487             }
8488
8489             Roo.log("keypress blocked");
8490
8491             e.preventDefault();
8492             return false;
8493         });
8494         
8495     },
8496     // private
8497     onSubmit : function(e){
8498         e.stopEvent();
8499     },
8500
8501      /**
8502      * Returns true if client-side validation on the form is successful.
8503      * @return Boolean
8504      */
8505     isValid : function(){
8506         var items = this.getItems();
8507         var valid = true;
8508         var target = false;
8509         
8510         items.each(function(f){
8511             
8512             if(f.validate()){
8513                 return;
8514             }
8515             
8516             Roo.log('invalid field: ' + f.name);
8517             
8518             valid = false;
8519
8520             if(!target && f.el.isVisible(true)){
8521                 target = f;
8522             }
8523            
8524         });
8525         
8526         if(this.errorMask && !valid){
8527             Roo.bootstrap.Form.popover.mask(this, target);
8528         }
8529         
8530         return valid;
8531     },
8532     
8533     /**
8534      * Returns true if any fields in this form have changed since their original load.
8535      * @return Boolean
8536      */
8537     isDirty : function(){
8538         var dirty = false;
8539         var items = this.getItems();
8540         items.each(function(f){
8541            if(f.isDirty()){
8542                dirty = true;
8543                return false;
8544            }
8545            return true;
8546         });
8547         return dirty;
8548     },
8549      /**
8550      * Performs a predefined action (submit or load) or custom actions you define on this form.
8551      * @param {String} actionName The name of the action type
8552      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8553      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8554      * accept other config options):
8555      * <pre>
8556 Property          Type             Description
8557 ----------------  ---------------  ----------------------------------------------------------------------------------
8558 url               String           The url for the action (defaults to the form's url)
8559 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8560 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8561 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8562                                    validate the form on the client (defaults to false)
8563      * </pre>
8564      * @return {BasicForm} this
8565      */
8566     doAction : function(action, options){
8567         if(typeof action == 'string'){
8568             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8569         }
8570         if(this.fireEvent('beforeaction', this, action) !== false){
8571             this.beforeAction(action);
8572             action.run.defer(100, action);
8573         }
8574         return this;
8575     },
8576
8577     // private
8578     beforeAction : function(action){
8579         var o = action.options;
8580         
8581         if(this.loadMask){
8582             
8583             if(this.maskBody){
8584                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8585             } else {
8586                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8587             }
8588         }
8589         // not really supported yet.. ??
8590
8591         //if(this.waitMsgTarget === true){
8592         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8593         //}else if(this.waitMsgTarget){
8594         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8595         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8596         //}else {
8597         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8598        // }
8599
8600     },
8601
8602     // private
8603     afterAction : function(action, success){
8604         this.activeAction = null;
8605         var o = action.options;
8606
8607         if(this.loadMask){
8608             
8609             if(this.maskBody){
8610                 Roo.get(document.body).unmask();
8611             } else {
8612                 this.el.unmask();
8613             }
8614         }
8615         
8616         //if(this.waitMsgTarget === true){
8617 //            this.el.unmask();
8618         //}else if(this.waitMsgTarget){
8619         //    this.waitMsgTarget.unmask();
8620         //}else{
8621         //    Roo.MessageBox.updateProgress(1);
8622         //    Roo.MessageBox.hide();
8623        // }
8624         //
8625         if(success){
8626             if(o.reset){
8627                 this.reset();
8628             }
8629             Roo.callback(o.success, o.scope, [this, action]);
8630             this.fireEvent('actioncomplete', this, action);
8631
8632         }else{
8633
8634             // failure condition..
8635             // we have a scenario where updates need confirming.
8636             // eg. if a locking scenario exists..
8637             // we look for { errors : { needs_confirm : true }} in the response.
8638             if (
8639                 (typeof(action.result) != 'undefined')  &&
8640                 (typeof(action.result.errors) != 'undefined')  &&
8641                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8642            ){
8643                 var _t = this;
8644                 Roo.log("not supported yet");
8645                  /*
8646
8647                 Roo.MessageBox.confirm(
8648                     "Change requires confirmation",
8649                     action.result.errorMsg,
8650                     function(r) {
8651                         if (r != 'yes') {
8652                             return;
8653                         }
8654                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8655                     }
8656
8657                 );
8658                 */
8659
8660
8661                 return;
8662             }
8663
8664             Roo.callback(o.failure, o.scope, [this, action]);
8665             // show an error message if no failed handler is set..
8666             if (!this.hasListener('actionfailed')) {
8667                 Roo.log("need to add dialog support");
8668                 /*
8669                 Roo.MessageBox.alert("Error",
8670                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8671                         action.result.errorMsg :
8672                         "Saving Failed, please check your entries or try again"
8673                 );
8674                 */
8675             }
8676
8677             this.fireEvent('actionfailed', this, action);
8678         }
8679
8680     },
8681     /**
8682      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8683      * @param {String} id The value to search for
8684      * @return Field
8685      */
8686     findField : function(id){
8687         var items = this.getItems();
8688         var field = items.get(id);
8689         if(!field){
8690              items.each(function(f){
8691                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8692                     field = f;
8693                     return false;
8694                 }
8695                 return true;
8696             });
8697         }
8698         return field || null;
8699     },
8700      /**
8701      * Mark fields in this form invalid in bulk.
8702      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8703      * @return {BasicForm} this
8704      */
8705     markInvalid : function(errors){
8706         if(errors instanceof Array){
8707             for(var i = 0, len = errors.length; i < len; i++){
8708                 var fieldError = errors[i];
8709                 var f = this.findField(fieldError.id);
8710                 if(f){
8711                     f.markInvalid(fieldError.msg);
8712                 }
8713             }
8714         }else{
8715             var field, id;
8716             for(id in errors){
8717                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8718                     field.markInvalid(errors[id]);
8719                 }
8720             }
8721         }
8722         //Roo.each(this.childForms || [], function (f) {
8723         //    f.markInvalid(errors);
8724         //});
8725
8726         return this;
8727     },
8728
8729     /**
8730      * Set values for fields in this form in bulk.
8731      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8732      * @return {BasicForm} this
8733      */
8734     setValues : function(values){
8735         if(values instanceof Array){ // array of objects
8736             for(var i = 0, len = values.length; i < len; i++){
8737                 var v = values[i];
8738                 var f = this.findField(v.id);
8739                 if(f){
8740                     f.setValue(v.value);
8741                     if(this.trackResetOnLoad){
8742                         f.originalValue = f.getValue();
8743                     }
8744                 }
8745             }
8746         }else{ // object hash
8747             var field, id;
8748             for(id in values){
8749                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8750
8751                     if (field.setFromData &&
8752                         field.valueField &&
8753                         field.displayField &&
8754                         // combos' with local stores can
8755                         // be queried via setValue()
8756                         // to set their value..
8757                         (field.store && !field.store.isLocal)
8758                         ) {
8759                         // it's a combo
8760                         var sd = { };
8761                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8762                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8763                         field.setFromData(sd);
8764
8765                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8766                         
8767                         field.setFromData(values);
8768                         
8769                     } else {
8770                         field.setValue(values[id]);
8771                     }
8772
8773
8774                     if(this.trackResetOnLoad){
8775                         field.originalValue = field.getValue();
8776                     }
8777                 }
8778             }
8779         }
8780
8781         //Roo.each(this.childForms || [], function (f) {
8782         //    f.setValues(values);
8783         //});
8784
8785         return this;
8786     },
8787
8788     /**
8789      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8790      * they are returned as an array.
8791      * @param {Boolean} asString
8792      * @return {Object}
8793      */
8794     getValues : function(asString){
8795         //if (this.childForms) {
8796             // copy values from the child forms
8797         //    Roo.each(this.childForms, function (f) {
8798         //        this.setValues(f.getValues());
8799         //    }, this);
8800         //}
8801
8802
8803
8804         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8805         if(asString === true){
8806             return fs;
8807         }
8808         return Roo.urlDecode(fs);
8809     },
8810
8811     /**
8812      * Returns the fields in this form as an object with key/value pairs.
8813      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8814      * @return {Object}
8815      */
8816     getFieldValues : function(with_hidden)
8817     {
8818         var items = this.getItems();
8819         var ret = {};
8820         items.each(function(f){
8821             
8822             if (!f.getName()) {
8823                 return;
8824             }
8825             
8826             var v = f.getValue();
8827             
8828             if (f.inputType =='radio') {
8829                 if (typeof(ret[f.getName()]) == 'undefined') {
8830                     ret[f.getName()] = ''; // empty..
8831                 }
8832
8833                 if (!f.el.dom.checked) {
8834                     return;
8835
8836                 }
8837                 v = f.el.dom.value;
8838
8839             }
8840             
8841             if(f.xtype == 'MoneyField'){
8842                 ret[f.currencyName] = f.getCurrency();
8843             }
8844
8845             // not sure if this supported any more..
8846             if ((typeof(v) == 'object') && f.getRawValue) {
8847                 v = f.getRawValue() ; // dates..
8848             }
8849             // combo boxes where name != hiddenName...
8850             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8851                 ret[f.name] = f.getRawValue();
8852             }
8853             ret[f.getName()] = v;
8854         });
8855
8856         return ret;
8857     },
8858
8859     /**
8860      * Clears all invalid messages in this form.
8861      * @return {BasicForm} this
8862      */
8863     clearInvalid : function(){
8864         var items = this.getItems();
8865
8866         items.each(function(f){
8867            f.clearInvalid();
8868         });
8869
8870         return this;
8871     },
8872
8873     /**
8874      * Resets this form.
8875      * @return {BasicForm} this
8876      */
8877     reset : function(){
8878         var items = this.getItems();
8879         items.each(function(f){
8880             f.reset();
8881         });
8882
8883         Roo.each(this.childForms || [], function (f) {
8884             f.reset();
8885         });
8886
8887
8888         return this;
8889     },
8890     
8891     getItems : function()
8892     {
8893         var r=new Roo.util.MixedCollection(false, function(o){
8894             return o.id || (o.id = Roo.id());
8895         });
8896         var iter = function(el) {
8897             if (el.inputEl) {
8898                 r.add(el);
8899             }
8900             if (!el.items) {
8901                 return;
8902             }
8903             Roo.each(el.items,function(e) {
8904                 iter(e);
8905             });
8906         };
8907
8908         iter(this);
8909         return r;
8910     },
8911     
8912     hideFields : function(items)
8913     {
8914         Roo.each(items, function(i){
8915             
8916             var f = this.findField(i);
8917             
8918             if(!f){
8919                 return;
8920             }
8921             
8922             f.hide();
8923             
8924         }, this);
8925     },
8926     
8927     showFields : function(items)
8928     {
8929         Roo.each(items, function(i){
8930             
8931             var f = this.findField(i);
8932             
8933             if(!f){
8934                 return;
8935             }
8936             
8937             f.show();
8938             
8939         }, this);
8940     }
8941
8942 });
8943
8944 Roo.apply(Roo.bootstrap.Form, {
8945     
8946     popover : {
8947         
8948         padding : 5,
8949         
8950         isApplied : false,
8951         
8952         isMasked : false,
8953         
8954         form : false,
8955         
8956         target : false,
8957         
8958         toolTip : false,
8959         
8960         intervalID : false,
8961         
8962         maskEl : false,
8963         
8964         apply : function()
8965         {
8966             if(this.isApplied){
8967                 return;
8968             }
8969             
8970             this.maskEl = {
8971                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8972                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8973                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8974                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8975             };
8976             
8977             this.maskEl.top.enableDisplayMode("block");
8978             this.maskEl.left.enableDisplayMode("block");
8979             this.maskEl.bottom.enableDisplayMode("block");
8980             this.maskEl.right.enableDisplayMode("block");
8981             
8982             this.toolTip = new Roo.bootstrap.Tooltip({
8983                 cls : 'roo-form-error-popover',
8984                 alignment : {
8985                     'left' : ['r-l', [-2,0], 'right'],
8986                     'right' : ['l-r', [2,0], 'left'],
8987                     'bottom' : ['tl-bl', [0,2], 'top'],
8988                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8989                 }
8990             });
8991             
8992             this.toolTip.render(Roo.get(document.body));
8993
8994             this.toolTip.el.enableDisplayMode("block");
8995             
8996             Roo.get(document.body).on('click', function(){
8997                 this.unmask();
8998             }, this);
8999             
9000             Roo.get(document.body).on('touchstart', function(){
9001                 this.unmask();
9002             }, this);
9003             
9004             this.isApplied = true
9005         },
9006         
9007         mask : function(form, target)
9008         {
9009             this.form = form;
9010             
9011             this.target = target;
9012             
9013             if(!this.form.errorMask || !target.el){
9014                 return;
9015             }
9016             
9017             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
9018             
9019             Roo.log(scrollable);
9020             
9021             var ot = this.target.el.calcOffsetsTo(scrollable);
9022             
9023             var scrollTo = ot[1] - this.form.maskOffset;
9024             
9025             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
9026             
9027             scrollable.scrollTo('top', scrollTo);
9028             
9029             var box = this.target.el.getBox();
9030             Roo.log(box);
9031             var zIndex = Roo.bootstrap.Modal.zIndex++;
9032
9033             
9034             this.maskEl.top.setStyle('position', 'absolute');
9035             this.maskEl.top.setStyle('z-index', zIndex);
9036             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
9037             this.maskEl.top.setLeft(0);
9038             this.maskEl.top.setTop(0);
9039             this.maskEl.top.show();
9040             
9041             this.maskEl.left.setStyle('position', 'absolute');
9042             this.maskEl.left.setStyle('z-index', zIndex);
9043             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
9044             this.maskEl.left.setLeft(0);
9045             this.maskEl.left.setTop(box.y - this.padding);
9046             this.maskEl.left.show();
9047
9048             this.maskEl.bottom.setStyle('position', 'absolute');
9049             this.maskEl.bottom.setStyle('z-index', zIndex);
9050             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
9051             this.maskEl.bottom.setLeft(0);
9052             this.maskEl.bottom.setTop(box.bottom + this.padding);
9053             this.maskEl.bottom.show();
9054
9055             this.maskEl.right.setStyle('position', 'absolute');
9056             this.maskEl.right.setStyle('z-index', zIndex);
9057             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
9058             this.maskEl.right.setLeft(box.right + this.padding);
9059             this.maskEl.right.setTop(box.y - this.padding);
9060             this.maskEl.right.show();
9061
9062             this.toolTip.bindEl = this.target.el;
9063
9064             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
9065
9066             var tip = this.target.blankText;
9067
9068             if(this.target.getValue() !== '' ) {
9069                 
9070                 if (this.target.invalidText.length) {
9071                     tip = this.target.invalidText;
9072                 } else if (this.target.regexText.length){
9073                     tip = this.target.regexText;
9074                 }
9075             }
9076
9077             this.toolTip.show(tip);
9078
9079             this.intervalID = window.setInterval(function() {
9080                 Roo.bootstrap.Form.popover.unmask();
9081             }, 10000);
9082
9083             window.onwheel = function(){ return false;};
9084             
9085             (function(){ this.isMasked = true; }).defer(500, this);
9086             
9087         },
9088         
9089         unmask : function()
9090         {
9091             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
9092                 return;
9093             }
9094             
9095             this.maskEl.top.setStyle('position', 'absolute');
9096             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
9097             this.maskEl.top.hide();
9098
9099             this.maskEl.left.setStyle('position', 'absolute');
9100             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
9101             this.maskEl.left.hide();
9102
9103             this.maskEl.bottom.setStyle('position', 'absolute');
9104             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
9105             this.maskEl.bottom.hide();
9106
9107             this.maskEl.right.setStyle('position', 'absolute');
9108             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
9109             this.maskEl.right.hide();
9110             
9111             this.toolTip.hide();
9112             
9113             this.toolTip.el.hide();
9114             
9115             window.onwheel = function(){ return true;};
9116             
9117             if(this.intervalID){
9118                 window.clearInterval(this.intervalID);
9119                 this.intervalID = false;
9120             }
9121             
9122             this.isMasked = false;
9123             
9124         }
9125         
9126     }
9127     
9128 });
9129
9130 /*
9131  * Based on:
9132  * Ext JS Library 1.1.1
9133  * Copyright(c) 2006-2007, Ext JS, LLC.
9134  *
9135  * Originally Released Under LGPL - original licence link has changed is not relivant.
9136  *
9137  * Fork - LGPL
9138  * <script type="text/javascript">
9139  */
9140 /**
9141  * @class Roo.form.VTypes
9142  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
9143  * @singleton
9144  */
9145 Roo.form.VTypes = function(){
9146     // closure these in so they are only created once.
9147     var alpha = /^[a-zA-Z_]+$/;
9148     var alphanum = /^[a-zA-Z0-9_]+$/;
9149     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
9150     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
9151
9152     // All these messages and functions are configurable
9153     return {
9154         /**
9155          * The function used to validate email addresses
9156          * @param {String} value The email address
9157          */
9158         'email' : function(v){
9159             return email.test(v);
9160         },
9161         /**
9162          * The error text to display when the email validation function returns false
9163          * @type String
9164          */
9165         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
9166         /**
9167          * The keystroke filter mask to be applied on email input
9168          * @type RegExp
9169          */
9170         'emailMask' : /[a-z0-9_\.\-@]/i,
9171
9172         /**
9173          * The function used to validate URLs
9174          * @param {String} value The URL
9175          */
9176         'url' : function(v){
9177             return url.test(v);
9178         },
9179         /**
9180          * The error text to display when the url validation function returns false
9181          * @type String
9182          */
9183         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
9184         
9185         /**
9186          * The function used to validate alpha values
9187          * @param {String} value The value
9188          */
9189         'alpha' : function(v){
9190             return alpha.test(v);
9191         },
9192         /**
9193          * The error text to display when the alpha validation function returns false
9194          * @type String
9195          */
9196         'alphaText' : 'This field should only contain letters and _',
9197         /**
9198          * The keystroke filter mask to be applied on alpha input
9199          * @type RegExp
9200          */
9201         'alphaMask' : /[a-z_]/i,
9202
9203         /**
9204          * The function used to validate alphanumeric values
9205          * @param {String} value The value
9206          */
9207         'alphanum' : function(v){
9208             return alphanum.test(v);
9209         },
9210         /**
9211          * The error text to display when the alphanumeric validation function returns false
9212          * @type String
9213          */
9214         'alphanumText' : 'This field should only contain letters, numbers and _',
9215         /**
9216          * The keystroke filter mask to be applied on alphanumeric input
9217          * @type RegExp
9218          */
9219         'alphanumMask' : /[a-z0-9_]/i
9220     };
9221 }();/*
9222  * - LGPL
9223  *
9224  * Input
9225  * 
9226  */
9227
9228 /**
9229  * @class Roo.bootstrap.Input
9230  * @extends Roo.bootstrap.Component
9231  * Bootstrap Input class
9232  * @cfg {Boolean} disabled is it disabled
9233  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
9234  * @cfg {String} name name of the input
9235  * @cfg {string} fieldLabel - the label associated
9236  * @cfg {string} placeholder - placeholder to put in text.
9237  * @cfg {string}  before - input group add on before
9238  * @cfg {string} after - input group add on after
9239  * @cfg {string} size - (lg|sm) or leave empty..
9240  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
9241  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
9242  * @cfg {Number} md colspan out of 12 for computer-sized screens
9243  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
9244  * @cfg {string} value default value of the input
9245  * @cfg {Number} labelWidth set the width of label 
9246  * @cfg {Number} labellg set the width of label (1-12)
9247  * @cfg {Number} labelmd set the width of label (1-12)
9248  * @cfg {Number} labelsm set the width of label (1-12)
9249  * @cfg {Number} labelxs set the width of label (1-12)
9250  * @cfg {String} labelAlign (top|left)
9251  * @cfg {Boolean} readOnly Specifies that the field should be read-only
9252  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9253  * @cfg {String} indicatorpos (left|right) default left
9254  * @cfg {String} capture (user|camera) use for file input only. (default empty)
9255  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9256
9257  * @cfg {String} align (left|center|right) Default left
9258  * @cfg {Boolean} forceFeedback (true|false) Default false
9259  * 
9260  * @constructor
9261  * Create a new Input
9262  * @param {Object} config The config object
9263  */
9264
9265 Roo.bootstrap.Input = function(config){
9266     
9267     Roo.bootstrap.Input.superclass.constructor.call(this, config);
9268     
9269     this.addEvents({
9270         /**
9271          * @event focus
9272          * Fires when this field receives input focus.
9273          * @param {Roo.form.Field} this
9274          */
9275         focus : true,
9276         /**
9277          * @event blur
9278          * Fires when this field loses input focus.
9279          * @param {Roo.form.Field} this
9280          */
9281         blur : true,
9282         /**
9283          * @event specialkey
9284          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9285          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9286          * @param {Roo.form.Field} this
9287          * @param {Roo.EventObject} e The event object
9288          */
9289         specialkey : true,
9290         /**
9291          * @event change
9292          * Fires just before the field blurs if the field value has changed.
9293          * @param {Roo.form.Field} this
9294          * @param {Mixed} newValue The new value
9295          * @param {Mixed} oldValue The original value
9296          */
9297         change : true,
9298         /**
9299          * @event invalid
9300          * Fires after the field has been marked as invalid.
9301          * @param {Roo.form.Field} this
9302          * @param {String} msg The validation message
9303          */
9304         invalid : true,
9305         /**
9306          * @event valid
9307          * Fires after the field has been validated with no errors.
9308          * @param {Roo.form.Field} this
9309          */
9310         valid : true,
9311          /**
9312          * @event keyup
9313          * Fires after the key up
9314          * @param {Roo.form.Field} this
9315          * @param {Roo.EventObject}  e The event Object
9316          */
9317         keyup : true
9318     });
9319 };
9320
9321 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
9322      /**
9323      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9324       automatic validation (defaults to "keyup").
9325      */
9326     validationEvent : "keyup",
9327      /**
9328      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9329      */
9330     validateOnBlur : true,
9331     /**
9332      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9333      */
9334     validationDelay : 250,
9335      /**
9336      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9337      */
9338     focusClass : "x-form-focus",  // not needed???
9339     
9340        
9341     /**
9342      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9343      */
9344     invalidClass : "has-warning",
9345     
9346     /**
9347      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9348      */
9349     validClass : "has-success",
9350     
9351     /**
9352      * @cfg {Boolean} hasFeedback (true|false) default true
9353      */
9354     hasFeedback : true,
9355     
9356     /**
9357      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9358      */
9359     invalidFeedbackClass : "glyphicon-warning-sign",
9360     
9361     /**
9362      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9363      */
9364     validFeedbackClass : "glyphicon-ok",
9365     
9366     /**
9367      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9368      */
9369     selectOnFocus : false,
9370     
9371      /**
9372      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9373      */
9374     maskRe : null,
9375        /**
9376      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9377      */
9378     vtype : null,
9379     
9380       /**
9381      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9382      */
9383     disableKeyFilter : false,
9384     
9385        /**
9386      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9387      */
9388     disabled : false,
9389      /**
9390      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9391      */
9392     allowBlank : true,
9393     /**
9394      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9395      */
9396     blankText : "Please complete this mandatory field",
9397     
9398      /**
9399      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9400      */
9401     minLength : 0,
9402     /**
9403      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9404      */
9405     maxLength : Number.MAX_VALUE,
9406     /**
9407      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9408      */
9409     minLengthText : "The minimum length for this field is {0}",
9410     /**
9411      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9412      */
9413     maxLengthText : "The maximum length for this field is {0}",
9414   
9415     
9416     /**
9417      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9418      * If available, this function will be called only after the basic validators all return true, and will be passed the
9419      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9420      */
9421     validator : null,
9422     /**
9423      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9424      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9425      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9426      */
9427     regex : null,
9428     /**
9429      * @cfg {String} regexText -- Depricated - use Invalid Text
9430      */
9431     regexText : "",
9432     
9433     /**
9434      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9435      */
9436     invalidText : "",
9437     
9438     
9439     
9440     autocomplete: false,
9441     
9442     
9443     fieldLabel : '',
9444     inputType : 'text',
9445     
9446     name : false,
9447     placeholder: false,
9448     before : false,
9449     after : false,
9450     size : false,
9451     hasFocus : false,
9452     preventMark: false,
9453     isFormField : true,
9454     value : '',
9455     labelWidth : 2,
9456     labelAlign : false,
9457     readOnly : false,
9458     align : false,
9459     formatedValue : false,
9460     forceFeedback : false,
9461     
9462     indicatorpos : 'left',
9463     
9464     labellg : 0,
9465     labelmd : 0,
9466     labelsm : 0,
9467     labelxs : 0,
9468     
9469     capture : '',
9470     accept : '',
9471     
9472     parentLabelAlign : function()
9473     {
9474         var parent = this;
9475         while (parent.parent()) {
9476             parent = parent.parent();
9477             if (typeof(parent.labelAlign) !='undefined') {
9478                 return parent.labelAlign;
9479             }
9480         }
9481         return 'left';
9482         
9483     },
9484     
9485     getAutoCreate : function()
9486     {
9487         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9488         
9489         var id = Roo.id();
9490         
9491         var cfg = {};
9492         
9493         if(this.inputType != 'hidden'){
9494             cfg.cls = 'form-group' //input-group
9495         }
9496         
9497         var input =  {
9498             tag: 'input',
9499             id : id,
9500             type : this.inputType,
9501             value : this.value,
9502             cls : 'form-control',
9503             placeholder : this.placeholder || '',
9504             autocomplete : this.autocomplete || 'new-password'
9505         };
9506         
9507         if(this.capture.length){
9508             input.capture = this.capture;
9509         }
9510         
9511         if(this.accept.length){
9512             input.accept = this.accept + "/*";
9513         }
9514         
9515         if(this.align){
9516             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9517         }
9518         
9519         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9520             input.maxLength = this.maxLength;
9521         }
9522         
9523         if (this.disabled) {
9524             input.disabled=true;
9525         }
9526         
9527         if (this.readOnly) {
9528             input.readonly=true;
9529         }
9530         
9531         if (this.name) {
9532             input.name = this.name;
9533         }
9534         
9535         if (this.size) {
9536             input.cls += ' input-' + this.size;
9537         }
9538         
9539         var settings=this;
9540         ['xs','sm','md','lg'].map(function(size){
9541             if (settings[size]) {
9542                 cfg.cls += ' col-' + size + '-' + settings[size];
9543             }
9544         });
9545         
9546         var inputblock = input;
9547         
9548         var feedback = {
9549             tag: 'span',
9550             cls: 'glyphicon form-control-feedback'
9551         };
9552             
9553         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9554             
9555             inputblock = {
9556                 cls : 'has-feedback',
9557                 cn :  [
9558                     input,
9559                     feedback
9560                 ] 
9561             };  
9562         }
9563         
9564         if (this.before || this.after) {
9565             
9566             inputblock = {
9567                 cls : 'input-group',
9568                 cn :  [] 
9569             };
9570             
9571             if (this.before && typeof(this.before) == 'string') {
9572                 
9573                 inputblock.cn.push({
9574                     tag :'span',
9575                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9576                     html : this.before
9577                 });
9578             }
9579             if (this.before && typeof(this.before) == 'object') {
9580                 this.before = Roo.factory(this.before);
9581                 
9582                 inputblock.cn.push({
9583                     tag :'span',
9584                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9585                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9586                 });
9587             }
9588             
9589             inputblock.cn.push(input);
9590             
9591             if (this.after && typeof(this.after) == 'string') {
9592                 inputblock.cn.push({
9593                     tag :'span',
9594                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9595                     html : this.after
9596                 });
9597             }
9598             if (this.after && typeof(this.after) == 'object') {
9599                 this.after = Roo.factory(this.after);
9600                 
9601                 inputblock.cn.push({
9602                     tag :'span',
9603                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9604                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9605                 });
9606             }
9607             
9608             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9609                 inputblock.cls += ' has-feedback';
9610                 inputblock.cn.push(feedback);
9611             }
9612         };
9613         var indicator = {
9614             tag : 'i',
9615             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9616             tooltip : 'This field is required'
9617         };
9618         if (Roo.bootstrap.version == 4) {
9619             indicator = {
9620                 tag : 'i',
9621                 style : 'display-none'
9622             };
9623         }
9624         if (align ==='left' && this.fieldLabel.length) {
9625             
9626             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9627             
9628             cfg.cn = [
9629                 indicator,
9630                 {
9631                     tag: 'label',
9632                     'for' :  id,
9633                     cls : 'control-label col-form-label',
9634                     html : this.fieldLabel
9635
9636                 },
9637                 {
9638                     cls : "", 
9639                     cn: [
9640                         inputblock
9641                     ]
9642                 }
9643             ];
9644             
9645             var labelCfg = cfg.cn[1];
9646             var contentCfg = cfg.cn[2];
9647             
9648             if(this.indicatorpos == 'right'){
9649                 cfg.cn = [
9650                     {
9651                         tag: 'label',
9652                         'for' :  id,
9653                         cls : 'control-label col-form-label',
9654                         cn : [
9655                             {
9656                                 tag : 'span',
9657                                 html : this.fieldLabel
9658                             },
9659                             indicator
9660                         ]
9661                     },
9662                     {
9663                         cls : "",
9664                         cn: [
9665                             inputblock
9666                         ]
9667                     }
9668
9669                 ];
9670                 
9671                 labelCfg = cfg.cn[0];
9672                 contentCfg = cfg.cn[1];
9673             
9674             }
9675             
9676             if(this.labelWidth > 12){
9677                 labelCfg.style = "width: " + this.labelWidth + 'px';
9678             }
9679             
9680             if(this.labelWidth < 13 && this.labelmd == 0){
9681                 this.labelmd = this.labelWidth;
9682             }
9683             
9684             if(this.labellg > 0){
9685                 labelCfg.cls += ' col-lg-' + this.labellg;
9686                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9687             }
9688             
9689             if(this.labelmd > 0){
9690                 labelCfg.cls += ' col-md-' + this.labelmd;
9691                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9692             }
9693             
9694             if(this.labelsm > 0){
9695                 labelCfg.cls += ' col-sm-' + this.labelsm;
9696                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9697             }
9698             
9699             if(this.labelxs > 0){
9700                 labelCfg.cls += ' col-xs-' + this.labelxs;
9701                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9702             }
9703             
9704             
9705         } else if ( this.fieldLabel.length) {
9706                 
9707             cfg.cn = [
9708                 {
9709                     tag : 'i',
9710                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9711                     tooltip : 'This field is required'
9712                 },
9713                 {
9714                     tag: 'label',
9715                    //cls : 'input-group-addon',
9716                     html : this.fieldLabel
9717
9718                 },
9719
9720                inputblock
9721
9722            ];
9723            
9724            if(this.indicatorpos == 'right'){
9725                 
9726                 cfg.cn = [
9727                     {
9728                         tag: 'label',
9729                        //cls : 'input-group-addon',
9730                         html : this.fieldLabel
9731
9732                     },
9733                     {
9734                         tag : 'i',
9735                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9736                         tooltip : 'This field is required'
9737                     },
9738
9739                    inputblock
9740
9741                ];
9742
9743             }
9744
9745         } else {
9746             
9747             cfg.cn = [
9748
9749                     inputblock
9750
9751             ];
9752                 
9753                 
9754         };
9755         
9756         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9757            cfg.cls += ' navbar-form';
9758         }
9759         
9760         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9761             // on BS4 we do this only if not form 
9762             cfg.cls += ' navbar-form';
9763             cfg.tag = 'li';
9764         }
9765         
9766         return cfg;
9767         
9768     },
9769     /**
9770      * return the real input element.
9771      */
9772     inputEl: function ()
9773     {
9774         return this.el.select('input.form-control',true).first();
9775     },
9776     
9777     tooltipEl : function()
9778     {
9779         return this.inputEl();
9780     },
9781     
9782     indicatorEl : function()
9783     {
9784         if (Roo.bootstrap.version == 4) {
9785             return false; // not enabled in v4 yet.
9786         }
9787         
9788         var indicator = this.el.select('i.roo-required-indicator',true).first();
9789         
9790         if(!indicator){
9791             return false;
9792         }
9793         
9794         return indicator;
9795         
9796     },
9797     
9798     setDisabled : function(v)
9799     {
9800         var i  = this.inputEl().dom;
9801         if (!v) {
9802             i.removeAttribute('disabled');
9803             return;
9804             
9805         }
9806         i.setAttribute('disabled','true');
9807     },
9808     initEvents : function()
9809     {
9810           
9811         this.inputEl().on("keydown" , this.fireKey,  this);
9812         this.inputEl().on("focus", this.onFocus,  this);
9813         this.inputEl().on("blur", this.onBlur,  this);
9814         
9815         this.inputEl().relayEvent('keyup', this);
9816         
9817         this.indicator = this.indicatorEl();
9818         
9819         if(this.indicator){
9820             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9821         }
9822  
9823         // reference to original value for reset
9824         this.originalValue = this.getValue();
9825         //Roo.form.TextField.superclass.initEvents.call(this);
9826         if(this.validationEvent == 'keyup'){
9827             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9828             this.inputEl().on('keyup', this.filterValidation, this);
9829         }
9830         else if(this.validationEvent !== false){
9831             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9832         }
9833         
9834         if(this.selectOnFocus){
9835             this.on("focus", this.preFocus, this);
9836             
9837         }
9838         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9839             this.inputEl().on("keypress", this.filterKeys, this);
9840         } else {
9841             this.inputEl().relayEvent('keypress', this);
9842         }
9843        /* if(this.grow){
9844             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9845             this.el.on("click", this.autoSize,  this);
9846         }
9847         */
9848         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9849             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9850         }
9851         
9852         if (typeof(this.before) == 'object') {
9853             this.before.render(this.el.select('.roo-input-before',true).first());
9854         }
9855         if (typeof(this.after) == 'object') {
9856             this.after.render(this.el.select('.roo-input-after',true).first());
9857         }
9858         
9859         this.inputEl().on('change', this.onChange, this);
9860         
9861     },
9862     filterValidation : function(e){
9863         if(!e.isNavKeyPress()){
9864             this.validationTask.delay(this.validationDelay);
9865         }
9866     },
9867      /**
9868      * Validates the field value
9869      * @return {Boolean} True if the value is valid, else false
9870      */
9871     validate : function(){
9872         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9873         if(this.disabled || this.validateValue(this.getRawValue())){
9874             this.markValid();
9875             return true;
9876         }
9877         
9878         this.markInvalid();
9879         return false;
9880     },
9881     
9882     
9883     /**
9884      * Validates a value according to the field's validation rules and marks the field as invalid
9885      * if the validation fails
9886      * @param {Mixed} value The value to validate
9887      * @return {Boolean} True if the value is valid, else false
9888      */
9889     validateValue : function(value)
9890     {
9891         if(this.getVisibilityEl().hasClass('hidden')){
9892             return true;
9893         }
9894         
9895         if(value.length < 1)  { // if it's blank
9896             if(this.allowBlank){
9897                 return true;
9898             }
9899             return false;
9900         }
9901         
9902         if(value.length < this.minLength){
9903             return false;
9904         }
9905         if(value.length > this.maxLength){
9906             return false;
9907         }
9908         if(this.vtype){
9909             var vt = Roo.form.VTypes;
9910             if(!vt[this.vtype](value, this)){
9911                 return false;
9912             }
9913         }
9914         if(typeof this.validator == "function"){
9915             var msg = this.validator(value);
9916             if(msg !== true){
9917                 return false;
9918             }
9919             if (typeof(msg) == 'string') {
9920                 this.invalidText = msg;
9921             }
9922         }
9923         
9924         if(this.regex && !this.regex.test(value)){
9925             return false;
9926         }
9927         
9928         return true;
9929     },
9930     
9931      // private
9932     fireKey : function(e){
9933         //Roo.log('field ' + e.getKey());
9934         if(e.isNavKeyPress()){
9935             this.fireEvent("specialkey", this, e);
9936         }
9937     },
9938     focus : function (selectText){
9939         if(this.rendered){
9940             this.inputEl().focus();
9941             if(selectText === true){
9942                 this.inputEl().dom.select();
9943             }
9944         }
9945         return this;
9946     } ,
9947     
9948     onFocus : function(){
9949         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9950            // this.el.addClass(this.focusClass);
9951         }
9952         if(!this.hasFocus){
9953             this.hasFocus = true;
9954             this.startValue = this.getValue();
9955             this.fireEvent("focus", this);
9956         }
9957     },
9958     
9959     beforeBlur : Roo.emptyFn,
9960
9961     
9962     // private
9963     onBlur : function(){
9964         this.beforeBlur();
9965         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9966             //this.el.removeClass(this.focusClass);
9967         }
9968         this.hasFocus = false;
9969         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9970             this.validate();
9971         }
9972         var v = this.getValue();
9973         if(String(v) !== String(this.startValue)){
9974             this.fireEvent('change', this, v, this.startValue);
9975         }
9976         this.fireEvent("blur", this);
9977     },
9978     
9979     onChange : function(e)
9980     {
9981         var v = this.getValue();
9982         if(String(v) !== String(this.startValue)){
9983             this.fireEvent('change', this, v, this.startValue);
9984         }
9985         
9986     },
9987     
9988     /**
9989      * Resets the current field value to the originally loaded value and clears any validation messages
9990      */
9991     reset : function(){
9992         this.setValue(this.originalValue);
9993         this.validate();
9994     },
9995      /**
9996      * Returns the name of the field
9997      * @return {Mixed} name The name field
9998      */
9999     getName: function(){
10000         return this.name;
10001     },
10002      /**
10003      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
10004      * @return {Mixed} value The field value
10005      */
10006     getValue : function(){
10007         
10008         var v = this.inputEl().getValue();
10009         
10010         return v;
10011     },
10012     /**
10013      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
10014      * @return {Mixed} value The field value
10015      */
10016     getRawValue : function(){
10017         var v = this.inputEl().getValue();
10018         
10019         return v;
10020     },
10021     
10022     /**
10023      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
10024      * @param {Mixed} value The value to set
10025      */
10026     setRawValue : function(v){
10027         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10028     },
10029     
10030     selectText : function(start, end){
10031         var v = this.getRawValue();
10032         if(v.length > 0){
10033             start = start === undefined ? 0 : start;
10034             end = end === undefined ? v.length : end;
10035             var d = this.inputEl().dom;
10036             if(d.setSelectionRange){
10037                 d.setSelectionRange(start, end);
10038             }else if(d.createTextRange){
10039                 var range = d.createTextRange();
10040                 range.moveStart("character", start);
10041                 range.moveEnd("character", v.length-end);
10042                 range.select();
10043             }
10044         }
10045     },
10046     
10047     /**
10048      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
10049      * @param {Mixed} value The value to set
10050      */
10051     setValue : function(v){
10052         this.value = v;
10053         if(this.rendered){
10054             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10055             this.validate();
10056         }
10057     },
10058     
10059     /*
10060     processValue : function(value){
10061         if(this.stripCharsRe){
10062             var newValue = value.replace(this.stripCharsRe, '');
10063             if(newValue !== value){
10064                 this.setRawValue(newValue);
10065                 return newValue;
10066             }
10067         }
10068         return value;
10069     },
10070   */
10071     preFocus : function(){
10072         
10073         if(this.selectOnFocus){
10074             this.inputEl().dom.select();
10075         }
10076     },
10077     filterKeys : function(e){
10078         var k = e.getKey();
10079         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
10080             return;
10081         }
10082         var c = e.getCharCode(), cc = String.fromCharCode(c);
10083         if(Roo.isIE && (e.isSpecialKey() || !cc)){
10084             return;
10085         }
10086         if(!this.maskRe.test(cc)){
10087             e.stopEvent();
10088         }
10089     },
10090      /**
10091      * Clear any invalid styles/messages for this field
10092      */
10093     clearInvalid : function(){
10094         
10095         if(!this.el || this.preventMark){ // not rendered
10096             return;
10097         }
10098         
10099         
10100         this.el.removeClass([this.invalidClass, 'is-invalid']);
10101         
10102         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10103             
10104             var feedback = this.el.select('.form-control-feedback', true).first();
10105             
10106             if(feedback){
10107                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10108             }
10109             
10110         }
10111         
10112         if(this.indicator){
10113             this.indicator.removeClass('visible');
10114             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10115         }
10116         
10117         this.fireEvent('valid', this);
10118     },
10119     
10120      /**
10121      * Mark this field as valid
10122      */
10123     markValid : function()
10124     {
10125         if(!this.el  || this.preventMark){ // not rendered...
10126             return;
10127         }
10128         
10129         this.el.removeClass([this.invalidClass, this.validClass]);
10130         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10131
10132         var feedback = this.el.select('.form-control-feedback', true).first();
10133             
10134         if(feedback){
10135             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10136         }
10137         
10138         if(this.indicator){
10139             this.indicator.removeClass('visible');
10140             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10141         }
10142         
10143         if(this.disabled){
10144             return;
10145         }
10146         
10147         if(this.allowBlank && !this.getRawValue().length){
10148             return;
10149         }
10150         if (Roo.bootstrap.version == 3) {
10151             this.el.addClass(this.validClass);
10152         } else {
10153             this.inputEl().addClass('is-valid');
10154         }
10155
10156         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10157             
10158             var feedback = this.el.select('.form-control-feedback', true).first();
10159             
10160             if(feedback){
10161                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10162                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10163             }
10164             
10165         }
10166         
10167         this.fireEvent('valid', this);
10168     },
10169     
10170      /**
10171      * Mark this field as invalid
10172      * @param {String} msg The validation message
10173      */
10174     markInvalid : function(msg)
10175     {
10176         if(!this.el  || this.preventMark){ // not rendered
10177             return;
10178         }
10179         
10180         this.el.removeClass([this.invalidClass, this.validClass]);
10181         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10182         
10183         var feedback = this.el.select('.form-control-feedback', true).first();
10184             
10185         if(feedback){
10186             this.el.select('.form-control-feedback', true).first().removeClass(
10187                     [this.invalidFeedbackClass, this.validFeedbackClass]);
10188         }
10189
10190         if(this.disabled){
10191             return;
10192         }
10193         
10194         if(this.allowBlank && !this.getRawValue().length){
10195             return;
10196         }
10197         
10198         if(this.indicator){
10199             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10200             this.indicator.addClass('visible');
10201         }
10202         if (Roo.bootstrap.version == 3) {
10203             this.el.addClass(this.invalidClass);
10204         } else {
10205             this.inputEl().addClass('is-invalid');
10206         }
10207         
10208         
10209         
10210         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10211             
10212             var feedback = this.el.select('.form-control-feedback', true).first();
10213             
10214             if(feedback){
10215                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10216                 
10217                 if(this.getValue().length || this.forceFeedback){
10218                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10219                 }
10220                 
10221             }
10222             
10223         }
10224         
10225         this.fireEvent('invalid', this, msg);
10226     },
10227     // private
10228     SafariOnKeyDown : function(event)
10229     {
10230         // this is a workaround for a password hang bug on chrome/ webkit.
10231         if (this.inputEl().dom.type != 'password') {
10232             return;
10233         }
10234         
10235         var isSelectAll = false;
10236         
10237         if(this.inputEl().dom.selectionEnd > 0){
10238             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
10239         }
10240         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
10241             event.preventDefault();
10242             this.setValue('');
10243             return;
10244         }
10245         
10246         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
10247             
10248             event.preventDefault();
10249             // this is very hacky as keydown always get's upper case.
10250             //
10251             var cc = String.fromCharCode(event.getCharCode());
10252             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
10253             
10254         }
10255     },
10256     adjustWidth : function(tag, w){
10257         tag = tag.toLowerCase();
10258         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10259             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10260                 if(tag == 'input'){
10261                     return w + 2;
10262                 }
10263                 if(tag == 'textarea'){
10264                     return w-2;
10265                 }
10266             }else if(Roo.isOpera){
10267                 if(tag == 'input'){
10268                     return w + 2;
10269                 }
10270                 if(tag == 'textarea'){
10271                     return w-2;
10272                 }
10273             }
10274         }
10275         return w;
10276     },
10277     
10278     setFieldLabel : function(v)
10279     {
10280         if(!this.rendered){
10281             return;
10282         }
10283         
10284         if(this.indicatorEl()){
10285             var ar = this.el.select('label > span',true);
10286             
10287             if (ar.elements.length) {
10288                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10289                 this.fieldLabel = v;
10290                 return;
10291             }
10292             
10293             var br = this.el.select('label',true);
10294             
10295             if(br.elements.length) {
10296                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10297                 this.fieldLabel = v;
10298                 return;
10299             }
10300             
10301             Roo.log('Cannot Found any of label > span || label in input');
10302             return;
10303         }
10304         
10305         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10306         this.fieldLabel = v;
10307         
10308         
10309     }
10310 });
10311
10312  
10313 /*
10314  * - LGPL
10315  *
10316  * Input
10317  * 
10318  */
10319
10320 /**
10321  * @class Roo.bootstrap.TextArea
10322  * @extends Roo.bootstrap.Input
10323  * Bootstrap TextArea class
10324  * @cfg {Number} cols Specifies the visible width of a text area
10325  * @cfg {Number} rows Specifies the visible number of lines in a text area
10326  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10327  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10328  * @cfg {string} html text
10329  * 
10330  * @constructor
10331  * Create a new TextArea
10332  * @param {Object} config The config object
10333  */
10334
10335 Roo.bootstrap.TextArea = function(config){
10336     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10337    
10338 };
10339
10340 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
10341      
10342     cols : false,
10343     rows : 5,
10344     readOnly : false,
10345     warp : 'soft',
10346     resize : false,
10347     value: false,
10348     html: false,
10349     
10350     getAutoCreate : function(){
10351         
10352         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10353         
10354         var id = Roo.id();
10355         
10356         var cfg = {};
10357         
10358         if(this.inputType != 'hidden'){
10359             cfg.cls = 'form-group' //input-group
10360         }
10361         
10362         var input =  {
10363             tag: 'textarea',
10364             id : id,
10365             warp : this.warp,
10366             rows : this.rows,
10367             value : this.value || '',
10368             html: this.html || '',
10369             cls : 'form-control',
10370             placeholder : this.placeholder || '' 
10371             
10372         };
10373         
10374         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10375             input.maxLength = this.maxLength;
10376         }
10377         
10378         if(this.resize){
10379             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10380         }
10381         
10382         if(this.cols){
10383             input.cols = this.cols;
10384         }
10385         
10386         if (this.readOnly) {
10387             input.readonly = true;
10388         }
10389         
10390         if (this.name) {
10391             input.name = this.name;
10392         }
10393         
10394         if (this.size) {
10395             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10396         }
10397         
10398         var settings=this;
10399         ['xs','sm','md','lg'].map(function(size){
10400             if (settings[size]) {
10401                 cfg.cls += ' col-' + size + '-' + settings[size];
10402             }
10403         });
10404         
10405         var inputblock = input;
10406         
10407         if(this.hasFeedback && !this.allowBlank){
10408             
10409             var feedback = {
10410                 tag: 'span',
10411                 cls: 'glyphicon form-control-feedback'
10412             };
10413
10414             inputblock = {
10415                 cls : 'has-feedback',
10416                 cn :  [
10417                     input,
10418                     feedback
10419                 ] 
10420             };  
10421         }
10422         
10423         
10424         if (this.before || this.after) {
10425             
10426             inputblock = {
10427                 cls : 'input-group',
10428                 cn :  [] 
10429             };
10430             if (this.before) {
10431                 inputblock.cn.push({
10432                     tag :'span',
10433                     cls : 'input-group-addon',
10434                     html : this.before
10435                 });
10436             }
10437             
10438             inputblock.cn.push(input);
10439             
10440             if(this.hasFeedback && !this.allowBlank){
10441                 inputblock.cls += ' has-feedback';
10442                 inputblock.cn.push(feedback);
10443             }
10444             
10445             if (this.after) {
10446                 inputblock.cn.push({
10447                     tag :'span',
10448                     cls : 'input-group-addon',
10449                     html : this.after
10450                 });
10451             }
10452             
10453         }
10454         
10455         if (align ==='left' && this.fieldLabel.length) {
10456             cfg.cn = [
10457                 {
10458                     tag: 'label',
10459                     'for' :  id,
10460                     cls : 'control-label',
10461                     html : this.fieldLabel
10462                 },
10463                 {
10464                     cls : "",
10465                     cn: [
10466                         inputblock
10467                     ]
10468                 }
10469
10470             ];
10471             
10472             if(this.labelWidth > 12){
10473                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10474             }
10475
10476             if(this.labelWidth < 13 && this.labelmd == 0){
10477                 this.labelmd = this.labelWidth;
10478             }
10479
10480             if(this.labellg > 0){
10481                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10482                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10483             }
10484
10485             if(this.labelmd > 0){
10486                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10487                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10488             }
10489
10490             if(this.labelsm > 0){
10491                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10492                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10493             }
10494
10495             if(this.labelxs > 0){
10496                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10497                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10498             }
10499             
10500         } else if ( this.fieldLabel.length) {
10501             cfg.cn = [
10502
10503                {
10504                    tag: 'label',
10505                    //cls : 'input-group-addon',
10506                    html : this.fieldLabel
10507
10508                },
10509
10510                inputblock
10511
10512            ];
10513
10514         } else {
10515
10516             cfg.cn = [
10517
10518                 inputblock
10519
10520             ];
10521                 
10522         }
10523         
10524         if (this.disabled) {
10525             input.disabled=true;
10526         }
10527         
10528         return cfg;
10529         
10530     },
10531     /**
10532      * return the real textarea element.
10533      */
10534     inputEl: function ()
10535     {
10536         return this.el.select('textarea.form-control',true).first();
10537     },
10538     
10539     /**
10540      * Clear any invalid styles/messages for this field
10541      */
10542     clearInvalid : function()
10543     {
10544         
10545         if(!this.el || this.preventMark){ // not rendered
10546             return;
10547         }
10548         
10549         var label = this.el.select('label', true).first();
10550         var icon = this.el.select('i.fa-star', true).first();
10551         
10552         if(label && icon){
10553             icon.remove();
10554         }
10555         this.el.removeClass( this.validClass);
10556         this.inputEl().removeClass('is-invalid');
10557          
10558         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10559             
10560             var feedback = this.el.select('.form-control-feedback', true).first();
10561             
10562             if(feedback){
10563                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10564             }
10565             
10566         }
10567         
10568         this.fireEvent('valid', this);
10569     },
10570     
10571      /**
10572      * Mark this field as valid
10573      */
10574     markValid : function()
10575     {
10576         if(!this.el  || this.preventMark){ // not rendered
10577             return;
10578         }
10579         
10580         this.el.removeClass([this.invalidClass, this.validClass]);
10581         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10582         
10583         var feedback = this.el.select('.form-control-feedback', true).first();
10584             
10585         if(feedback){
10586             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10587         }
10588
10589         if(this.disabled || this.allowBlank){
10590             return;
10591         }
10592         
10593         var label = this.el.select('label', true).first();
10594         var icon = this.el.select('i.fa-star', true).first();
10595         
10596         if(label && icon){
10597             icon.remove();
10598         }
10599         if (Roo.bootstrap.version == 3) {
10600             this.el.addClass(this.validClass);
10601         } else {
10602             this.inputEl().addClass('is-valid');
10603         }
10604         
10605         
10606         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10607             
10608             var feedback = this.el.select('.form-control-feedback', true).first();
10609             
10610             if(feedback){
10611                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10612                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10613             }
10614             
10615         }
10616         
10617         this.fireEvent('valid', this);
10618     },
10619     
10620      /**
10621      * Mark this field as invalid
10622      * @param {String} msg The validation message
10623      */
10624     markInvalid : function(msg)
10625     {
10626         if(!this.el  || this.preventMark){ // not rendered
10627             return;
10628         }
10629         
10630         this.el.removeClass([this.invalidClass, this.validClass]);
10631         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10632         
10633         var feedback = this.el.select('.form-control-feedback', true).first();
10634             
10635         if(feedback){
10636             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10637         }
10638
10639         if(this.disabled || this.allowBlank){
10640             return;
10641         }
10642         
10643         var label = this.el.select('label', true).first();
10644         var icon = this.el.select('i.fa-star', true).first();
10645         
10646         if(!this.getValue().length && label && !icon){
10647             this.el.createChild({
10648                 tag : 'i',
10649                 cls : 'text-danger fa fa-lg fa-star',
10650                 tooltip : 'This field is required',
10651                 style : 'margin-right:5px;'
10652             }, label, true);
10653         }
10654         
10655         if (Roo.bootstrap.version == 3) {
10656             this.el.addClass(this.invalidClass);
10657         } else {
10658             this.inputEl().addClass('is-invalid');
10659         }
10660         
10661         // fixme ... this may be depricated need to test..
10662         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10663             
10664             var feedback = this.el.select('.form-control-feedback', true).first();
10665             
10666             if(feedback){
10667                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10668                 
10669                 if(this.getValue().length || this.forceFeedback){
10670                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10671                 }
10672                 
10673             }
10674             
10675         }
10676         
10677         this.fireEvent('invalid', this, msg);
10678     }
10679 });
10680
10681  
10682 /*
10683  * - LGPL
10684  *
10685  * trigger field - base class for combo..
10686  * 
10687  */
10688  
10689 /**
10690  * @class Roo.bootstrap.TriggerField
10691  * @extends Roo.bootstrap.Input
10692  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10693  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10694  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10695  * for which you can provide a custom implementation.  For example:
10696  * <pre><code>
10697 var trigger = new Roo.bootstrap.TriggerField();
10698 trigger.onTriggerClick = myTriggerFn;
10699 trigger.applyTo('my-field');
10700 </code></pre>
10701  *
10702  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10703  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10704  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10705  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10706  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
10707
10708  * @constructor
10709  * Create a new TriggerField.
10710  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10711  * to the base TextField)
10712  */
10713 Roo.bootstrap.TriggerField = function(config){
10714     this.mimicing = false;
10715     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10716 };
10717
10718 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10719     /**
10720      * @cfg {String} triggerClass A CSS class to apply to the trigger
10721      */
10722      /**
10723      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10724      */
10725     hideTrigger:false,
10726
10727     /**
10728      * @cfg {Boolean} removable (true|false) special filter default false
10729      */
10730     removable : false,
10731     
10732     /** @cfg {Boolean} grow @hide */
10733     /** @cfg {Number} growMin @hide */
10734     /** @cfg {Number} growMax @hide */
10735
10736     /**
10737      * @hide 
10738      * @method
10739      */
10740     autoSize: Roo.emptyFn,
10741     // private
10742     monitorTab : true,
10743     // private
10744     deferHeight : true,
10745
10746     
10747     actionMode : 'wrap',
10748     
10749     caret : false,
10750     
10751     
10752     getAutoCreate : function(){
10753        
10754         var align = this.labelAlign || this.parentLabelAlign();
10755         
10756         var id = Roo.id();
10757         
10758         var cfg = {
10759             cls: 'form-group' //input-group
10760         };
10761         
10762         
10763         var input =  {
10764             tag: 'input',
10765             id : id,
10766             type : this.inputType,
10767             cls : 'form-control',
10768             autocomplete: 'new-password',
10769             placeholder : this.placeholder || '' 
10770             
10771         };
10772         if (this.name) {
10773             input.name = this.name;
10774         }
10775         if (this.size) {
10776             input.cls += ' input-' + this.size;
10777         }
10778         
10779         if (this.disabled) {
10780             input.disabled=true;
10781         }
10782         
10783         var inputblock = input;
10784         
10785         if(this.hasFeedback && !this.allowBlank){
10786             
10787             var feedback = {
10788                 tag: 'span',
10789                 cls: 'glyphicon form-control-feedback'
10790             };
10791             
10792             if(this.removable && !this.editable && !this.tickable){
10793                 inputblock = {
10794                     cls : 'has-feedback',
10795                     cn :  [
10796                         inputblock,
10797                         {
10798                             tag: 'button',
10799                             html : 'x',
10800                             cls : 'roo-combo-removable-btn close'
10801                         },
10802                         feedback
10803                     ] 
10804                 };
10805             } else {
10806                 inputblock = {
10807                     cls : 'has-feedback',
10808                     cn :  [
10809                         inputblock,
10810                         feedback
10811                     ] 
10812                 };
10813             }
10814
10815         } else {
10816             if(this.removable && !this.editable && !this.tickable){
10817                 inputblock = {
10818                     cls : 'roo-removable',
10819                     cn :  [
10820                         inputblock,
10821                         {
10822                             tag: 'button',
10823                             html : 'x',
10824                             cls : 'roo-combo-removable-btn close'
10825                         }
10826                     ] 
10827                 };
10828             }
10829         }
10830         
10831         if (this.before || this.after) {
10832             
10833             inputblock = {
10834                 cls : 'input-group',
10835                 cn :  [] 
10836             };
10837             if (this.before) {
10838                 inputblock.cn.push({
10839                     tag :'span',
10840                     cls : 'input-group-addon input-group-prepend input-group-text',
10841                     html : this.before
10842                 });
10843             }
10844             
10845             inputblock.cn.push(input);
10846             
10847             if(this.hasFeedback && !this.allowBlank){
10848                 inputblock.cls += ' has-feedback';
10849                 inputblock.cn.push(feedback);
10850             }
10851             
10852             if (this.after) {
10853                 inputblock.cn.push({
10854                     tag :'span',
10855                     cls : 'input-group-addon input-group-append input-group-text',
10856                     html : this.after
10857                 });
10858             }
10859             
10860         };
10861         
10862       
10863         
10864         var ibwrap = inputblock;
10865         
10866         if(this.multiple){
10867             ibwrap = {
10868                 tag: 'ul',
10869                 cls: 'roo-select2-choices',
10870                 cn:[
10871                     {
10872                         tag: 'li',
10873                         cls: 'roo-select2-search-field',
10874                         cn: [
10875
10876                             inputblock
10877                         ]
10878                     }
10879                 ]
10880             };
10881                 
10882         }
10883         
10884         var combobox = {
10885             cls: 'roo-select2-container input-group',
10886             cn: [
10887                  {
10888                     tag: 'input',
10889                     type : 'hidden',
10890                     cls: 'form-hidden-field'
10891                 },
10892                 ibwrap
10893             ]
10894         };
10895         
10896         if(!this.multiple && this.showToggleBtn){
10897             
10898             var caret = {
10899                         tag: 'span',
10900                         cls: 'caret'
10901              };
10902             if (this.caret != false) {
10903                 caret = {
10904                      tag: 'i',
10905                      cls: 'fa fa-' + this.caret
10906                 };
10907                 
10908             }
10909             
10910             combobox.cn.push({
10911                 tag :'span',
10912                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10913                 cn : [
10914                     Roo.bootstrap.version == 3 ? caret : '',
10915                     {
10916                         tag: 'span',
10917                         cls: 'combobox-clear',
10918                         cn  : [
10919                             {
10920                                 tag : 'i',
10921                                 cls: 'icon-remove'
10922                             }
10923                         ]
10924                     }
10925                 ]
10926
10927             })
10928         }
10929         
10930         if(this.multiple){
10931             combobox.cls += ' roo-select2-container-multi';
10932         }
10933          var indicator = {
10934             tag : 'i',
10935             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10936             tooltip : 'This field is required'
10937         };
10938         if (Roo.bootstrap.version == 4) {
10939             indicator = {
10940                 tag : 'i',
10941                 style : 'display:none'
10942             };
10943         }
10944         
10945         
10946         if (align ==='left' && this.fieldLabel.length) {
10947             
10948             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10949
10950             cfg.cn = [
10951                 indicator,
10952                 {
10953                     tag: 'label',
10954                     'for' :  id,
10955                     cls : 'control-label',
10956                     html : this.fieldLabel
10957
10958                 },
10959                 {
10960                     cls : "", 
10961                     cn: [
10962                         combobox
10963                     ]
10964                 }
10965
10966             ];
10967             
10968             var labelCfg = cfg.cn[1];
10969             var contentCfg = cfg.cn[2];
10970             
10971             if(this.indicatorpos == 'right'){
10972                 cfg.cn = [
10973                     {
10974                         tag: 'label',
10975                         'for' :  id,
10976                         cls : 'control-label',
10977                         cn : [
10978                             {
10979                                 tag : 'span',
10980                                 html : this.fieldLabel
10981                             },
10982                             indicator
10983                         ]
10984                     },
10985                     {
10986                         cls : "", 
10987                         cn: [
10988                             combobox
10989                         ]
10990                     }
10991
10992                 ];
10993                 
10994                 labelCfg = cfg.cn[0];
10995                 contentCfg = cfg.cn[1];
10996             }
10997             
10998             if(this.labelWidth > 12){
10999                 labelCfg.style = "width: " + this.labelWidth + 'px';
11000             }
11001             
11002             if(this.labelWidth < 13 && this.labelmd == 0){
11003                 this.labelmd = this.labelWidth;
11004             }
11005             
11006             if(this.labellg > 0){
11007                 labelCfg.cls += ' col-lg-' + this.labellg;
11008                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11009             }
11010             
11011             if(this.labelmd > 0){
11012                 labelCfg.cls += ' col-md-' + this.labelmd;
11013                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11014             }
11015             
11016             if(this.labelsm > 0){
11017                 labelCfg.cls += ' col-sm-' + this.labelsm;
11018                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11019             }
11020             
11021             if(this.labelxs > 0){
11022                 labelCfg.cls += ' col-xs-' + this.labelxs;
11023                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11024             }
11025             
11026         } else if ( this.fieldLabel.length) {
11027 //                Roo.log(" label");
11028             cfg.cn = [
11029                 indicator,
11030                {
11031                    tag: 'label',
11032                    //cls : 'input-group-addon',
11033                    html : this.fieldLabel
11034
11035                },
11036
11037                combobox
11038
11039             ];
11040             
11041             if(this.indicatorpos == 'right'){
11042                 
11043                 cfg.cn = [
11044                     {
11045                        tag: 'label',
11046                        cn : [
11047                            {
11048                                tag : 'span',
11049                                html : this.fieldLabel
11050                            },
11051                            indicator
11052                        ]
11053
11054                     },
11055                     combobox
11056
11057                 ];
11058
11059             }
11060
11061         } else {
11062             
11063 //                Roo.log(" no label && no align");
11064                 cfg = combobox
11065                      
11066                 
11067         }
11068         
11069         var settings=this;
11070         ['xs','sm','md','lg'].map(function(size){
11071             if (settings[size]) {
11072                 cfg.cls += ' col-' + size + '-' + settings[size];
11073             }
11074         });
11075         
11076         return cfg;
11077         
11078     },
11079     
11080     
11081     
11082     // private
11083     onResize : function(w, h){
11084 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
11085 //        if(typeof w == 'number'){
11086 //            var x = w - this.trigger.getWidth();
11087 //            this.inputEl().setWidth(this.adjustWidth('input', x));
11088 //            this.trigger.setStyle('left', x+'px');
11089 //        }
11090     },
11091
11092     // private
11093     adjustSize : Roo.BoxComponent.prototype.adjustSize,
11094
11095     // private
11096     getResizeEl : function(){
11097         return this.inputEl();
11098     },
11099
11100     // private
11101     getPositionEl : function(){
11102         return this.inputEl();
11103     },
11104
11105     // private
11106     alignErrorIcon : function(){
11107         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
11108     },
11109
11110     // private
11111     initEvents : function(){
11112         
11113         this.createList();
11114         
11115         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
11116         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
11117         if(!this.multiple && this.showToggleBtn){
11118             this.trigger = this.el.select('span.dropdown-toggle',true).first();
11119             if(this.hideTrigger){
11120                 this.trigger.setDisplayed(false);
11121             }
11122             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
11123         }
11124         
11125         if(this.multiple){
11126             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
11127         }
11128         
11129         if(this.removable && !this.editable && !this.tickable){
11130             var close = this.closeTriggerEl();
11131             
11132             if(close){
11133                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
11134                 close.on('click', this.removeBtnClick, this, close);
11135             }
11136         }
11137         
11138         //this.trigger.addClassOnOver('x-form-trigger-over');
11139         //this.trigger.addClassOnClick('x-form-trigger-click');
11140         
11141         //if(!this.width){
11142         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
11143         //}
11144     },
11145     
11146     closeTriggerEl : function()
11147     {
11148         var close = this.el.select('.roo-combo-removable-btn', true).first();
11149         return close ? close : false;
11150     },
11151     
11152     removeBtnClick : function(e, h, el)
11153     {
11154         e.preventDefault();
11155         
11156         if(this.fireEvent("remove", this) !== false){
11157             this.reset();
11158             this.fireEvent("afterremove", this)
11159         }
11160     },
11161     
11162     createList : function()
11163     {
11164         this.list = Roo.get(document.body).createChild({
11165             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
11166             cls: 'typeahead typeahead-long dropdown-menu',
11167             style: 'display:none'
11168         });
11169         
11170         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
11171         
11172     },
11173
11174     // private
11175     initTrigger : function(){
11176        
11177     },
11178
11179     // private
11180     onDestroy : function(){
11181         if(this.trigger){
11182             this.trigger.removeAllListeners();
11183           //  this.trigger.remove();
11184         }
11185         //if(this.wrap){
11186         //    this.wrap.remove();
11187         //}
11188         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
11189     },
11190
11191     // private
11192     onFocus : function(){
11193         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
11194         /*
11195         if(!this.mimicing){
11196             this.wrap.addClass('x-trigger-wrap-focus');
11197             this.mimicing = true;
11198             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
11199             if(this.monitorTab){
11200                 this.el.on("keydown", this.checkTab, this);
11201             }
11202         }
11203         */
11204     },
11205
11206     // private
11207     checkTab : function(e){
11208         if(e.getKey() == e.TAB){
11209             this.triggerBlur();
11210         }
11211     },
11212
11213     // private
11214     onBlur : function(){
11215         // do nothing
11216     },
11217
11218     // private
11219     mimicBlur : function(e, t){
11220         /*
11221         if(!this.wrap.contains(t) && this.validateBlur()){
11222             this.triggerBlur();
11223         }
11224         */
11225     },
11226
11227     // private
11228     triggerBlur : function(){
11229         this.mimicing = false;
11230         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
11231         if(this.monitorTab){
11232             this.el.un("keydown", this.checkTab, this);
11233         }
11234         //this.wrap.removeClass('x-trigger-wrap-focus');
11235         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
11236     },
11237
11238     // private
11239     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
11240     validateBlur : function(e, t){
11241         return true;
11242     },
11243
11244     // private
11245     onDisable : function(){
11246         this.inputEl().dom.disabled = true;
11247         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
11248         //if(this.wrap){
11249         //    this.wrap.addClass('x-item-disabled');
11250         //}
11251     },
11252
11253     // private
11254     onEnable : function(){
11255         this.inputEl().dom.disabled = false;
11256         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11257         //if(this.wrap){
11258         //    this.el.removeClass('x-item-disabled');
11259         //}
11260     },
11261
11262     // private
11263     onShow : function(){
11264         var ae = this.getActionEl();
11265         
11266         if(ae){
11267             ae.dom.style.display = '';
11268             ae.dom.style.visibility = 'visible';
11269         }
11270     },
11271
11272     // private
11273     
11274     onHide : function(){
11275         var ae = this.getActionEl();
11276         ae.dom.style.display = 'none';
11277     },
11278
11279     /**
11280      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
11281      * by an implementing function.
11282      * @method
11283      * @param {EventObject} e
11284      */
11285     onTriggerClick : Roo.emptyFn
11286 });
11287  /*
11288  * Based on:
11289  * Ext JS Library 1.1.1
11290  * Copyright(c) 2006-2007, Ext JS, LLC.
11291  *
11292  * Originally Released Under LGPL - original licence link has changed is not relivant.
11293  *
11294  * Fork - LGPL
11295  * <script type="text/javascript">
11296  */
11297
11298
11299 /**
11300  * @class Roo.data.SortTypes
11301  * @singleton
11302  * Defines the default sorting (casting?) comparison functions used when sorting data.
11303  */
11304 Roo.data.SortTypes = {
11305     /**
11306      * Default sort that does nothing
11307      * @param {Mixed} s The value being converted
11308      * @return {Mixed} The comparison value
11309      */
11310     none : function(s){
11311         return s;
11312     },
11313     
11314     /**
11315      * The regular expression used to strip tags
11316      * @type {RegExp}
11317      * @property
11318      */
11319     stripTagsRE : /<\/?[^>]+>/gi,
11320     
11321     /**
11322      * Strips all HTML tags to sort on text only
11323      * @param {Mixed} s The value being converted
11324      * @return {String} The comparison value
11325      */
11326     asText : function(s){
11327         return String(s).replace(this.stripTagsRE, "");
11328     },
11329     
11330     /**
11331      * Strips all HTML tags to sort on text only - Case insensitive
11332      * @param {Mixed} s The value being converted
11333      * @return {String} The comparison value
11334      */
11335     asUCText : function(s){
11336         return String(s).toUpperCase().replace(this.stripTagsRE, "");
11337     },
11338     
11339     /**
11340      * Case insensitive string
11341      * @param {Mixed} s The value being converted
11342      * @return {String} The comparison value
11343      */
11344     asUCString : function(s) {
11345         return String(s).toUpperCase();
11346     },
11347     
11348     /**
11349      * Date sorting
11350      * @param {Mixed} s The value being converted
11351      * @return {Number} The comparison value
11352      */
11353     asDate : function(s) {
11354         if(!s){
11355             return 0;
11356         }
11357         if(s instanceof Date){
11358             return s.getTime();
11359         }
11360         return Date.parse(String(s));
11361     },
11362     
11363     /**
11364      * Float sorting
11365      * @param {Mixed} s The value being converted
11366      * @return {Float} The comparison value
11367      */
11368     asFloat : function(s) {
11369         var val = parseFloat(String(s).replace(/,/g, ""));
11370         if(isNaN(val)) {
11371             val = 0;
11372         }
11373         return val;
11374     },
11375     
11376     /**
11377      * Integer sorting
11378      * @param {Mixed} s The value being converted
11379      * @return {Number} The comparison value
11380      */
11381     asInt : function(s) {
11382         var val = parseInt(String(s).replace(/,/g, ""));
11383         if(isNaN(val)) {
11384             val = 0;
11385         }
11386         return val;
11387     }
11388 };/*
11389  * Based on:
11390  * Ext JS Library 1.1.1
11391  * Copyright(c) 2006-2007, Ext JS, LLC.
11392  *
11393  * Originally Released Under LGPL - original licence link has changed is not relivant.
11394  *
11395  * Fork - LGPL
11396  * <script type="text/javascript">
11397  */
11398
11399 /**
11400 * @class Roo.data.Record
11401  * Instances of this class encapsulate both record <em>definition</em> information, and record
11402  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11403  * to access Records cached in an {@link Roo.data.Store} object.<br>
11404  * <p>
11405  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11406  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11407  * objects.<br>
11408  * <p>
11409  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11410  * @constructor
11411  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11412  * {@link #create}. The parameters are the same.
11413  * @param {Array} data An associative Array of data values keyed by the field name.
11414  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11415  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11416  * not specified an integer id is generated.
11417  */
11418 Roo.data.Record = function(data, id){
11419     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11420     this.data = data;
11421 };
11422
11423 /**
11424  * Generate a constructor for a specific record layout.
11425  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11426  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11427  * Each field definition object may contain the following properties: <ul>
11428  * <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,
11429  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11430  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11431  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11432  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11433  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11434  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11435  * this may be omitted.</p></li>
11436  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11437  * <ul><li>auto (Default, implies no conversion)</li>
11438  * <li>string</li>
11439  * <li>int</li>
11440  * <li>float</li>
11441  * <li>boolean</li>
11442  * <li>date</li></ul></p></li>
11443  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11444  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11445  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11446  * by the Reader into an object that will be stored in the Record. It is passed the
11447  * following parameters:<ul>
11448  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11449  * </ul></p></li>
11450  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11451  * </ul>
11452  * <br>usage:<br><pre><code>
11453 var TopicRecord = Roo.data.Record.create(
11454     {name: 'title', mapping: 'topic_title'},
11455     {name: 'author', mapping: 'username'},
11456     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11457     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11458     {name: 'lastPoster', mapping: 'user2'},
11459     {name: 'excerpt', mapping: 'post_text'}
11460 );
11461
11462 var myNewRecord = new TopicRecord({
11463     title: 'Do my job please',
11464     author: 'noobie',
11465     totalPosts: 1,
11466     lastPost: new Date(),
11467     lastPoster: 'Animal',
11468     excerpt: 'No way dude!'
11469 });
11470 myStore.add(myNewRecord);
11471 </code></pre>
11472  * @method create
11473  * @static
11474  */
11475 Roo.data.Record.create = function(o){
11476     var f = function(){
11477         f.superclass.constructor.apply(this, arguments);
11478     };
11479     Roo.extend(f, Roo.data.Record);
11480     var p = f.prototype;
11481     p.fields = new Roo.util.MixedCollection(false, function(field){
11482         return field.name;
11483     });
11484     for(var i = 0, len = o.length; i < len; i++){
11485         p.fields.add(new Roo.data.Field(o[i]));
11486     }
11487     f.getField = function(name){
11488         return p.fields.get(name);  
11489     };
11490     return f;
11491 };
11492
11493 Roo.data.Record.AUTO_ID = 1000;
11494 Roo.data.Record.EDIT = 'edit';
11495 Roo.data.Record.REJECT = 'reject';
11496 Roo.data.Record.COMMIT = 'commit';
11497
11498 Roo.data.Record.prototype = {
11499     /**
11500      * Readonly flag - true if this record has been modified.
11501      * @type Boolean
11502      */
11503     dirty : false,
11504     editing : false,
11505     error: null,
11506     modified: null,
11507
11508     // private
11509     join : function(store){
11510         this.store = store;
11511     },
11512
11513     /**
11514      * Set the named field to the specified value.
11515      * @param {String} name The name of the field to set.
11516      * @param {Object} value The value to set the field to.
11517      */
11518     set : function(name, value){
11519         if(this.data[name] == value){
11520             return;
11521         }
11522         this.dirty = true;
11523         if(!this.modified){
11524             this.modified = {};
11525         }
11526         if(typeof this.modified[name] == 'undefined'){
11527             this.modified[name] = this.data[name];
11528         }
11529         this.data[name] = value;
11530         if(!this.editing && this.store){
11531             this.store.afterEdit(this);
11532         }       
11533     },
11534
11535     /**
11536      * Get the value of the named field.
11537      * @param {String} name The name of the field to get the value of.
11538      * @return {Object} The value of the field.
11539      */
11540     get : function(name){
11541         return this.data[name]; 
11542     },
11543
11544     // private
11545     beginEdit : function(){
11546         this.editing = true;
11547         this.modified = {}; 
11548     },
11549
11550     // private
11551     cancelEdit : function(){
11552         this.editing = false;
11553         delete this.modified;
11554     },
11555
11556     // private
11557     endEdit : function(){
11558         this.editing = false;
11559         if(this.dirty && this.store){
11560             this.store.afterEdit(this);
11561         }
11562     },
11563
11564     /**
11565      * Usually called by the {@link Roo.data.Store} which owns the Record.
11566      * Rejects all changes made to the Record since either creation, or the last commit operation.
11567      * Modified fields are reverted to their original values.
11568      * <p>
11569      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11570      * of reject operations.
11571      */
11572     reject : function(){
11573         var m = this.modified;
11574         for(var n in m){
11575             if(typeof m[n] != "function"){
11576                 this.data[n] = m[n];
11577             }
11578         }
11579         this.dirty = false;
11580         delete this.modified;
11581         this.editing = false;
11582         if(this.store){
11583             this.store.afterReject(this);
11584         }
11585     },
11586
11587     /**
11588      * Usually called by the {@link Roo.data.Store} which owns the Record.
11589      * Commits all changes made to the Record since either creation, or the last commit operation.
11590      * <p>
11591      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11592      * of commit operations.
11593      */
11594     commit : function(){
11595         this.dirty = false;
11596         delete this.modified;
11597         this.editing = false;
11598         if(this.store){
11599             this.store.afterCommit(this);
11600         }
11601     },
11602
11603     // private
11604     hasError : function(){
11605         return this.error != null;
11606     },
11607
11608     // private
11609     clearError : function(){
11610         this.error = null;
11611     },
11612
11613     /**
11614      * Creates a copy of this record.
11615      * @param {String} id (optional) A new record id if you don't want to use this record's id
11616      * @return {Record}
11617      */
11618     copy : function(newId) {
11619         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11620     }
11621 };/*
11622  * Based on:
11623  * Ext JS Library 1.1.1
11624  * Copyright(c) 2006-2007, Ext JS, LLC.
11625  *
11626  * Originally Released Under LGPL - original licence link has changed is not relivant.
11627  *
11628  * Fork - LGPL
11629  * <script type="text/javascript">
11630  */
11631
11632
11633
11634 /**
11635  * @class Roo.data.Store
11636  * @extends Roo.util.Observable
11637  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11638  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11639  * <p>
11640  * 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
11641  * has no knowledge of the format of the data returned by the Proxy.<br>
11642  * <p>
11643  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11644  * instances from the data object. These records are cached and made available through accessor functions.
11645  * @constructor
11646  * Creates a new Store.
11647  * @param {Object} config A config object containing the objects needed for the Store to access data,
11648  * and read the data into Records.
11649  */
11650 Roo.data.Store = function(config){
11651     this.data = new Roo.util.MixedCollection(false);
11652     this.data.getKey = function(o){
11653         return o.id;
11654     };
11655     this.baseParams = {};
11656     // private
11657     this.paramNames = {
11658         "start" : "start",
11659         "limit" : "limit",
11660         "sort" : "sort",
11661         "dir" : "dir",
11662         "multisort" : "_multisort"
11663     };
11664
11665     if(config && config.data){
11666         this.inlineData = config.data;
11667         delete config.data;
11668     }
11669
11670     Roo.apply(this, config);
11671     
11672     if(this.reader){ // reader passed
11673         this.reader = Roo.factory(this.reader, Roo.data);
11674         this.reader.xmodule = this.xmodule || false;
11675         if(!this.recordType){
11676             this.recordType = this.reader.recordType;
11677         }
11678         if(this.reader.onMetaChange){
11679             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11680         }
11681     }
11682
11683     if(this.recordType){
11684         this.fields = this.recordType.prototype.fields;
11685     }
11686     this.modified = [];
11687
11688     this.addEvents({
11689         /**
11690          * @event datachanged
11691          * Fires when the data cache has changed, and a widget which is using this Store
11692          * as a Record cache should refresh its view.
11693          * @param {Store} this
11694          */
11695         datachanged : true,
11696         /**
11697          * @event metachange
11698          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11699          * @param {Store} this
11700          * @param {Object} meta The JSON metadata
11701          */
11702         metachange : true,
11703         /**
11704          * @event add
11705          * Fires when Records have been added to the Store
11706          * @param {Store} this
11707          * @param {Roo.data.Record[]} records The array of Records added
11708          * @param {Number} index The index at which the record(s) were added
11709          */
11710         add : true,
11711         /**
11712          * @event remove
11713          * Fires when a Record has been removed from the Store
11714          * @param {Store} this
11715          * @param {Roo.data.Record} record The Record that was removed
11716          * @param {Number} index The index at which the record was removed
11717          */
11718         remove : true,
11719         /**
11720          * @event update
11721          * Fires when a Record has been updated
11722          * @param {Store} this
11723          * @param {Roo.data.Record} record The Record that was updated
11724          * @param {String} operation The update operation being performed.  Value may be one of:
11725          * <pre><code>
11726  Roo.data.Record.EDIT
11727  Roo.data.Record.REJECT
11728  Roo.data.Record.COMMIT
11729          * </code></pre>
11730          */
11731         update : true,
11732         /**
11733          * @event clear
11734          * Fires when the data cache has been cleared.
11735          * @param {Store} this
11736          */
11737         clear : true,
11738         /**
11739          * @event beforeload
11740          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11741          * the load action will be canceled.
11742          * @param {Store} this
11743          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11744          */
11745         beforeload : true,
11746         /**
11747          * @event beforeloadadd
11748          * Fires after a new set of Records has been loaded.
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          */
11753         beforeloadadd : true,
11754         /**
11755          * @event load
11756          * Fires after a new set of Records has been loaded, before they are added to the store.
11757          * @param {Store} this
11758          * @param {Roo.data.Record[]} records The Records that were loaded
11759          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11760          * @params {Object} return from reader
11761          */
11762         load : true,
11763         /**
11764          * @event loadexception
11765          * Fires if an exception occurs in the Proxy during loading.
11766          * Called with the signature of the Proxy's "loadexception" event.
11767          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11768          * 
11769          * @param {Proxy} 
11770          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11771          * @param {Object} load options 
11772          * @param {Object} jsonData from your request (normally this contains the Exception)
11773          */
11774         loadexception : true
11775     });
11776     
11777     if(this.proxy){
11778         this.proxy = Roo.factory(this.proxy, Roo.data);
11779         this.proxy.xmodule = this.xmodule || false;
11780         this.relayEvents(this.proxy,  ["loadexception"]);
11781     }
11782     this.sortToggle = {};
11783     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11784
11785     Roo.data.Store.superclass.constructor.call(this);
11786
11787     if(this.inlineData){
11788         this.loadData(this.inlineData);
11789         delete this.inlineData;
11790     }
11791 };
11792
11793 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11794      /**
11795     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11796     * without a remote query - used by combo/forms at present.
11797     */
11798     
11799     /**
11800     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11801     */
11802     /**
11803     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11804     */
11805     /**
11806     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11807     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11808     */
11809     /**
11810     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11811     * on any HTTP request
11812     */
11813     /**
11814     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11815     */
11816     /**
11817     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11818     */
11819     multiSort: false,
11820     /**
11821     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11822     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11823     */
11824     remoteSort : false,
11825
11826     /**
11827     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11828      * loaded or when a record is removed. (defaults to false).
11829     */
11830     pruneModifiedRecords : false,
11831
11832     // private
11833     lastOptions : null,
11834
11835     /**
11836      * Add Records to the Store and fires the add event.
11837      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11838      */
11839     add : function(records){
11840         records = [].concat(records);
11841         for(var i = 0, len = records.length; i < len; i++){
11842             records[i].join(this);
11843         }
11844         var index = this.data.length;
11845         this.data.addAll(records);
11846         this.fireEvent("add", this, records, index);
11847     },
11848
11849     /**
11850      * Remove a Record from the Store and fires the remove event.
11851      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11852      */
11853     remove : function(record){
11854         var index = this.data.indexOf(record);
11855         this.data.removeAt(index);
11856  
11857         if(this.pruneModifiedRecords){
11858             this.modified.remove(record);
11859         }
11860         this.fireEvent("remove", this, record, index);
11861     },
11862
11863     /**
11864      * Remove all Records from the Store and fires the clear event.
11865      */
11866     removeAll : function(){
11867         this.data.clear();
11868         if(this.pruneModifiedRecords){
11869             this.modified = [];
11870         }
11871         this.fireEvent("clear", this);
11872     },
11873
11874     /**
11875      * Inserts Records to the Store at the given index and fires the add event.
11876      * @param {Number} index The start index at which to insert the passed Records.
11877      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11878      */
11879     insert : function(index, records){
11880         records = [].concat(records);
11881         for(var i = 0, len = records.length; i < len; i++){
11882             this.data.insert(index, records[i]);
11883             records[i].join(this);
11884         }
11885         this.fireEvent("add", this, records, index);
11886     },
11887
11888     /**
11889      * Get the index within the cache of the passed Record.
11890      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11891      * @return {Number} The index of the passed Record. Returns -1 if not found.
11892      */
11893     indexOf : function(record){
11894         return this.data.indexOf(record);
11895     },
11896
11897     /**
11898      * Get the index within the cache of the Record with the passed id.
11899      * @param {String} id The id of the Record to find.
11900      * @return {Number} The index of the Record. Returns -1 if not found.
11901      */
11902     indexOfId : function(id){
11903         return this.data.indexOfKey(id);
11904     },
11905
11906     /**
11907      * Get the Record with the specified id.
11908      * @param {String} id The id of the Record to find.
11909      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11910      */
11911     getById : function(id){
11912         return this.data.key(id);
11913     },
11914
11915     /**
11916      * Get the Record at the specified index.
11917      * @param {Number} index The index of the Record to find.
11918      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11919      */
11920     getAt : function(index){
11921         return this.data.itemAt(index);
11922     },
11923
11924     /**
11925      * Returns a range of Records between specified indices.
11926      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11927      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11928      * @return {Roo.data.Record[]} An array of Records
11929      */
11930     getRange : function(start, end){
11931         return this.data.getRange(start, end);
11932     },
11933
11934     // private
11935     storeOptions : function(o){
11936         o = Roo.apply({}, o);
11937         delete o.callback;
11938         delete o.scope;
11939         this.lastOptions = o;
11940     },
11941
11942     /**
11943      * Loads the Record cache from the configured Proxy using the configured Reader.
11944      * <p>
11945      * If using remote paging, then the first load call must specify the <em>start</em>
11946      * and <em>limit</em> properties in the options.params property to establish the initial
11947      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11948      * <p>
11949      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11950      * and this call will return before the new data has been loaded. Perform any post-processing
11951      * in a callback function, or in a "load" event handler.</strong>
11952      * <p>
11953      * @param {Object} options An object containing properties which control loading options:<ul>
11954      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11955      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11956      * passed the following arguments:<ul>
11957      * <li>r : Roo.data.Record[]</li>
11958      * <li>options: Options object from the load call</li>
11959      * <li>success: Boolean success indicator</li></ul></li>
11960      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11961      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11962      * </ul>
11963      */
11964     load : function(options){
11965         options = options || {};
11966         if(this.fireEvent("beforeload", this, options) !== false){
11967             this.storeOptions(options);
11968             var p = Roo.apply(options.params || {}, this.baseParams);
11969             // if meta was not loaded from remote source.. try requesting it.
11970             if (!this.reader.metaFromRemote) {
11971                 p._requestMeta = 1;
11972             }
11973             if(this.sortInfo && this.remoteSort){
11974                 var pn = this.paramNames;
11975                 p[pn["sort"]] = this.sortInfo.field;
11976                 p[pn["dir"]] = this.sortInfo.direction;
11977             }
11978             if (this.multiSort) {
11979                 var pn = this.paramNames;
11980                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11981             }
11982             
11983             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11984         }
11985     },
11986
11987     /**
11988      * Reloads the Record cache from the configured Proxy using the configured Reader and
11989      * the options from the last load operation performed.
11990      * @param {Object} options (optional) An object containing properties which may override the options
11991      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11992      * the most recently used options are reused).
11993      */
11994     reload : function(options){
11995         this.load(Roo.applyIf(options||{}, this.lastOptions));
11996     },
11997
11998     // private
11999     // Called as a callback by the Reader during a load operation.
12000     loadRecords : function(o, options, success){
12001         if(!o || success === false){
12002             if(success !== false){
12003                 this.fireEvent("load", this, [], options, o);
12004             }
12005             if(options.callback){
12006                 options.callback.call(options.scope || this, [], options, false);
12007             }
12008             return;
12009         }
12010         // if data returned failure - throw an exception.
12011         if (o.success === false) {
12012             // show a message if no listener is registered.
12013             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
12014                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
12015             }
12016             // loadmask wil be hooked into this..
12017             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
12018             return;
12019         }
12020         var r = o.records, t = o.totalRecords || r.length;
12021         
12022         this.fireEvent("beforeloadadd", this, r, options, o);
12023         
12024         if(!options || options.add !== true){
12025             if(this.pruneModifiedRecords){
12026                 this.modified = [];
12027             }
12028             for(var i = 0, len = r.length; i < len; i++){
12029                 r[i].join(this);
12030             }
12031             if(this.snapshot){
12032                 this.data = this.snapshot;
12033                 delete this.snapshot;
12034             }
12035             this.data.clear();
12036             this.data.addAll(r);
12037             this.totalLength = t;
12038             this.applySort();
12039             this.fireEvent("datachanged", this);
12040         }else{
12041             this.totalLength = Math.max(t, this.data.length+r.length);
12042             this.add(r);
12043         }
12044         
12045         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
12046                 
12047             var e = new Roo.data.Record({});
12048
12049             e.set(this.parent.displayField, this.parent.emptyTitle);
12050             e.set(this.parent.valueField, '');
12051
12052             this.insert(0, e);
12053         }
12054             
12055         this.fireEvent("load", this, r, options, o);
12056         if(options.callback){
12057             options.callback.call(options.scope || this, r, options, true);
12058         }
12059     },
12060
12061
12062     /**
12063      * Loads data from a passed data block. A Reader which understands the format of the data
12064      * must have been configured in the constructor.
12065      * @param {Object} data The data block from which to read the Records.  The format of the data expected
12066      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
12067      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
12068      */
12069     loadData : function(o, append){
12070         var r = this.reader.readRecords(o);
12071         this.loadRecords(r, {add: append}, true);
12072     },
12073     
12074      /**
12075      * using 'cn' the nested child reader read the child array into it's child stores.
12076      * @param {Object} rec The record with a 'children array
12077      */
12078     loadDataFromChildren : function(rec)
12079     {
12080         this.loadData(this.reader.toLoadData(rec));
12081     },
12082     
12083
12084     /**
12085      * Gets the number of cached records.
12086      * <p>
12087      * <em>If using paging, this may not be the total size of the dataset. If the data object
12088      * used by the Reader contains the dataset size, then the getTotalCount() function returns
12089      * the data set size</em>
12090      */
12091     getCount : function(){
12092         return this.data.length || 0;
12093     },
12094
12095     /**
12096      * Gets the total number of records in the dataset as returned by the server.
12097      * <p>
12098      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
12099      * the dataset size</em>
12100      */
12101     getTotalCount : function(){
12102         return this.totalLength || 0;
12103     },
12104
12105     /**
12106      * Returns the sort state of the Store as an object with two properties:
12107      * <pre><code>
12108  field {String} The name of the field by which the Records are sorted
12109  direction {String} The sort order, "ASC" or "DESC"
12110      * </code></pre>
12111      */
12112     getSortState : function(){
12113         return this.sortInfo;
12114     },
12115
12116     // private
12117     applySort : function(){
12118         if(this.sortInfo && !this.remoteSort){
12119             var s = this.sortInfo, f = s.field;
12120             var st = this.fields.get(f).sortType;
12121             var fn = function(r1, r2){
12122                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
12123                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
12124             };
12125             this.data.sort(s.direction, fn);
12126             if(this.snapshot && this.snapshot != this.data){
12127                 this.snapshot.sort(s.direction, fn);
12128             }
12129         }
12130     },
12131
12132     /**
12133      * Sets the default sort column and order to be used by the next load operation.
12134      * @param {String} fieldName The name of the field to sort by.
12135      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12136      */
12137     setDefaultSort : function(field, dir){
12138         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
12139     },
12140
12141     /**
12142      * Sort the Records.
12143      * If remote sorting is used, the sort is performed on the server, and the cache is
12144      * reloaded. If local sorting is used, the cache is sorted internally.
12145      * @param {String} fieldName The name of the field to sort by.
12146      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12147      */
12148     sort : function(fieldName, dir){
12149         var f = this.fields.get(fieldName);
12150         if(!dir){
12151             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
12152             
12153             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
12154                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
12155             }else{
12156                 dir = f.sortDir;
12157             }
12158         }
12159         this.sortToggle[f.name] = dir;
12160         this.sortInfo = {field: f.name, direction: dir};
12161         if(!this.remoteSort){
12162             this.applySort();
12163             this.fireEvent("datachanged", this);
12164         }else{
12165             this.load(this.lastOptions);
12166         }
12167     },
12168
12169     /**
12170      * Calls the specified function for each of the Records in the cache.
12171      * @param {Function} fn The function to call. The Record is passed as the first parameter.
12172      * Returning <em>false</em> aborts and exits the iteration.
12173      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
12174      */
12175     each : function(fn, scope){
12176         this.data.each(fn, scope);
12177     },
12178
12179     /**
12180      * Gets all records modified since the last commit.  Modified records are persisted across load operations
12181      * (e.g., during paging).
12182      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
12183      */
12184     getModifiedRecords : function(){
12185         return this.modified;
12186     },
12187
12188     // private
12189     createFilterFn : function(property, value, anyMatch){
12190         if(!value.exec){ // not a regex
12191             value = String(value);
12192             if(value.length == 0){
12193                 return false;
12194             }
12195             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
12196         }
12197         return function(r){
12198             return value.test(r.data[property]);
12199         };
12200     },
12201
12202     /**
12203      * Sums the value of <i>property</i> for each record between start and end and returns the result.
12204      * @param {String} property A field on your records
12205      * @param {Number} start The record index to start at (defaults to 0)
12206      * @param {Number} end The last record index to include (defaults to length - 1)
12207      * @return {Number} The sum
12208      */
12209     sum : function(property, start, end){
12210         var rs = this.data.items, v = 0;
12211         start = start || 0;
12212         end = (end || end === 0) ? end : rs.length-1;
12213
12214         for(var i = start; i <= end; i++){
12215             v += (rs[i].data[property] || 0);
12216         }
12217         return v;
12218     },
12219
12220     /**
12221      * Filter the records by a specified property.
12222      * @param {String} field A field on your records
12223      * @param {String/RegExp} value Either a string that the field
12224      * should start with or a RegExp to test against the field
12225      * @param {Boolean} anyMatch True to match any part not just the beginning
12226      */
12227     filter : function(property, value, anyMatch){
12228         var fn = this.createFilterFn(property, value, anyMatch);
12229         return fn ? this.filterBy(fn) : this.clearFilter();
12230     },
12231
12232     /**
12233      * Filter by a function. The specified function will be called with each
12234      * record in this data source. If the function returns true the record is included,
12235      * otherwise it is filtered.
12236      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12237      * @param {Object} scope (optional) The scope of the function (defaults to this)
12238      */
12239     filterBy : function(fn, scope){
12240         this.snapshot = this.snapshot || this.data;
12241         this.data = this.queryBy(fn, scope||this);
12242         this.fireEvent("datachanged", this);
12243     },
12244
12245     /**
12246      * Query the records by a specified property.
12247      * @param {String} field A field on your records
12248      * @param {String/RegExp} value Either a string that the field
12249      * should start with or a RegExp to test against the field
12250      * @param {Boolean} anyMatch True to match any part not just the beginning
12251      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12252      */
12253     query : function(property, value, anyMatch){
12254         var fn = this.createFilterFn(property, value, anyMatch);
12255         return fn ? this.queryBy(fn) : this.data.clone();
12256     },
12257
12258     /**
12259      * Query by a function. The specified function will be called with each
12260      * record in this data source. If the function returns true the record is included
12261      * in the results.
12262      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12263      * @param {Object} scope (optional) The scope of the function (defaults to this)
12264       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12265      **/
12266     queryBy : function(fn, scope){
12267         var data = this.snapshot || this.data;
12268         return data.filterBy(fn, scope||this);
12269     },
12270
12271     /**
12272      * Collects unique values for a particular dataIndex from this store.
12273      * @param {String} dataIndex The property to collect
12274      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12275      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12276      * @return {Array} An array of the unique values
12277      **/
12278     collect : function(dataIndex, allowNull, bypassFilter){
12279         var d = (bypassFilter === true && this.snapshot) ?
12280                 this.snapshot.items : this.data.items;
12281         var v, sv, r = [], l = {};
12282         for(var i = 0, len = d.length; i < len; i++){
12283             v = d[i].data[dataIndex];
12284             sv = String(v);
12285             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12286                 l[sv] = true;
12287                 r[r.length] = v;
12288             }
12289         }
12290         return r;
12291     },
12292
12293     /**
12294      * Revert to a view of the Record cache with no filtering applied.
12295      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12296      */
12297     clearFilter : function(suppressEvent){
12298         if(this.snapshot && this.snapshot != this.data){
12299             this.data = this.snapshot;
12300             delete this.snapshot;
12301             if(suppressEvent !== true){
12302                 this.fireEvent("datachanged", this);
12303             }
12304         }
12305     },
12306
12307     // private
12308     afterEdit : function(record){
12309         if(this.modified.indexOf(record) == -1){
12310             this.modified.push(record);
12311         }
12312         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12313     },
12314     
12315     // private
12316     afterReject : function(record){
12317         this.modified.remove(record);
12318         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12319     },
12320
12321     // private
12322     afterCommit : function(record){
12323         this.modified.remove(record);
12324         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12325     },
12326
12327     /**
12328      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12329      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12330      */
12331     commitChanges : function(){
12332         var m = this.modified.slice(0);
12333         this.modified = [];
12334         for(var i = 0, len = m.length; i < len; i++){
12335             m[i].commit();
12336         }
12337     },
12338
12339     /**
12340      * Cancel outstanding changes on all changed records.
12341      */
12342     rejectChanges : function(){
12343         var m = this.modified.slice(0);
12344         this.modified = [];
12345         for(var i = 0, len = m.length; i < len; i++){
12346             m[i].reject();
12347         }
12348     },
12349
12350     onMetaChange : function(meta, rtype, o){
12351         this.recordType = rtype;
12352         this.fields = rtype.prototype.fields;
12353         delete this.snapshot;
12354         this.sortInfo = meta.sortInfo || this.sortInfo;
12355         this.modified = [];
12356         this.fireEvent('metachange', this, this.reader.meta);
12357     },
12358     
12359     moveIndex : function(data, type)
12360     {
12361         var index = this.indexOf(data);
12362         
12363         var newIndex = index + type;
12364         
12365         this.remove(data);
12366         
12367         this.insert(newIndex, data);
12368         
12369     }
12370 });/*
12371  * Based on:
12372  * Ext JS Library 1.1.1
12373  * Copyright(c) 2006-2007, Ext JS, LLC.
12374  *
12375  * Originally Released Under LGPL - original licence link has changed is not relivant.
12376  *
12377  * Fork - LGPL
12378  * <script type="text/javascript">
12379  */
12380
12381 /**
12382  * @class Roo.data.SimpleStore
12383  * @extends Roo.data.Store
12384  * Small helper class to make creating Stores from Array data easier.
12385  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12386  * @cfg {Array} fields An array of field definition objects, or field name strings.
12387  * @cfg {Object} an existing reader (eg. copied from another store)
12388  * @cfg {Array} data The multi-dimensional array of data
12389  * @constructor
12390  * @param {Object} config
12391  */
12392 Roo.data.SimpleStore = function(config)
12393 {
12394     Roo.data.SimpleStore.superclass.constructor.call(this, {
12395         isLocal : true,
12396         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
12397                 id: config.id
12398             },
12399             Roo.data.Record.create(config.fields)
12400         ),
12401         proxy : new Roo.data.MemoryProxy(config.data)
12402     });
12403     this.load();
12404 };
12405 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12406  * Based on:
12407  * Ext JS Library 1.1.1
12408  * Copyright(c) 2006-2007, Ext JS, LLC.
12409  *
12410  * Originally Released Under LGPL - original licence link has changed is not relivant.
12411  *
12412  * Fork - LGPL
12413  * <script type="text/javascript">
12414  */
12415
12416 /**
12417 /**
12418  * @extends Roo.data.Store
12419  * @class Roo.data.JsonStore
12420  * Small helper class to make creating Stores for JSON data easier. <br/>
12421 <pre><code>
12422 var store = new Roo.data.JsonStore({
12423     url: 'get-images.php',
12424     root: 'images',
12425     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12426 });
12427 </code></pre>
12428  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12429  * JsonReader and HttpProxy (unless inline data is provided).</b>
12430  * @cfg {Array} fields An array of field definition objects, or field name strings.
12431  * @constructor
12432  * @param {Object} config
12433  */
12434 Roo.data.JsonStore = function(c){
12435     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12436         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12437         reader: new Roo.data.JsonReader(c, c.fields)
12438     }));
12439 };
12440 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12441  * Based on:
12442  * Ext JS Library 1.1.1
12443  * Copyright(c) 2006-2007, Ext JS, LLC.
12444  *
12445  * Originally Released Under LGPL - original licence link has changed is not relivant.
12446  *
12447  * Fork - LGPL
12448  * <script type="text/javascript">
12449  */
12450
12451  
12452 Roo.data.Field = function(config){
12453     if(typeof config == "string"){
12454         config = {name: config};
12455     }
12456     Roo.apply(this, config);
12457     
12458     if(!this.type){
12459         this.type = "auto";
12460     }
12461     
12462     var st = Roo.data.SortTypes;
12463     // named sortTypes are supported, here we look them up
12464     if(typeof this.sortType == "string"){
12465         this.sortType = st[this.sortType];
12466     }
12467     
12468     // set default sortType for strings and dates
12469     if(!this.sortType){
12470         switch(this.type){
12471             case "string":
12472                 this.sortType = st.asUCString;
12473                 break;
12474             case "date":
12475                 this.sortType = st.asDate;
12476                 break;
12477             default:
12478                 this.sortType = st.none;
12479         }
12480     }
12481
12482     // define once
12483     var stripRe = /[\$,%]/g;
12484
12485     // prebuilt conversion function for this field, instead of
12486     // switching every time we're reading a value
12487     if(!this.convert){
12488         var cv, dateFormat = this.dateFormat;
12489         switch(this.type){
12490             case "":
12491             case "auto":
12492             case undefined:
12493                 cv = function(v){ return v; };
12494                 break;
12495             case "string":
12496                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12497                 break;
12498             case "int":
12499                 cv = function(v){
12500                     return v !== undefined && v !== null && v !== '' ?
12501                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12502                     };
12503                 break;
12504             case "float":
12505                 cv = function(v){
12506                     return v !== undefined && v !== null && v !== '' ?
12507                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12508                     };
12509                 break;
12510             case "bool":
12511             case "boolean":
12512                 cv = function(v){ return v === true || v === "true" || v == 1; };
12513                 break;
12514             case "date":
12515                 cv = function(v){
12516                     if(!v){
12517                         return '';
12518                     }
12519                     if(v instanceof Date){
12520                         return v;
12521                     }
12522                     if(dateFormat){
12523                         if(dateFormat == "timestamp"){
12524                             return new Date(v*1000);
12525                         }
12526                         return Date.parseDate(v, dateFormat);
12527                     }
12528                     var parsed = Date.parse(v);
12529                     return parsed ? new Date(parsed) : null;
12530                 };
12531              break;
12532             
12533         }
12534         this.convert = cv;
12535     }
12536 };
12537
12538 Roo.data.Field.prototype = {
12539     dateFormat: null,
12540     defaultValue: "",
12541     mapping: null,
12542     sortType : null,
12543     sortDir : "ASC"
12544 };/*
12545  * Based on:
12546  * Ext JS Library 1.1.1
12547  * Copyright(c) 2006-2007, Ext JS, LLC.
12548  *
12549  * Originally Released Under LGPL - original licence link has changed is not relivant.
12550  *
12551  * Fork - LGPL
12552  * <script type="text/javascript">
12553  */
12554  
12555 // Base class for reading structured data from a data source.  This class is intended to be
12556 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12557
12558 /**
12559  * @class Roo.data.DataReader
12560  * Base class for reading structured data from a data source.  This class is intended to be
12561  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12562  */
12563
12564 Roo.data.DataReader = function(meta, recordType){
12565     
12566     this.meta = meta;
12567     
12568     this.recordType = recordType instanceof Array ? 
12569         Roo.data.Record.create(recordType) : recordType;
12570 };
12571
12572 Roo.data.DataReader.prototype = {
12573     
12574     
12575     readerType : 'Data',
12576      /**
12577      * Create an empty record
12578      * @param {Object} data (optional) - overlay some values
12579      * @return {Roo.data.Record} record created.
12580      */
12581     newRow :  function(d) {
12582         var da =  {};
12583         this.recordType.prototype.fields.each(function(c) {
12584             switch( c.type) {
12585                 case 'int' : da[c.name] = 0; break;
12586                 case 'date' : da[c.name] = new Date(); break;
12587                 case 'float' : da[c.name] = 0.0; break;
12588                 case 'boolean' : da[c.name] = false; break;
12589                 default : da[c.name] = ""; break;
12590             }
12591             
12592         });
12593         return new this.recordType(Roo.apply(da, d));
12594     }
12595     
12596     
12597 };/*
12598  * Based on:
12599  * Ext JS Library 1.1.1
12600  * Copyright(c) 2006-2007, Ext JS, LLC.
12601  *
12602  * Originally Released Under LGPL - original licence link has changed is not relivant.
12603  *
12604  * Fork - LGPL
12605  * <script type="text/javascript">
12606  */
12607
12608 /**
12609  * @class Roo.data.DataProxy
12610  * @extends Roo.data.Observable
12611  * This class is an abstract base class for implementations which provide retrieval of
12612  * unformatted data objects.<br>
12613  * <p>
12614  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12615  * (of the appropriate type which knows how to parse the data object) to provide a block of
12616  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12617  * <p>
12618  * Custom implementations must implement the load method as described in
12619  * {@link Roo.data.HttpProxy#load}.
12620  */
12621 Roo.data.DataProxy = function(){
12622     this.addEvents({
12623         /**
12624          * @event beforeload
12625          * Fires before a network request is made to retrieve a data object.
12626          * @param {Object} This DataProxy object.
12627          * @param {Object} params The params parameter to the load function.
12628          */
12629         beforeload : true,
12630         /**
12631          * @event load
12632          * Fires before the load method's callback is called.
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          */
12637         load : true,
12638         /**
12639          * @event loadexception
12640          * Fires if an Exception occurs during data retrieval.
12641          * @param {Object} This DataProxy object.
12642          * @param {Object} o The data object.
12643          * @param {Object} arg The callback argument object passed to the load function.
12644          * @param {Object} e The Exception.
12645          */
12646         loadexception : true
12647     });
12648     Roo.data.DataProxy.superclass.constructor.call(this);
12649 };
12650
12651 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12652
12653     /**
12654      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12655      */
12656 /*
12657  * Based on:
12658  * Ext JS Library 1.1.1
12659  * Copyright(c) 2006-2007, Ext JS, LLC.
12660  *
12661  * Originally Released Under LGPL - original licence link has changed is not relivant.
12662  *
12663  * Fork - LGPL
12664  * <script type="text/javascript">
12665  */
12666 /**
12667  * @class Roo.data.MemoryProxy
12668  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12669  * to the Reader when its load method is called.
12670  * @constructor
12671  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12672  */
12673 Roo.data.MemoryProxy = function(data){
12674     if (data.data) {
12675         data = data.data;
12676     }
12677     Roo.data.MemoryProxy.superclass.constructor.call(this);
12678     this.data = data;
12679 };
12680
12681 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12682     
12683     /**
12684      * Load data from the requested source (in this case an in-memory
12685      * data object passed to the constructor), read the data object into
12686      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12687      * process that block using the passed callback.
12688      * @param {Object} params This parameter is not used by the MemoryProxy class.
12689      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12690      * object into a block of Roo.data.Records.
12691      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12692      * The function must be passed <ul>
12693      * <li>The Record block object</li>
12694      * <li>The "arg" argument from the load function</li>
12695      * <li>A boolean success indicator</li>
12696      * </ul>
12697      * @param {Object} scope The scope in which to call the callback
12698      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12699      */
12700     load : function(params, reader, callback, scope, arg){
12701         params = params || {};
12702         var result;
12703         try {
12704             result = reader.readRecords(params.data ? params.data :this.data);
12705         }catch(e){
12706             this.fireEvent("loadexception", this, arg, null, e);
12707             callback.call(scope, null, arg, false);
12708             return;
12709         }
12710         callback.call(scope, result, arg, true);
12711     },
12712     
12713     // private
12714     update : function(params, records){
12715         
12716     }
12717 });/*
12718  * Based on:
12719  * Ext JS Library 1.1.1
12720  * Copyright(c) 2006-2007, Ext JS, LLC.
12721  *
12722  * Originally Released Under LGPL - original licence link has changed is not relivant.
12723  *
12724  * Fork - LGPL
12725  * <script type="text/javascript">
12726  */
12727 /**
12728  * @class Roo.data.HttpProxy
12729  * @extends Roo.data.DataProxy
12730  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12731  * configured to reference a certain URL.<br><br>
12732  * <p>
12733  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12734  * from which the running page was served.<br><br>
12735  * <p>
12736  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12737  * <p>
12738  * Be aware that to enable the browser to parse an XML document, the server must set
12739  * the Content-Type header in the HTTP response to "text/xml".
12740  * @constructor
12741  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12742  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12743  * will be used to make the request.
12744  */
12745 Roo.data.HttpProxy = function(conn){
12746     Roo.data.HttpProxy.superclass.constructor.call(this);
12747     // is conn a conn config or a real conn?
12748     this.conn = conn;
12749     this.useAjax = !conn || !conn.events;
12750   
12751 };
12752
12753 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12754     // thse are take from connection...
12755     
12756     /**
12757      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12758      */
12759     /**
12760      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12761      * extra parameters to each request made by this object. (defaults to undefined)
12762      */
12763     /**
12764      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12765      *  to each request made by this object. (defaults to undefined)
12766      */
12767     /**
12768      * @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)
12769      */
12770     /**
12771      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12772      */
12773      /**
12774      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12775      * @type Boolean
12776      */
12777   
12778
12779     /**
12780      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12781      * @type Boolean
12782      */
12783     /**
12784      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12785      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12786      * a finer-grained basis than the DataProxy events.
12787      */
12788     getConnection : function(){
12789         return this.useAjax ? Roo.Ajax : this.conn;
12790     },
12791
12792     /**
12793      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12794      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12795      * process that block using the passed callback.
12796      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12797      * for the request to the remote server.
12798      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12799      * object into a block of Roo.data.Records.
12800      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12801      * The function must be passed <ul>
12802      * <li>The Record block object</li>
12803      * <li>The "arg" argument from the load function</li>
12804      * <li>A boolean success indicator</li>
12805      * </ul>
12806      * @param {Object} scope The scope in which to call the callback
12807      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12808      */
12809     load : function(params, reader, callback, scope, arg){
12810         if(this.fireEvent("beforeload", this, params) !== false){
12811             var  o = {
12812                 params : params || {},
12813                 request: {
12814                     callback : callback,
12815                     scope : scope,
12816                     arg : arg
12817                 },
12818                 reader: reader,
12819                 callback : this.loadResponse,
12820                 scope: this
12821             };
12822             if(this.useAjax){
12823                 Roo.applyIf(o, this.conn);
12824                 if(this.activeRequest){
12825                     Roo.Ajax.abort(this.activeRequest);
12826                 }
12827                 this.activeRequest = Roo.Ajax.request(o);
12828             }else{
12829                 this.conn.request(o);
12830             }
12831         }else{
12832             callback.call(scope||this, null, arg, false);
12833         }
12834     },
12835
12836     // private
12837     loadResponse : function(o, success, response){
12838         delete this.activeRequest;
12839         if(!success){
12840             this.fireEvent("loadexception", this, o, response);
12841             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12842             return;
12843         }
12844         var result;
12845         try {
12846             result = o.reader.read(response);
12847         }catch(e){
12848             this.fireEvent("loadexception", this, o, response, e);
12849             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12850             return;
12851         }
12852         
12853         this.fireEvent("load", this, o, o.request.arg);
12854         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12855     },
12856
12857     // private
12858     update : function(dataSet){
12859
12860     },
12861
12862     // private
12863     updateResponse : function(dataSet){
12864
12865     }
12866 });/*
12867  * Based on:
12868  * Ext JS Library 1.1.1
12869  * Copyright(c) 2006-2007, Ext JS, LLC.
12870  *
12871  * Originally Released Under LGPL - original licence link has changed is not relivant.
12872  *
12873  * Fork - LGPL
12874  * <script type="text/javascript">
12875  */
12876
12877 /**
12878  * @class Roo.data.ScriptTagProxy
12879  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12880  * other than the originating domain of the running page.<br><br>
12881  * <p>
12882  * <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
12883  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12884  * <p>
12885  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12886  * source code that is used as the source inside a &lt;script> tag.<br><br>
12887  * <p>
12888  * In order for the browser to process the returned data, the server must wrap the data object
12889  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12890  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12891  * depending on whether the callback name was passed:
12892  * <p>
12893  * <pre><code>
12894 boolean scriptTag = false;
12895 String cb = request.getParameter("callback");
12896 if (cb != null) {
12897     scriptTag = true;
12898     response.setContentType("text/javascript");
12899 } else {
12900     response.setContentType("application/x-json");
12901 }
12902 Writer out = response.getWriter();
12903 if (scriptTag) {
12904     out.write(cb + "(");
12905 }
12906 out.print(dataBlock.toJsonString());
12907 if (scriptTag) {
12908     out.write(");");
12909 }
12910 </pre></code>
12911  *
12912  * @constructor
12913  * @param {Object} config A configuration object.
12914  */
12915 Roo.data.ScriptTagProxy = function(config){
12916     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12917     Roo.apply(this, config);
12918     this.head = document.getElementsByTagName("head")[0];
12919 };
12920
12921 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12922
12923 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12924     /**
12925      * @cfg {String} url The URL from which to request the data object.
12926      */
12927     /**
12928      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12929      */
12930     timeout : 30000,
12931     /**
12932      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12933      * the server the name of the callback function set up by the load call to process the returned data object.
12934      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12935      * javascript output which calls this named function passing the data object as its only parameter.
12936      */
12937     callbackParam : "callback",
12938     /**
12939      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12940      * name to the request.
12941      */
12942     nocache : true,
12943
12944     /**
12945      * Load data from the configured URL, read the data object into
12946      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12947      * process that block using the passed callback.
12948      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12949      * for the request to the remote server.
12950      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12951      * object into a block of Roo.data.Records.
12952      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12953      * The function must be passed <ul>
12954      * <li>The Record block object</li>
12955      * <li>The "arg" argument from the load function</li>
12956      * <li>A boolean success indicator</li>
12957      * </ul>
12958      * @param {Object} scope The scope in which to call the callback
12959      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12960      */
12961     load : function(params, reader, callback, scope, arg){
12962         if(this.fireEvent("beforeload", this, params) !== false){
12963
12964             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12965
12966             var url = this.url;
12967             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12968             if(this.nocache){
12969                 url += "&_dc=" + (new Date().getTime());
12970             }
12971             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12972             var trans = {
12973                 id : transId,
12974                 cb : "stcCallback"+transId,
12975                 scriptId : "stcScript"+transId,
12976                 params : params,
12977                 arg : arg,
12978                 url : url,
12979                 callback : callback,
12980                 scope : scope,
12981                 reader : reader
12982             };
12983             var conn = this;
12984
12985             window[trans.cb] = function(o){
12986                 conn.handleResponse(o, trans);
12987             };
12988
12989             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12990
12991             if(this.autoAbort !== false){
12992                 this.abort();
12993             }
12994
12995             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12996
12997             var script = document.createElement("script");
12998             script.setAttribute("src", url);
12999             script.setAttribute("type", "text/javascript");
13000             script.setAttribute("id", trans.scriptId);
13001             this.head.appendChild(script);
13002
13003             this.trans = trans;
13004         }else{
13005             callback.call(scope||this, null, arg, false);
13006         }
13007     },
13008
13009     // private
13010     isLoading : function(){
13011         return this.trans ? true : false;
13012     },
13013
13014     /**
13015      * Abort the current server request.
13016      */
13017     abort : function(){
13018         if(this.isLoading()){
13019             this.destroyTrans(this.trans);
13020         }
13021     },
13022
13023     // private
13024     destroyTrans : function(trans, isLoaded){
13025         this.head.removeChild(document.getElementById(trans.scriptId));
13026         clearTimeout(trans.timeoutId);
13027         if(isLoaded){
13028             window[trans.cb] = undefined;
13029             try{
13030                 delete window[trans.cb];
13031             }catch(e){}
13032         }else{
13033             // if hasn't been loaded, wait for load to remove it to prevent script error
13034             window[trans.cb] = function(){
13035                 window[trans.cb] = undefined;
13036                 try{
13037                     delete window[trans.cb];
13038                 }catch(e){}
13039             };
13040         }
13041     },
13042
13043     // private
13044     handleResponse : function(o, trans){
13045         this.trans = false;
13046         this.destroyTrans(trans, true);
13047         var result;
13048         try {
13049             result = trans.reader.readRecords(o);
13050         }catch(e){
13051             this.fireEvent("loadexception", this, o, trans.arg, e);
13052             trans.callback.call(trans.scope||window, null, trans.arg, false);
13053             return;
13054         }
13055         this.fireEvent("load", this, o, trans.arg);
13056         trans.callback.call(trans.scope||window, result, trans.arg, true);
13057     },
13058
13059     // private
13060     handleFailure : function(trans){
13061         this.trans = false;
13062         this.destroyTrans(trans, false);
13063         this.fireEvent("loadexception", this, null, trans.arg);
13064         trans.callback.call(trans.scope||window, null, trans.arg, false);
13065     }
13066 });/*
13067  * Based on:
13068  * Ext JS Library 1.1.1
13069  * Copyright(c) 2006-2007, Ext JS, LLC.
13070  *
13071  * Originally Released Under LGPL - original licence link has changed is not relivant.
13072  *
13073  * Fork - LGPL
13074  * <script type="text/javascript">
13075  */
13076
13077 /**
13078  * @class Roo.data.JsonReader
13079  * @extends Roo.data.DataReader
13080  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
13081  * based on mappings in a provided Roo.data.Record constructor.
13082  * 
13083  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
13084  * in the reply previously. 
13085  * 
13086  * <p>
13087  * Example code:
13088  * <pre><code>
13089 var RecordDef = Roo.data.Record.create([
13090     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
13091     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
13092 ]);
13093 var myReader = new Roo.data.JsonReader({
13094     totalProperty: "results",    // The property which contains the total dataset size (optional)
13095     root: "rows",                // The property which contains an Array of row objects
13096     id: "id"                     // The property within each row object that provides an ID for the record (optional)
13097 }, RecordDef);
13098 </code></pre>
13099  * <p>
13100  * This would consume a JSON file like this:
13101  * <pre><code>
13102 { 'results': 2, 'rows': [
13103     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
13104     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
13105 }
13106 </code></pre>
13107  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
13108  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
13109  * paged from the remote server.
13110  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
13111  * @cfg {String} root name of the property which contains the Array of row objects.
13112  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13113  * @cfg {Array} fields Array of field definition objects
13114  * @constructor
13115  * Create a new JsonReader
13116  * @param {Object} meta Metadata configuration options
13117  * @param {Object} recordType Either an Array of field definition objects,
13118  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
13119  */
13120 Roo.data.JsonReader = function(meta, recordType){
13121     
13122     meta = meta || {};
13123     // set some defaults:
13124     Roo.applyIf(meta, {
13125         totalProperty: 'total',
13126         successProperty : 'success',
13127         root : 'data',
13128         id : 'id'
13129     });
13130     
13131     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13132 };
13133 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
13134     
13135     readerType : 'Json',
13136     
13137     /**
13138      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
13139      * Used by Store query builder to append _requestMeta to params.
13140      * 
13141      */
13142     metaFromRemote : false,
13143     /**
13144      * This method is only used by a DataProxy which has retrieved data from a remote server.
13145      * @param {Object} response The XHR object which contains the JSON data in its responseText.
13146      * @return {Object} data A data block which is used by an Roo.data.Store object as
13147      * a cache of Roo.data.Records.
13148      */
13149     read : function(response){
13150         var json = response.responseText;
13151        
13152         var o = /* eval:var:o */ eval("("+json+")");
13153         if(!o) {
13154             throw {message: "JsonReader.read: Json object not found"};
13155         }
13156         
13157         if(o.metaData){
13158             
13159             delete this.ef;
13160             this.metaFromRemote = true;
13161             this.meta = o.metaData;
13162             this.recordType = Roo.data.Record.create(o.metaData.fields);
13163             this.onMetaChange(this.meta, this.recordType, o);
13164         }
13165         return this.readRecords(o);
13166     },
13167
13168     // private function a store will implement
13169     onMetaChange : function(meta, recordType, o){
13170
13171     },
13172
13173     /**
13174          * @ignore
13175          */
13176     simpleAccess: function(obj, subsc) {
13177         return obj[subsc];
13178     },
13179
13180         /**
13181          * @ignore
13182          */
13183     getJsonAccessor: function(){
13184         var re = /[\[\.]/;
13185         return function(expr) {
13186             try {
13187                 return(re.test(expr))
13188                     ? new Function("obj", "return obj." + expr)
13189                     : function(obj){
13190                         return obj[expr];
13191                     };
13192             } catch(e){}
13193             return Roo.emptyFn;
13194         };
13195     }(),
13196
13197     /**
13198      * Create a data block containing Roo.data.Records from an XML document.
13199      * @param {Object} o An object which contains an Array of row objects in the property specified
13200      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
13201      * which contains the total size of the dataset.
13202      * @return {Object} data A data block which is used by an Roo.data.Store object as
13203      * a cache of Roo.data.Records.
13204      */
13205     readRecords : function(o){
13206         /**
13207          * After any data loads, the raw JSON data is available for further custom processing.
13208          * @type Object
13209          */
13210         this.o = o;
13211         var s = this.meta, Record = this.recordType,
13212             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
13213
13214 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
13215         if (!this.ef) {
13216             if(s.totalProperty) {
13217                     this.getTotal = this.getJsonAccessor(s.totalProperty);
13218                 }
13219                 if(s.successProperty) {
13220                     this.getSuccess = this.getJsonAccessor(s.successProperty);
13221                 }
13222                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
13223                 if (s.id) {
13224                         var g = this.getJsonAccessor(s.id);
13225                         this.getId = function(rec) {
13226                                 var r = g(rec);  
13227                                 return (r === undefined || r === "") ? null : r;
13228                         };
13229                 } else {
13230                         this.getId = function(){return null;};
13231                 }
13232             this.ef = [];
13233             for(var jj = 0; jj < fl; jj++){
13234                 f = fi[jj];
13235                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
13236                 this.ef[jj] = this.getJsonAccessor(map);
13237             }
13238         }
13239
13240         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
13241         if(s.totalProperty){
13242             var vt = parseInt(this.getTotal(o), 10);
13243             if(!isNaN(vt)){
13244                 totalRecords = vt;
13245             }
13246         }
13247         if(s.successProperty){
13248             var vs = this.getSuccess(o);
13249             if(vs === false || vs === 'false'){
13250                 success = false;
13251             }
13252         }
13253         var records = [];
13254         for(var i = 0; i < c; i++){
13255                 var n = root[i];
13256             var values = {};
13257             var id = this.getId(n);
13258             for(var j = 0; j < fl; j++){
13259                 f = fi[j];
13260             var v = this.ef[j](n);
13261             if (!f.convert) {
13262                 Roo.log('missing convert for ' + f.name);
13263                 Roo.log(f);
13264                 continue;
13265             }
13266             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
13267             }
13268             var record = new Record(values, id);
13269             record.json = n;
13270             records[i] = record;
13271         }
13272         return {
13273             raw : o,
13274             success : success,
13275             records : records,
13276             totalRecords : totalRecords
13277         };
13278     },
13279     // used when loading children.. @see loadDataFromChildren
13280     toLoadData: function(rec)
13281     {
13282         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13283         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13284         return { data : data, total : data.length };
13285         
13286     }
13287 });/*
13288  * Based on:
13289  * Ext JS Library 1.1.1
13290  * Copyright(c) 2006-2007, Ext JS, LLC.
13291  *
13292  * Originally Released Under LGPL - original licence link has changed is not relivant.
13293  *
13294  * Fork - LGPL
13295  * <script type="text/javascript">
13296  */
13297
13298 /**
13299  * @class Roo.data.ArrayReader
13300  * @extends Roo.data.DataReader
13301  * Data reader class to create an Array of Roo.data.Record objects from an Array.
13302  * Each element of that Array represents a row of data fields. The
13303  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13304  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13305  * <p>
13306  * Example code:.
13307  * <pre><code>
13308 var RecordDef = Roo.data.Record.create([
13309     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
13310     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
13311 ]);
13312 var myReader = new Roo.data.ArrayReader({
13313     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
13314 }, RecordDef);
13315 </code></pre>
13316  * <p>
13317  * This would consume an Array like this:
13318  * <pre><code>
13319 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13320   </code></pre>
13321  
13322  * @constructor
13323  * Create a new JsonReader
13324  * @param {Object} meta Metadata configuration options.
13325  * @param {Object|Array} recordType Either an Array of field definition objects
13326  * 
13327  * @cfg {Array} fields Array of field definition objects
13328  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13329  * as specified to {@link Roo.data.Record#create},
13330  * or an {@link Roo.data.Record} object
13331  *
13332  * 
13333  * created using {@link Roo.data.Record#create}.
13334  */
13335 Roo.data.ArrayReader = function(meta, recordType)
13336 {    
13337     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13338 };
13339
13340 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13341     
13342       /**
13343      * Create a data block containing Roo.data.Records from an XML document.
13344      * @param {Object} o An Array of row objects which represents the dataset.
13345      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13346      * a cache of Roo.data.Records.
13347      */
13348     readRecords : function(o)
13349     {
13350         var sid = this.meta ? this.meta.id : null;
13351         var recordType = this.recordType, fields = recordType.prototype.fields;
13352         var records = [];
13353         var root = o;
13354         for(var i = 0; i < root.length; i++){
13355                 var n = root[i];
13356             var values = {};
13357             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13358             for(var j = 0, jlen = fields.length; j < jlen; j++){
13359                 var f = fields.items[j];
13360                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13361                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13362                 v = f.convert(v);
13363                 values[f.name] = v;
13364             }
13365             var record = new recordType(values, id);
13366             record.json = n;
13367             records[records.length] = record;
13368         }
13369         return {
13370             records : records,
13371             totalRecords : records.length
13372         };
13373     },
13374     // used when loading children.. @see loadDataFromChildren
13375     toLoadData: function(rec)
13376     {
13377         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13378         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13379         
13380     }
13381     
13382     
13383 });/*
13384  * - LGPL
13385  * * 
13386  */
13387
13388 /**
13389  * @class Roo.bootstrap.ComboBox
13390  * @extends Roo.bootstrap.TriggerField
13391  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13392  * @cfg {Boolean} append (true|false) default false
13393  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13394  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13395  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13396  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13397  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13398  * @cfg {Boolean} animate default true
13399  * @cfg {Boolean} emptyResultText only for touch device
13400  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13401  * @cfg {String} emptyTitle default ''
13402  * @constructor
13403  * Create a new ComboBox.
13404  * @param {Object} config Configuration options
13405  */
13406 Roo.bootstrap.ComboBox = function(config){
13407     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13408     this.addEvents({
13409         /**
13410          * @event expand
13411          * Fires when the dropdown list is expanded
13412         * @param {Roo.bootstrap.ComboBox} combo This combo box
13413         */
13414         'expand' : true,
13415         /**
13416          * @event collapse
13417          * Fires when the dropdown list is collapsed
13418         * @param {Roo.bootstrap.ComboBox} combo This combo box
13419         */
13420         'collapse' : true,
13421         /**
13422          * @event beforeselect
13423          * Fires before a list item is selected. Return false to cancel the selection.
13424         * @param {Roo.bootstrap.ComboBox} combo This combo box
13425         * @param {Roo.data.Record} record The data record returned from the underlying store
13426         * @param {Number} index The index of the selected item in the dropdown list
13427         */
13428         'beforeselect' : true,
13429         /**
13430          * @event select
13431          * Fires when a list item is selected
13432         * @param {Roo.bootstrap.ComboBox} combo This combo box
13433         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13434         * @param {Number} index The index of the selected item in the dropdown list
13435         */
13436         'select' : true,
13437         /**
13438          * @event beforequery
13439          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13440          * The event object passed has these properties:
13441         * @param {Roo.bootstrap.ComboBox} combo This combo box
13442         * @param {String} query The query
13443         * @param {Boolean} forceAll true to force "all" query
13444         * @param {Boolean} cancel true to cancel the query
13445         * @param {Object} e The query event object
13446         */
13447         'beforequery': true,
13448          /**
13449          * @event add
13450          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13451         * @param {Roo.bootstrap.ComboBox} combo This combo box
13452         */
13453         'add' : true,
13454         /**
13455          * @event edit
13456          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13457         * @param {Roo.bootstrap.ComboBox} combo This combo box
13458         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13459         */
13460         'edit' : true,
13461         /**
13462          * @event remove
13463          * Fires when the remove value from the combobox array
13464         * @param {Roo.bootstrap.ComboBox} combo This combo box
13465         */
13466         'remove' : true,
13467         /**
13468          * @event afterremove
13469          * Fires when the remove value from the combobox array
13470         * @param {Roo.bootstrap.ComboBox} combo This combo box
13471         */
13472         'afterremove' : true,
13473         /**
13474          * @event specialfilter
13475          * Fires when specialfilter
13476             * @param {Roo.bootstrap.ComboBox} combo This combo box
13477             */
13478         'specialfilter' : true,
13479         /**
13480          * @event tick
13481          * Fires when tick the element
13482             * @param {Roo.bootstrap.ComboBox} combo This combo box
13483             */
13484         'tick' : true,
13485         /**
13486          * @event touchviewdisplay
13487          * Fires when touch view require special display (default is using displayField)
13488             * @param {Roo.bootstrap.ComboBox} combo This combo box
13489             * @param {Object} cfg set html .
13490             */
13491         'touchviewdisplay' : true
13492         
13493     });
13494     
13495     this.item = [];
13496     this.tickItems = [];
13497     
13498     this.selectedIndex = -1;
13499     if(this.mode == 'local'){
13500         if(config.queryDelay === undefined){
13501             this.queryDelay = 10;
13502         }
13503         if(config.minChars === undefined){
13504             this.minChars = 0;
13505         }
13506     }
13507 };
13508
13509 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13510      
13511     /**
13512      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13513      * rendering into an Roo.Editor, defaults to false)
13514      */
13515     /**
13516      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13517      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13518      */
13519     /**
13520      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13521      */
13522     /**
13523      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13524      * the dropdown list (defaults to undefined, with no header element)
13525      */
13526
13527      /**
13528      * @cfg {String/Roo.Template} tpl The template to use to render the output
13529      */
13530      
13531      /**
13532      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13533      */
13534     listWidth: undefined,
13535     /**
13536      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13537      * mode = 'remote' or 'text' if mode = 'local')
13538      */
13539     displayField: undefined,
13540     
13541     /**
13542      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13543      * mode = 'remote' or 'value' if mode = 'local'). 
13544      * Note: use of a valueField requires the user make a selection
13545      * in order for a value to be mapped.
13546      */
13547     valueField: undefined,
13548     /**
13549      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13550      */
13551     modalTitle : '',
13552     
13553     /**
13554      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13555      * field's data value (defaults to the underlying DOM element's name)
13556      */
13557     hiddenName: undefined,
13558     /**
13559      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13560      */
13561     listClass: '',
13562     /**
13563      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13564      */
13565     selectedClass: 'active',
13566     
13567     /**
13568      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13569      */
13570     shadow:'sides',
13571     /**
13572      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13573      * anchor positions (defaults to 'tl-bl')
13574      */
13575     listAlign: 'tl-bl?',
13576     /**
13577      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13578      */
13579     maxHeight: 300,
13580     /**
13581      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13582      * query specified by the allQuery config option (defaults to 'query')
13583      */
13584     triggerAction: 'query',
13585     /**
13586      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13587      * (defaults to 4, does not apply if editable = false)
13588      */
13589     minChars : 4,
13590     /**
13591      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13592      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13593      */
13594     typeAhead: false,
13595     /**
13596      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13597      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13598      */
13599     queryDelay: 500,
13600     /**
13601      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13602      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13603      */
13604     pageSize: 0,
13605     /**
13606      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13607      * when editable = true (defaults to false)
13608      */
13609     selectOnFocus:false,
13610     /**
13611      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13612      */
13613     queryParam: 'query',
13614     /**
13615      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13616      * when mode = 'remote' (defaults to 'Loading...')
13617      */
13618     loadingText: 'Loading...',
13619     /**
13620      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13621      */
13622     resizable: false,
13623     /**
13624      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13625      */
13626     handleHeight : 8,
13627     /**
13628      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13629      * traditional select (defaults to true)
13630      */
13631     editable: true,
13632     /**
13633      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13634      */
13635     allQuery: '',
13636     /**
13637      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13638      */
13639     mode: 'remote',
13640     /**
13641      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13642      * listWidth has a higher value)
13643      */
13644     minListWidth : 70,
13645     /**
13646      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13647      * allow the user to set arbitrary text into the field (defaults to false)
13648      */
13649     forceSelection:false,
13650     /**
13651      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13652      * if typeAhead = true (defaults to 250)
13653      */
13654     typeAheadDelay : 250,
13655     /**
13656      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13657      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13658      */
13659     valueNotFoundText : undefined,
13660     /**
13661      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13662      */
13663     blockFocus : false,
13664     
13665     /**
13666      * @cfg {Boolean} disableClear Disable showing of clear button.
13667      */
13668     disableClear : false,
13669     /**
13670      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13671      */
13672     alwaysQuery : false,
13673     
13674     /**
13675      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13676      */
13677     multiple : false,
13678     
13679     /**
13680      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13681      */
13682     invalidClass : "has-warning",
13683     
13684     /**
13685      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13686      */
13687     validClass : "has-success",
13688     
13689     /**
13690      * @cfg {Boolean} specialFilter (true|false) special filter default false
13691      */
13692     specialFilter : false,
13693     
13694     /**
13695      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13696      */
13697     mobileTouchView : true,
13698     
13699     /**
13700      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13701      */
13702     useNativeIOS : false,
13703     
13704     /**
13705      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13706      */
13707     mobile_restrict_height : false,
13708     
13709     ios_options : false,
13710     
13711     //private
13712     addicon : false,
13713     editicon: false,
13714     
13715     page: 0,
13716     hasQuery: false,
13717     append: false,
13718     loadNext: false,
13719     autoFocus : true,
13720     tickable : false,
13721     btnPosition : 'right',
13722     triggerList : true,
13723     showToggleBtn : true,
13724     animate : true,
13725     emptyResultText: 'Empty',
13726     triggerText : 'Select',
13727     emptyTitle : '',
13728     
13729     // element that contains real text value.. (when hidden is used..)
13730     
13731     getAutoCreate : function()
13732     {   
13733         var cfg = false;
13734         //render
13735         /*
13736          * Render classic select for iso
13737          */
13738         
13739         if(Roo.isIOS && this.useNativeIOS){
13740             cfg = this.getAutoCreateNativeIOS();
13741             return cfg;
13742         }
13743         
13744         /*
13745          * Touch Devices
13746          */
13747         
13748         if(Roo.isTouch && this.mobileTouchView){
13749             cfg = this.getAutoCreateTouchView();
13750             return cfg;;
13751         }
13752         
13753         /*
13754          *  Normal ComboBox
13755          */
13756         if(!this.tickable){
13757             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13758             return cfg;
13759         }
13760         
13761         /*
13762          *  ComboBox with tickable selections
13763          */
13764              
13765         var align = this.labelAlign || this.parentLabelAlign();
13766         
13767         cfg = {
13768             cls : 'form-group roo-combobox-tickable' //input-group
13769         };
13770         
13771         var btn_text_select = '';
13772         var btn_text_done = '';
13773         var btn_text_cancel = '';
13774         
13775         if (this.btn_text_show) {
13776             btn_text_select = 'Select';
13777             btn_text_done = 'Done';
13778             btn_text_cancel = 'Cancel'; 
13779         }
13780         
13781         var buttons = {
13782             tag : 'div',
13783             cls : 'tickable-buttons',
13784             cn : [
13785                 {
13786                     tag : 'button',
13787                     type : 'button',
13788                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13789                     //html : this.triggerText
13790                     html: btn_text_select
13791                 },
13792                 {
13793                     tag : 'button',
13794                     type : 'button',
13795                     name : 'ok',
13796                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13797                     //html : 'Done'
13798                     html: btn_text_done
13799                 },
13800                 {
13801                     tag : 'button',
13802                     type : 'button',
13803                     name : 'cancel',
13804                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13805                     //html : 'Cancel'
13806                     html: btn_text_cancel
13807                 }
13808             ]
13809         };
13810         
13811         if(this.editable){
13812             buttons.cn.unshift({
13813                 tag: 'input',
13814                 cls: 'roo-select2-search-field-input'
13815             });
13816         }
13817         
13818         var _this = this;
13819         
13820         Roo.each(buttons.cn, function(c){
13821             if (_this.size) {
13822                 c.cls += ' btn-' + _this.size;
13823             }
13824
13825             if (_this.disabled) {
13826                 c.disabled = true;
13827             }
13828         });
13829         
13830         var box = {
13831             tag: 'div',
13832             style : 'display: contents',
13833             cn: [
13834                 {
13835                     tag: 'input',
13836                     type : 'hidden',
13837                     cls: 'form-hidden-field'
13838                 },
13839                 {
13840                     tag: 'ul',
13841                     cls: 'roo-select2-choices',
13842                     cn:[
13843                         {
13844                             tag: 'li',
13845                             cls: 'roo-select2-search-field',
13846                             cn: [
13847                                 buttons
13848                             ]
13849                         }
13850                     ]
13851                 }
13852             ]
13853         };
13854         
13855         var combobox = {
13856             cls: 'roo-select2-container input-group roo-select2-container-multi',
13857             cn: [
13858                 
13859                 box
13860 //                {
13861 //                    tag: 'ul',
13862 //                    cls: 'typeahead typeahead-long dropdown-menu',
13863 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13864 //                }
13865             ]
13866         };
13867         
13868         if(this.hasFeedback && !this.allowBlank){
13869             
13870             var feedback = {
13871                 tag: 'span',
13872                 cls: 'glyphicon form-control-feedback'
13873             };
13874
13875             combobox.cn.push(feedback);
13876         }
13877         
13878         var indicator = {
13879             tag : 'i',
13880             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13881             tooltip : 'This field is required'
13882         };
13883         if (Roo.bootstrap.version == 4) {
13884             indicator = {
13885                 tag : 'i',
13886                 style : 'display:none'
13887             };
13888         }
13889         if (align ==='left' && this.fieldLabel.length) {
13890             
13891             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13892             
13893             cfg.cn = [
13894                 indicator,
13895                 {
13896                     tag: 'label',
13897                     'for' :  id,
13898                     cls : 'control-label col-form-label',
13899                     html : this.fieldLabel
13900
13901                 },
13902                 {
13903                     cls : "", 
13904                     cn: [
13905                         combobox
13906                     ]
13907                 }
13908
13909             ];
13910             
13911             var labelCfg = cfg.cn[1];
13912             var contentCfg = cfg.cn[2];
13913             
13914
13915             if(this.indicatorpos == 'right'){
13916                 
13917                 cfg.cn = [
13918                     {
13919                         tag: 'label',
13920                         'for' :  id,
13921                         cls : 'control-label col-form-label',
13922                         cn : [
13923                             {
13924                                 tag : 'span',
13925                                 html : this.fieldLabel
13926                             },
13927                             indicator
13928                         ]
13929                     },
13930                     {
13931                         cls : "",
13932                         cn: [
13933                             combobox
13934                         ]
13935                     }
13936
13937                 ];
13938                 
13939                 
13940                 
13941                 labelCfg = cfg.cn[0];
13942                 contentCfg = cfg.cn[1];
13943             
13944             }
13945             
13946             if(this.labelWidth > 12){
13947                 labelCfg.style = "width: " + this.labelWidth + 'px';
13948             }
13949             
13950             if(this.labelWidth < 13 && this.labelmd == 0){
13951                 this.labelmd = this.labelWidth;
13952             }
13953             
13954             if(this.labellg > 0){
13955                 labelCfg.cls += ' col-lg-' + this.labellg;
13956                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13957             }
13958             
13959             if(this.labelmd > 0){
13960                 labelCfg.cls += ' col-md-' + this.labelmd;
13961                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13962             }
13963             
13964             if(this.labelsm > 0){
13965                 labelCfg.cls += ' col-sm-' + this.labelsm;
13966                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13967             }
13968             
13969             if(this.labelxs > 0){
13970                 labelCfg.cls += ' col-xs-' + this.labelxs;
13971                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13972             }
13973                 
13974                 
13975         } else if ( this.fieldLabel.length) {
13976 //                Roo.log(" label");
13977                  cfg.cn = [
13978                    indicator,
13979                     {
13980                         tag: 'label',
13981                         //cls : 'input-group-addon',
13982                         html : this.fieldLabel
13983                     },
13984                     combobox
13985                 ];
13986                 
13987                 if(this.indicatorpos == 'right'){
13988                     cfg.cn = [
13989                         {
13990                             tag: 'label',
13991                             //cls : 'input-group-addon',
13992                             html : this.fieldLabel
13993                         },
13994                         indicator,
13995                         combobox
13996                     ];
13997                     
13998                 }
13999
14000         } else {
14001             
14002 //                Roo.log(" no label && no align");
14003                 cfg = combobox
14004                      
14005                 
14006         }
14007          
14008         var settings=this;
14009         ['xs','sm','md','lg'].map(function(size){
14010             if (settings[size]) {
14011                 cfg.cls += ' col-' + size + '-' + settings[size];
14012             }
14013         });
14014         
14015         return cfg;
14016         
14017     },
14018     
14019     _initEventsCalled : false,
14020     
14021     // private
14022     initEvents: function()
14023     {   
14024         if (this._initEventsCalled) { // as we call render... prevent looping...
14025             return;
14026         }
14027         this._initEventsCalled = true;
14028         
14029         if (!this.store) {
14030             throw "can not find store for combo";
14031         }
14032         
14033         this.indicator = this.indicatorEl();
14034         
14035         this.store = Roo.factory(this.store, Roo.data);
14036         this.store.parent = this;
14037         
14038         // if we are building from html. then this element is so complex, that we can not really
14039         // use the rendered HTML.
14040         // so we have to trash and replace the previous code.
14041         if (Roo.XComponent.build_from_html) {
14042             // remove this element....
14043             var e = this.el.dom, k=0;
14044             while (e ) { e = e.previousSibling;  ++k;}
14045
14046             this.el.remove();
14047             
14048             this.el=false;
14049             this.rendered = false;
14050             
14051             this.render(this.parent().getChildContainer(true), k);
14052         }
14053         
14054         if(Roo.isIOS && this.useNativeIOS){
14055             this.initIOSView();
14056             return;
14057         }
14058         
14059         /*
14060          * Touch Devices
14061          */
14062         
14063         if(Roo.isTouch && this.mobileTouchView){
14064             this.initTouchView();
14065             return;
14066         }
14067         
14068         if(this.tickable){
14069             this.initTickableEvents();
14070             return;
14071         }
14072         
14073         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
14074         
14075         if(this.hiddenName){
14076             
14077             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14078             
14079             this.hiddenField.dom.value =
14080                 this.hiddenValue !== undefined ? this.hiddenValue :
14081                 this.value !== undefined ? this.value : '';
14082
14083             // prevent input submission
14084             this.el.dom.removeAttribute('name');
14085             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14086              
14087              
14088         }
14089         //if(Roo.isGecko){
14090         //    this.el.dom.setAttribute('autocomplete', 'off');
14091         //}
14092         
14093         var cls = 'x-combo-list';
14094         
14095         //this.list = new Roo.Layer({
14096         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
14097         //});
14098         
14099         var _this = this;
14100         
14101         (function(){
14102             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14103             _this.list.setWidth(lw);
14104         }).defer(100);
14105         
14106         this.list.on('mouseover', this.onViewOver, this);
14107         this.list.on('mousemove', this.onViewMove, this);
14108         this.list.on('scroll', this.onViewScroll, this);
14109         
14110         /*
14111         this.list.swallowEvent('mousewheel');
14112         this.assetHeight = 0;
14113
14114         if(this.title){
14115             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
14116             this.assetHeight += this.header.getHeight();
14117         }
14118
14119         this.innerList = this.list.createChild({cls:cls+'-inner'});
14120         this.innerList.on('mouseover', this.onViewOver, this);
14121         this.innerList.on('mousemove', this.onViewMove, this);
14122         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14123         
14124         if(this.allowBlank && !this.pageSize && !this.disableClear){
14125             this.footer = this.list.createChild({cls:cls+'-ft'});
14126             this.pageTb = new Roo.Toolbar(this.footer);
14127            
14128         }
14129         if(this.pageSize){
14130             this.footer = this.list.createChild({cls:cls+'-ft'});
14131             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
14132                     {pageSize: this.pageSize});
14133             
14134         }
14135         
14136         if (this.pageTb && this.allowBlank && !this.disableClear) {
14137             var _this = this;
14138             this.pageTb.add(new Roo.Toolbar.Fill(), {
14139                 cls: 'x-btn-icon x-btn-clear',
14140                 text: '&#160;',
14141                 handler: function()
14142                 {
14143                     _this.collapse();
14144                     _this.clearValue();
14145                     _this.onSelect(false, -1);
14146                 }
14147             });
14148         }
14149         if (this.footer) {
14150             this.assetHeight += this.footer.getHeight();
14151         }
14152         */
14153             
14154         if(!this.tpl){
14155             this.tpl = Roo.bootstrap.version == 4 ?
14156                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
14157                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
14158         }
14159
14160         this.view = new Roo.View(this.list, this.tpl, {
14161             singleSelect:true, store: this.store, selectedClass: this.selectedClass
14162         });
14163         //this.view.wrapEl.setDisplayed(false);
14164         this.view.on('click', this.onViewClick, this);
14165         
14166         
14167         this.store.on('beforeload', this.onBeforeLoad, this);
14168         this.store.on('load', this.onLoad, this);
14169         this.store.on('loadexception', this.onLoadException, this);
14170         /*
14171         if(this.resizable){
14172             this.resizer = new Roo.Resizable(this.list,  {
14173                pinned:true, handles:'se'
14174             });
14175             this.resizer.on('resize', function(r, w, h){
14176                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
14177                 this.listWidth = w;
14178                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
14179                 this.restrictHeight();
14180             }, this);
14181             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
14182         }
14183         */
14184         if(!this.editable){
14185             this.editable = true;
14186             this.setEditable(false);
14187         }
14188         
14189         /*
14190         
14191         if (typeof(this.events.add.listeners) != 'undefined') {
14192             
14193             this.addicon = this.wrap.createChild(
14194                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
14195        
14196             this.addicon.on('click', function(e) {
14197                 this.fireEvent('add', this);
14198             }, this);
14199         }
14200         if (typeof(this.events.edit.listeners) != 'undefined') {
14201             
14202             this.editicon = this.wrap.createChild(
14203                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
14204             if (this.addicon) {
14205                 this.editicon.setStyle('margin-left', '40px');
14206             }
14207             this.editicon.on('click', function(e) {
14208                 
14209                 // we fire even  if inothing is selected..
14210                 this.fireEvent('edit', this, this.lastData );
14211                 
14212             }, this);
14213         }
14214         */
14215         
14216         this.keyNav = new Roo.KeyNav(this.inputEl(), {
14217             "up" : function(e){
14218                 this.inKeyMode = true;
14219                 this.selectPrev();
14220             },
14221
14222             "down" : function(e){
14223                 if(!this.isExpanded()){
14224                     this.onTriggerClick();
14225                 }else{
14226                     this.inKeyMode = true;
14227                     this.selectNext();
14228                 }
14229             },
14230
14231             "enter" : function(e){
14232 //                this.onViewClick();
14233                 //return true;
14234                 this.collapse();
14235                 
14236                 if(this.fireEvent("specialkey", this, e)){
14237                     this.onViewClick(false);
14238                 }
14239                 
14240                 return true;
14241             },
14242
14243             "esc" : function(e){
14244                 this.collapse();
14245             },
14246
14247             "tab" : function(e){
14248                 this.collapse();
14249                 
14250                 if(this.fireEvent("specialkey", this, e)){
14251                     this.onViewClick(false);
14252                 }
14253                 
14254                 return true;
14255             },
14256
14257             scope : this,
14258
14259             doRelay : function(foo, bar, hname){
14260                 if(hname == 'down' || this.scope.isExpanded()){
14261                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14262                 }
14263                 return true;
14264             },
14265
14266             forceKeyDown: true
14267         });
14268         
14269         
14270         this.queryDelay = Math.max(this.queryDelay || 10,
14271                 this.mode == 'local' ? 10 : 250);
14272         
14273         
14274         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14275         
14276         if(this.typeAhead){
14277             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14278         }
14279         if(this.editable !== false){
14280             this.inputEl().on("keyup", this.onKeyUp, this);
14281         }
14282         if(this.forceSelection){
14283             this.inputEl().on('blur', this.doForce, this);
14284         }
14285         
14286         if(this.multiple){
14287             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14288             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14289         }
14290     },
14291     
14292     initTickableEvents: function()
14293     {   
14294         this.createList();
14295         
14296         if(this.hiddenName){
14297             
14298             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14299             
14300             this.hiddenField.dom.value =
14301                 this.hiddenValue !== undefined ? this.hiddenValue :
14302                 this.value !== undefined ? this.value : '';
14303
14304             // prevent input submission
14305             this.el.dom.removeAttribute('name');
14306             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14307              
14308              
14309         }
14310         
14311 //        this.list = this.el.select('ul.dropdown-menu',true).first();
14312         
14313         this.choices = this.el.select('ul.roo-select2-choices', true).first();
14314         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14315         if(this.triggerList){
14316             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14317         }
14318          
14319         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14320         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14321         
14322         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14323         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14324         
14325         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14326         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14327         
14328         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14329         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14330         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14331         
14332         this.okBtn.hide();
14333         this.cancelBtn.hide();
14334         
14335         var _this = this;
14336         
14337         (function(){
14338             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14339             _this.list.setWidth(lw);
14340         }).defer(100);
14341         
14342         this.list.on('mouseover', this.onViewOver, this);
14343         this.list.on('mousemove', this.onViewMove, this);
14344         
14345         this.list.on('scroll', this.onViewScroll, this);
14346         
14347         if(!this.tpl){
14348             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
14349                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14350         }
14351
14352         this.view = new Roo.View(this.list, this.tpl, {
14353             singleSelect:true,
14354             tickable:true,
14355             parent:this,
14356             store: this.store,
14357             selectedClass: this.selectedClass
14358         });
14359         
14360         //this.view.wrapEl.setDisplayed(false);
14361         this.view.on('click', this.onViewClick, this);
14362         
14363         
14364         
14365         this.store.on('beforeload', this.onBeforeLoad, this);
14366         this.store.on('load', this.onLoad, this);
14367         this.store.on('loadexception', this.onLoadException, this);
14368         
14369         if(this.editable){
14370             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14371                 "up" : function(e){
14372                     this.inKeyMode = true;
14373                     this.selectPrev();
14374                 },
14375
14376                 "down" : function(e){
14377                     this.inKeyMode = true;
14378                     this.selectNext();
14379                 },
14380
14381                 "enter" : function(e){
14382                     if(this.fireEvent("specialkey", this, e)){
14383                         this.onViewClick(false);
14384                     }
14385                     
14386                     return true;
14387                 },
14388
14389                 "esc" : function(e){
14390                     this.onTickableFooterButtonClick(e, false, false);
14391                 },
14392
14393                 "tab" : function(e){
14394                     this.fireEvent("specialkey", this, e);
14395                     
14396                     this.onTickableFooterButtonClick(e, false, false);
14397                     
14398                     return true;
14399                 },
14400
14401                 scope : this,
14402
14403                 doRelay : function(e, fn, key){
14404                     if(this.scope.isExpanded()){
14405                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14406                     }
14407                     return true;
14408                 },
14409
14410                 forceKeyDown: true
14411             });
14412         }
14413         
14414         this.queryDelay = Math.max(this.queryDelay || 10,
14415                 this.mode == 'local' ? 10 : 250);
14416         
14417         
14418         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14419         
14420         if(this.typeAhead){
14421             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14422         }
14423         
14424         if(this.editable !== false){
14425             this.tickableInputEl().on("keyup", this.onKeyUp, this);
14426         }
14427         
14428         this.indicator = this.indicatorEl();
14429         
14430         if(this.indicator){
14431             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14432             this.indicator.hide();
14433         }
14434         
14435     },
14436
14437     onDestroy : function(){
14438         if(this.view){
14439             this.view.setStore(null);
14440             this.view.el.removeAllListeners();
14441             this.view.el.remove();
14442             this.view.purgeListeners();
14443         }
14444         if(this.list){
14445             this.list.dom.innerHTML  = '';
14446         }
14447         
14448         if(this.store){
14449             this.store.un('beforeload', this.onBeforeLoad, this);
14450             this.store.un('load', this.onLoad, this);
14451             this.store.un('loadexception', this.onLoadException, this);
14452         }
14453         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14454     },
14455
14456     // private
14457     fireKey : function(e){
14458         if(e.isNavKeyPress() && !this.list.isVisible()){
14459             this.fireEvent("specialkey", this, e);
14460         }
14461     },
14462
14463     // private
14464     onResize: function(w, h){
14465 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14466 //        
14467 //        if(typeof w != 'number'){
14468 //            // we do not handle it!?!?
14469 //            return;
14470 //        }
14471 //        var tw = this.trigger.getWidth();
14472 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14473 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14474 //        var x = w - tw;
14475 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14476 //            
14477 //        //this.trigger.setStyle('left', x+'px');
14478 //        
14479 //        if(this.list && this.listWidth === undefined){
14480 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14481 //            this.list.setWidth(lw);
14482 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14483 //        }
14484         
14485     
14486         
14487     },
14488
14489     /**
14490      * Allow or prevent the user from directly editing the field text.  If false is passed,
14491      * the user will only be able to select from the items defined in the dropdown list.  This method
14492      * is the runtime equivalent of setting the 'editable' config option at config time.
14493      * @param {Boolean} value True to allow the user to directly edit the field text
14494      */
14495     setEditable : function(value){
14496         if(value == this.editable){
14497             return;
14498         }
14499         this.editable = value;
14500         if(!value){
14501             this.inputEl().dom.setAttribute('readOnly', true);
14502             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14503             this.inputEl().addClass('x-combo-noedit');
14504         }else{
14505             this.inputEl().dom.setAttribute('readOnly', false);
14506             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14507             this.inputEl().removeClass('x-combo-noedit');
14508         }
14509     },
14510
14511     // private
14512     
14513     onBeforeLoad : function(combo,opts){
14514         if(!this.hasFocus){
14515             return;
14516         }
14517          if (!opts.add) {
14518             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14519          }
14520         this.restrictHeight();
14521         this.selectedIndex = -1;
14522     },
14523
14524     // private
14525     onLoad : function(){
14526         
14527         this.hasQuery = false;
14528         
14529         if(!this.hasFocus){
14530             return;
14531         }
14532         
14533         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14534             this.loading.hide();
14535         }
14536         
14537         if(this.store.getCount() > 0){
14538             
14539             this.expand();
14540             this.restrictHeight();
14541             if(this.lastQuery == this.allQuery){
14542                 if(this.editable && !this.tickable){
14543                     this.inputEl().dom.select();
14544                 }
14545                 
14546                 if(
14547                     !this.selectByValue(this.value, true) &&
14548                     this.autoFocus && 
14549                     (
14550                         !this.store.lastOptions ||
14551                         typeof(this.store.lastOptions.add) == 'undefined' || 
14552                         this.store.lastOptions.add != true
14553                     )
14554                 ){
14555                     this.select(0, true);
14556                 }
14557             }else{
14558                 if(this.autoFocus){
14559                     this.selectNext();
14560                 }
14561                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14562                     this.taTask.delay(this.typeAheadDelay);
14563                 }
14564             }
14565         }else{
14566             this.onEmptyResults();
14567         }
14568         
14569         //this.el.focus();
14570     },
14571     // private
14572     onLoadException : function()
14573     {
14574         this.hasQuery = false;
14575         
14576         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14577             this.loading.hide();
14578         }
14579         
14580         if(this.tickable && this.editable){
14581             return;
14582         }
14583         
14584         this.collapse();
14585         // only causes errors at present
14586         //Roo.log(this.store.reader.jsonData);
14587         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14588             // fixme
14589             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14590         //}
14591         
14592         
14593     },
14594     // private
14595     onTypeAhead : function(){
14596         if(this.store.getCount() > 0){
14597             var r = this.store.getAt(0);
14598             var newValue = r.data[this.displayField];
14599             var len = newValue.length;
14600             var selStart = this.getRawValue().length;
14601             
14602             if(selStart != len){
14603                 this.setRawValue(newValue);
14604                 this.selectText(selStart, newValue.length);
14605             }
14606         }
14607     },
14608
14609     // private
14610     onSelect : function(record, index){
14611         
14612         if(this.fireEvent('beforeselect', this, record, index) !== false){
14613         
14614             this.setFromData(index > -1 ? record.data : false);
14615             
14616             this.collapse();
14617             this.fireEvent('select', this, record, index);
14618         }
14619     },
14620
14621     /**
14622      * Returns the currently selected field value or empty string if no value is set.
14623      * @return {String} value The selected value
14624      */
14625     getValue : function()
14626     {
14627         if(Roo.isIOS && this.useNativeIOS){
14628             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14629         }
14630         
14631         if(this.multiple){
14632             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14633         }
14634         
14635         if(this.valueField){
14636             return typeof this.value != 'undefined' ? this.value : '';
14637         }else{
14638             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14639         }
14640     },
14641     
14642     getRawValue : function()
14643     {
14644         if(Roo.isIOS && this.useNativeIOS){
14645             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14646         }
14647         
14648         var v = this.inputEl().getValue();
14649         
14650         return v;
14651     },
14652
14653     /**
14654      * Clears any text/value currently set in the field
14655      */
14656     clearValue : function(){
14657         
14658         if(this.hiddenField){
14659             this.hiddenField.dom.value = '';
14660         }
14661         this.value = '';
14662         this.setRawValue('');
14663         this.lastSelectionText = '';
14664         this.lastData = false;
14665         
14666         var close = this.closeTriggerEl();
14667         
14668         if(close){
14669             close.hide();
14670         }
14671         
14672         this.validate();
14673         
14674     },
14675
14676     /**
14677      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14678      * will be displayed in the field.  If the value does not match the data value of an existing item,
14679      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14680      * Otherwise the field will be blank (although the value will still be set).
14681      * @param {String} value The value to match
14682      */
14683     setValue : function(v)
14684     {
14685         if(Roo.isIOS && this.useNativeIOS){
14686             this.setIOSValue(v);
14687             return;
14688         }
14689         
14690         if(this.multiple){
14691             this.syncValue();
14692             return;
14693         }
14694         
14695         var text = v;
14696         if(this.valueField){
14697             var r = this.findRecord(this.valueField, v);
14698             if(r){
14699                 text = r.data[this.displayField];
14700             }else if(this.valueNotFoundText !== undefined){
14701                 text = this.valueNotFoundText;
14702             }
14703         }
14704         this.lastSelectionText = text;
14705         if(this.hiddenField){
14706             this.hiddenField.dom.value = v;
14707         }
14708         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14709         this.value = v;
14710         
14711         var close = this.closeTriggerEl();
14712         
14713         if(close){
14714             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14715         }
14716         
14717         this.validate();
14718     },
14719     /**
14720      * @property {Object} the last set data for the element
14721      */
14722     
14723     lastData : false,
14724     /**
14725      * Sets the value of the field based on a object which is related to the record format for the store.
14726      * @param {Object} value the value to set as. or false on reset?
14727      */
14728     setFromData : function(o){
14729         
14730         if(this.multiple){
14731             this.addItem(o);
14732             return;
14733         }
14734             
14735         var dv = ''; // display value
14736         var vv = ''; // value value..
14737         this.lastData = o;
14738         if (this.displayField) {
14739             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14740         } else {
14741             // this is an error condition!!!
14742             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14743         }
14744         
14745         if(this.valueField){
14746             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14747         }
14748         
14749         var close = this.closeTriggerEl();
14750         
14751         if(close){
14752             if(dv.length || vv * 1 > 0){
14753                 close.show() ;
14754                 this.blockFocus=true;
14755             } else {
14756                 close.hide();
14757             }             
14758         }
14759         
14760         if(this.hiddenField){
14761             this.hiddenField.dom.value = vv;
14762             
14763             this.lastSelectionText = dv;
14764             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14765             this.value = vv;
14766             return;
14767         }
14768         // no hidden field.. - we store the value in 'value', but still display
14769         // display field!!!!
14770         this.lastSelectionText = dv;
14771         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14772         this.value = vv;
14773         
14774         
14775         
14776     },
14777     // private
14778     reset : function(){
14779         // overridden so that last data is reset..
14780         
14781         if(this.multiple){
14782             this.clearItem();
14783             return;
14784         }
14785         
14786         this.setValue(this.originalValue);
14787         //this.clearInvalid();
14788         this.lastData = false;
14789         if (this.view) {
14790             this.view.clearSelections();
14791         }
14792         
14793         this.validate();
14794     },
14795     // private
14796     findRecord : function(prop, value){
14797         var record;
14798         if(this.store.getCount() > 0){
14799             this.store.each(function(r){
14800                 if(r.data[prop] == value){
14801                     record = r;
14802                     return false;
14803                 }
14804                 return true;
14805             });
14806         }
14807         return record;
14808     },
14809     
14810     getName: function()
14811     {
14812         // returns hidden if it's set..
14813         if (!this.rendered) {return ''};
14814         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14815         
14816     },
14817     // private
14818     onViewMove : function(e, t){
14819         this.inKeyMode = false;
14820     },
14821
14822     // private
14823     onViewOver : function(e, t){
14824         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14825             return;
14826         }
14827         var item = this.view.findItemFromChild(t);
14828         
14829         if(item){
14830             var index = this.view.indexOf(item);
14831             this.select(index, false);
14832         }
14833     },
14834
14835     // private
14836     onViewClick : function(view, doFocus, el, e)
14837     {
14838         var index = this.view.getSelectedIndexes()[0];
14839         
14840         var r = this.store.getAt(index);
14841         
14842         if(this.tickable){
14843             
14844             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14845                 return;
14846             }
14847             
14848             var rm = false;
14849             var _this = this;
14850             
14851             Roo.each(this.tickItems, function(v,k){
14852                 
14853                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14854                     Roo.log(v);
14855                     _this.tickItems.splice(k, 1);
14856                     
14857                     if(typeof(e) == 'undefined' && view == false){
14858                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14859                     }
14860                     
14861                     rm = true;
14862                     return;
14863                 }
14864             });
14865             
14866             if(rm){
14867                 return;
14868             }
14869             
14870             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14871                 this.tickItems.push(r.data);
14872             }
14873             
14874             if(typeof(e) == 'undefined' && view == false){
14875                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14876             }
14877                     
14878             return;
14879         }
14880         
14881         if(r){
14882             this.onSelect(r, index);
14883         }
14884         if(doFocus !== false && !this.blockFocus){
14885             this.inputEl().focus();
14886         }
14887     },
14888
14889     // private
14890     restrictHeight : function(){
14891         //this.innerList.dom.style.height = '';
14892         //var inner = this.innerList.dom;
14893         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14894         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14895         //this.list.beginUpdate();
14896         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14897         this.list.alignTo(this.inputEl(), this.listAlign);
14898         this.list.alignTo(this.inputEl(), this.listAlign);
14899         //this.list.endUpdate();
14900     },
14901
14902     // private
14903     onEmptyResults : function(){
14904         
14905         if(this.tickable && this.editable){
14906             this.hasFocus = false;
14907             this.restrictHeight();
14908             return;
14909         }
14910         
14911         this.collapse();
14912     },
14913
14914     /**
14915      * Returns true if the dropdown list is expanded, else false.
14916      */
14917     isExpanded : function(){
14918         return this.list.isVisible();
14919     },
14920
14921     /**
14922      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14923      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14924      * @param {String} value The data value of the item to select
14925      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14926      * selected item if it is not currently in view (defaults to true)
14927      * @return {Boolean} True if the value matched an item in the list, else false
14928      */
14929     selectByValue : function(v, scrollIntoView){
14930         if(v !== undefined && v !== null){
14931             var r = this.findRecord(this.valueField || this.displayField, v);
14932             if(r){
14933                 this.select(this.store.indexOf(r), scrollIntoView);
14934                 return true;
14935             }
14936         }
14937         return false;
14938     },
14939
14940     /**
14941      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14942      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14943      * @param {Number} index The zero-based index of the list item to select
14944      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14945      * selected item if it is not currently in view (defaults to true)
14946      */
14947     select : function(index, scrollIntoView){
14948         this.selectedIndex = index;
14949         this.view.select(index);
14950         if(scrollIntoView !== false){
14951             var el = this.view.getNode(index);
14952             /*
14953              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14954              */
14955             if(el){
14956                 this.list.scrollChildIntoView(el, false);
14957             }
14958         }
14959     },
14960
14961     // private
14962     selectNext : function(){
14963         var ct = this.store.getCount();
14964         if(ct > 0){
14965             if(this.selectedIndex == -1){
14966                 this.select(0);
14967             }else if(this.selectedIndex < ct-1){
14968                 this.select(this.selectedIndex+1);
14969             }
14970         }
14971     },
14972
14973     // private
14974     selectPrev : function(){
14975         var ct = this.store.getCount();
14976         if(ct > 0){
14977             if(this.selectedIndex == -1){
14978                 this.select(0);
14979             }else if(this.selectedIndex != 0){
14980                 this.select(this.selectedIndex-1);
14981             }
14982         }
14983     },
14984
14985     // private
14986     onKeyUp : function(e){
14987         if(this.editable !== false && !e.isSpecialKey()){
14988             this.lastKey = e.getKey();
14989             this.dqTask.delay(this.queryDelay);
14990         }
14991     },
14992
14993     // private
14994     validateBlur : function(){
14995         return !this.list || !this.list.isVisible();   
14996     },
14997
14998     // private
14999     initQuery : function(){
15000         
15001         var v = this.getRawValue();
15002         
15003         if(this.tickable && this.editable){
15004             v = this.tickableInputEl().getValue();
15005         }
15006         
15007         this.doQuery(v);
15008     },
15009
15010     // private
15011     doForce : function(){
15012         if(this.inputEl().dom.value.length > 0){
15013             this.inputEl().dom.value =
15014                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
15015              
15016         }
15017     },
15018
15019     /**
15020      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
15021      * query allowing the query action to be canceled if needed.
15022      * @param {String} query The SQL query to execute
15023      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
15024      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
15025      * saved in the current store (defaults to false)
15026      */
15027     doQuery : function(q, forceAll){
15028         
15029         if(q === undefined || q === null){
15030             q = '';
15031         }
15032         var qe = {
15033             query: q,
15034             forceAll: forceAll,
15035             combo: this,
15036             cancel:false
15037         };
15038         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
15039             return false;
15040         }
15041         q = qe.query;
15042         
15043         forceAll = qe.forceAll;
15044         if(forceAll === true || (q.length >= this.minChars)){
15045             
15046             this.hasQuery = true;
15047             
15048             if(this.lastQuery != q || this.alwaysQuery){
15049                 this.lastQuery = q;
15050                 if(this.mode == 'local'){
15051                     this.selectedIndex = -1;
15052                     if(forceAll){
15053                         this.store.clearFilter();
15054                     }else{
15055                         
15056                         if(this.specialFilter){
15057                             this.fireEvent('specialfilter', this);
15058                             this.onLoad();
15059                             return;
15060                         }
15061                         
15062                         this.store.filter(this.displayField, q);
15063                     }
15064                     
15065                     this.store.fireEvent("datachanged", this.store);
15066                     
15067                     this.onLoad();
15068                     
15069                     
15070                 }else{
15071                     
15072                     this.store.baseParams[this.queryParam] = q;
15073                     
15074                     var options = {params : this.getParams(q)};
15075                     
15076                     if(this.loadNext){
15077                         options.add = true;
15078                         options.params.start = this.page * this.pageSize;
15079                     }
15080                     
15081                     this.store.load(options);
15082                     
15083                     /*
15084                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
15085                      *  we should expand the list on onLoad
15086                      *  so command out it
15087                      */
15088 //                    this.expand();
15089                 }
15090             }else{
15091                 this.selectedIndex = -1;
15092                 this.onLoad();   
15093             }
15094         }
15095         
15096         this.loadNext = false;
15097     },
15098     
15099     // private
15100     getParams : function(q){
15101         var p = {};
15102         //p[this.queryParam] = q;
15103         
15104         if(this.pageSize){
15105             p.start = 0;
15106             p.limit = this.pageSize;
15107         }
15108         return p;
15109     },
15110
15111     /**
15112      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
15113      */
15114     collapse : function(){
15115         if(!this.isExpanded()){
15116             return;
15117         }
15118         
15119         this.list.hide();
15120         
15121         this.hasFocus = false;
15122         
15123         if(this.tickable){
15124             this.okBtn.hide();
15125             this.cancelBtn.hide();
15126             this.trigger.show();
15127             
15128             if(this.editable){
15129                 this.tickableInputEl().dom.value = '';
15130                 this.tickableInputEl().blur();
15131             }
15132             
15133         }
15134         
15135         Roo.get(document).un('mousedown', this.collapseIf, this);
15136         Roo.get(document).un('mousewheel', this.collapseIf, this);
15137         if (!this.editable) {
15138             Roo.get(document).un('keydown', this.listKeyPress, this);
15139         }
15140         this.fireEvent('collapse', this);
15141         
15142         this.validate();
15143     },
15144
15145     // private
15146     collapseIf : function(e){
15147         var in_combo  = e.within(this.el);
15148         var in_list =  e.within(this.list);
15149         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
15150         
15151         if (in_combo || in_list || is_list) {
15152             //e.stopPropagation();
15153             return;
15154         }
15155         
15156         if(this.tickable){
15157             this.onTickableFooterButtonClick(e, false, false);
15158         }
15159
15160         this.collapse();
15161         
15162     },
15163
15164     /**
15165      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
15166      */
15167     expand : function(){
15168        
15169         if(this.isExpanded() || !this.hasFocus){
15170             return;
15171         }
15172         
15173         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
15174         this.list.setWidth(lw);
15175         
15176         Roo.log('expand');
15177         
15178         this.list.show();
15179         
15180         this.restrictHeight();
15181         
15182         if(this.tickable){
15183             
15184             this.tickItems = Roo.apply([], this.item);
15185             
15186             this.okBtn.show();
15187             this.cancelBtn.show();
15188             this.trigger.hide();
15189             
15190             if(this.editable){
15191                 this.tickableInputEl().focus();
15192             }
15193             
15194         }
15195         
15196         Roo.get(document).on('mousedown', this.collapseIf, this);
15197         Roo.get(document).on('mousewheel', this.collapseIf, this);
15198         if (!this.editable) {
15199             Roo.get(document).on('keydown', this.listKeyPress, this);
15200         }
15201         
15202         this.fireEvent('expand', this);
15203     },
15204
15205     // private
15206     // Implements the default empty TriggerField.onTriggerClick function
15207     onTriggerClick : function(e)
15208     {
15209         Roo.log('trigger click');
15210         
15211         if(this.disabled || !this.triggerList){
15212             return;
15213         }
15214         
15215         this.page = 0;
15216         this.loadNext = false;
15217         
15218         if(this.isExpanded()){
15219             this.collapse();
15220             if (!this.blockFocus) {
15221                 this.inputEl().focus();
15222             }
15223             
15224         }else {
15225             this.hasFocus = true;
15226             if(this.triggerAction == 'all') {
15227                 this.doQuery(this.allQuery, true);
15228             } else {
15229                 this.doQuery(this.getRawValue());
15230             }
15231             if (!this.blockFocus) {
15232                 this.inputEl().focus();
15233             }
15234         }
15235     },
15236     
15237     onTickableTriggerClick : function(e)
15238     {
15239         if(this.disabled){
15240             return;
15241         }
15242         
15243         this.page = 0;
15244         this.loadNext = false;
15245         this.hasFocus = true;
15246         
15247         if(this.triggerAction == 'all') {
15248             this.doQuery(this.allQuery, true);
15249         } else {
15250             this.doQuery(this.getRawValue());
15251         }
15252     },
15253     
15254     onSearchFieldClick : function(e)
15255     {
15256         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
15257             this.onTickableFooterButtonClick(e, false, false);
15258             return;
15259         }
15260         
15261         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
15262             return;
15263         }
15264         
15265         this.page = 0;
15266         this.loadNext = false;
15267         this.hasFocus = true;
15268         
15269         if(this.triggerAction == 'all') {
15270             this.doQuery(this.allQuery, true);
15271         } else {
15272             this.doQuery(this.getRawValue());
15273         }
15274     },
15275     
15276     listKeyPress : function(e)
15277     {
15278         //Roo.log('listkeypress');
15279         // scroll to first matching element based on key pres..
15280         if (e.isSpecialKey()) {
15281             return false;
15282         }
15283         var k = String.fromCharCode(e.getKey()).toUpperCase();
15284         //Roo.log(k);
15285         var match  = false;
15286         var csel = this.view.getSelectedNodes();
15287         var cselitem = false;
15288         if (csel.length) {
15289             var ix = this.view.indexOf(csel[0]);
15290             cselitem  = this.store.getAt(ix);
15291             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15292                 cselitem = false;
15293             }
15294             
15295         }
15296         
15297         this.store.each(function(v) { 
15298             if (cselitem) {
15299                 // start at existing selection.
15300                 if (cselitem.id == v.id) {
15301                     cselitem = false;
15302                 }
15303                 return true;
15304             }
15305                 
15306             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15307                 match = this.store.indexOf(v);
15308                 return false;
15309             }
15310             return true;
15311         }, this);
15312         
15313         if (match === false) {
15314             return true; // no more action?
15315         }
15316         // scroll to?
15317         this.view.select(match);
15318         var sn = Roo.get(this.view.getSelectedNodes()[0]);
15319         sn.scrollIntoView(sn.dom.parentNode, false);
15320     },
15321     
15322     onViewScroll : function(e, t){
15323         
15324         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){
15325             return;
15326         }
15327         
15328         this.hasQuery = true;
15329         
15330         this.loading = this.list.select('.loading', true).first();
15331         
15332         if(this.loading === null){
15333             this.list.createChild({
15334                 tag: 'div',
15335                 cls: 'loading roo-select2-more-results roo-select2-active',
15336                 html: 'Loading more results...'
15337             });
15338             
15339             this.loading = this.list.select('.loading', true).first();
15340             
15341             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15342             
15343             this.loading.hide();
15344         }
15345         
15346         this.loading.show();
15347         
15348         var _combo = this;
15349         
15350         this.page++;
15351         this.loadNext = true;
15352         
15353         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15354         
15355         return;
15356     },
15357     
15358     addItem : function(o)
15359     {   
15360         var dv = ''; // display value
15361         
15362         if (this.displayField) {
15363             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15364         } else {
15365             // this is an error condition!!!
15366             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
15367         }
15368         
15369         if(!dv.length){
15370             return;
15371         }
15372         
15373         var choice = this.choices.createChild({
15374             tag: 'li',
15375             cls: 'roo-select2-search-choice',
15376             cn: [
15377                 {
15378                     tag: 'div',
15379                     html: dv
15380                 },
15381                 {
15382                     tag: 'a',
15383                     href: '#',
15384                     cls: 'roo-select2-search-choice-close fa fa-times',
15385                     tabindex: '-1'
15386                 }
15387             ]
15388             
15389         }, this.searchField);
15390         
15391         var close = choice.select('a.roo-select2-search-choice-close', true).first();
15392         
15393         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15394         
15395         this.item.push(o);
15396         
15397         this.lastData = o;
15398         
15399         this.syncValue();
15400         
15401         this.inputEl().dom.value = '';
15402         
15403         this.validate();
15404     },
15405     
15406     onRemoveItem : function(e, _self, o)
15407     {
15408         e.preventDefault();
15409         
15410         this.lastItem = Roo.apply([], this.item);
15411         
15412         var index = this.item.indexOf(o.data) * 1;
15413         
15414         if( index < 0){
15415             Roo.log('not this item?!');
15416             return;
15417         }
15418         
15419         this.item.splice(index, 1);
15420         o.item.remove();
15421         
15422         this.syncValue();
15423         
15424         this.fireEvent('remove', this, e);
15425         
15426         this.validate();
15427         
15428     },
15429     
15430     syncValue : function()
15431     {
15432         if(!this.item.length){
15433             this.clearValue();
15434             return;
15435         }
15436             
15437         var value = [];
15438         var _this = this;
15439         Roo.each(this.item, function(i){
15440             if(_this.valueField){
15441                 value.push(i[_this.valueField]);
15442                 return;
15443             }
15444
15445             value.push(i);
15446         });
15447
15448         this.value = value.join(',');
15449
15450         if(this.hiddenField){
15451             this.hiddenField.dom.value = this.value;
15452         }
15453         
15454         this.store.fireEvent("datachanged", this.store);
15455         
15456         this.validate();
15457     },
15458     
15459     clearItem : function()
15460     {
15461         if(!this.multiple){
15462             return;
15463         }
15464         
15465         this.item = [];
15466         
15467         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15468            c.remove();
15469         });
15470         
15471         this.syncValue();
15472         
15473         this.validate();
15474         
15475         if(this.tickable && !Roo.isTouch){
15476             this.view.refresh();
15477         }
15478     },
15479     
15480     inputEl: function ()
15481     {
15482         if(Roo.isIOS && this.useNativeIOS){
15483             return this.el.select('select.roo-ios-select', true).first();
15484         }
15485         
15486         if(Roo.isTouch && this.mobileTouchView){
15487             return this.el.select('input.form-control',true).first();
15488         }
15489         
15490         if(this.tickable){
15491             return this.searchField;
15492         }
15493         
15494         return this.el.select('input.form-control',true).first();
15495     },
15496     
15497     onTickableFooterButtonClick : function(e, btn, el)
15498     {
15499         e.preventDefault();
15500         
15501         this.lastItem = Roo.apply([], this.item);
15502         
15503         if(btn && btn.name == 'cancel'){
15504             this.tickItems = Roo.apply([], this.item);
15505             this.collapse();
15506             return;
15507         }
15508         
15509         this.clearItem();
15510         
15511         var _this = this;
15512         
15513         Roo.each(this.tickItems, function(o){
15514             _this.addItem(o);
15515         });
15516         
15517         this.collapse();
15518         
15519     },
15520     
15521     validate : function()
15522     {
15523         if(this.getVisibilityEl().hasClass('hidden')){
15524             return true;
15525         }
15526         
15527         var v = this.getRawValue();
15528         
15529         if(this.multiple){
15530             v = this.getValue();
15531         }
15532         
15533         if(this.disabled || this.allowBlank || v.length){
15534             this.markValid();
15535             return true;
15536         }
15537         
15538         this.markInvalid();
15539         return false;
15540     },
15541     
15542     tickableInputEl : function()
15543     {
15544         if(!this.tickable || !this.editable){
15545             return this.inputEl();
15546         }
15547         
15548         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15549     },
15550     
15551     
15552     getAutoCreateTouchView : function()
15553     {
15554         var id = Roo.id();
15555         
15556         var cfg = {
15557             cls: 'form-group' //input-group
15558         };
15559         
15560         var input =  {
15561             tag: 'input',
15562             id : id,
15563             type : this.inputType,
15564             cls : 'form-control x-combo-noedit',
15565             autocomplete: 'new-password',
15566             placeholder : this.placeholder || '',
15567             readonly : true
15568         };
15569         
15570         if (this.name) {
15571             input.name = this.name;
15572         }
15573         
15574         if (this.size) {
15575             input.cls += ' input-' + this.size;
15576         }
15577         
15578         if (this.disabled) {
15579             input.disabled = true;
15580         }
15581         
15582         var inputblock = {
15583             cls : '',
15584             cn : [
15585                 input
15586             ]
15587         };
15588         
15589         if(this.before){
15590             inputblock.cls += ' input-group';
15591             
15592             inputblock.cn.unshift({
15593                 tag :'span',
15594                 cls : 'input-group-addon input-group-prepend input-group-text',
15595                 html : this.before
15596             });
15597         }
15598         
15599         if(this.removable && !this.multiple){
15600             inputblock.cls += ' roo-removable';
15601             
15602             inputblock.cn.push({
15603                 tag: 'button',
15604                 html : 'x',
15605                 cls : 'roo-combo-removable-btn close'
15606             });
15607         }
15608
15609         if(this.hasFeedback && !this.allowBlank){
15610             
15611             inputblock.cls += ' has-feedback';
15612             
15613             inputblock.cn.push({
15614                 tag: 'span',
15615                 cls: 'glyphicon form-control-feedback'
15616             });
15617             
15618         }
15619         
15620         if (this.after) {
15621             
15622             inputblock.cls += (this.before) ? '' : ' input-group';
15623             
15624             inputblock.cn.push({
15625                 tag :'span',
15626                 cls : 'input-group-addon input-group-append input-group-text',
15627                 html : this.after
15628             });
15629         }
15630
15631         
15632         var ibwrap = inputblock;
15633         
15634         if(this.multiple){
15635             ibwrap = {
15636                 tag: 'ul',
15637                 cls: 'roo-select2-choices',
15638                 cn:[
15639                     {
15640                         tag: 'li',
15641                         cls: 'roo-select2-search-field',
15642                         cn: [
15643
15644                             inputblock
15645                         ]
15646                     }
15647                 ]
15648             };
15649         
15650             
15651         }
15652         
15653         var combobox = {
15654             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15655             cn: [
15656                 {
15657                     tag: 'input',
15658                     type : 'hidden',
15659                     cls: 'form-hidden-field'
15660                 },
15661                 ibwrap
15662             ]
15663         };
15664         
15665         if(!this.multiple && this.showToggleBtn){
15666             
15667             var caret = {
15668                 cls: 'caret'
15669             };
15670             
15671             if (this.caret != false) {
15672                 caret = {
15673                      tag: 'i',
15674                      cls: 'fa fa-' + this.caret
15675                 };
15676                 
15677             }
15678             
15679             combobox.cn.push({
15680                 tag :'span',
15681                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15682                 cn : [
15683                     Roo.bootstrap.version == 3 ? caret : '',
15684                     {
15685                         tag: 'span',
15686                         cls: 'combobox-clear',
15687                         cn  : [
15688                             {
15689                                 tag : 'i',
15690                                 cls: 'icon-remove'
15691                             }
15692                         ]
15693                     }
15694                 ]
15695
15696             })
15697         }
15698         
15699         if(this.multiple){
15700             combobox.cls += ' roo-select2-container-multi';
15701         }
15702         
15703         var align = this.labelAlign || this.parentLabelAlign();
15704         
15705         if (align ==='left' && this.fieldLabel.length) {
15706
15707             cfg.cn = [
15708                 {
15709                    tag : 'i',
15710                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15711                    tooltip : 'This field is required'
15712                 },
15713                 {
15714                     tag: 'label',
15715                     cls : 'control-label col-form-label',
15716                     html : this.fieldLabel
15717
15718                 },
15719                 {
15720                     cls : '', 
15721                     cn: [
15722                         combobox
15723                     ]
15724                 }
15725             ];
15726             
15727             var labelCfg = cfg.cn[1];
15728             var contentCfg = cfg.cn[2];
15729             
15730
15731             if(this.indicatorpos == 'right'){
15732                 cfg.cn = [
15733                     {
15734                         tag: 'label',
15735                         'for' :  id,
15736                         cls : 'control-label col-form-label',
15737                         cn : [
15738                             {
15739                                 tag : 'span',
15740                                 html : this.fieldLabel
15741                             },
15742                             {
15743                                 tag : 'i',
15744                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15745                                 tooltip : 'This field is required'
15746                             }
15747                         ]
15748                     },
15749                     {
15750                         cls : "",
15751                         cn: [
15752                             combobox
15753                         ]
15754                     }
15755
15756                 ];
15757                 
15758                 labelCfg = cfg.cn[0];
15759                 contentCfg = cfg.cn[1];
15760             }
15761             
15762            
15763             
15764             if(this.labelWidth > 12){
15765                 labelCfg.style = "width: " + this.labelWidth + 'px';
15766             }
15767             
15768             if(this.labelWidth < 13 && this.labelmd == 0){
15769                 this.labelmd = this.labelWidth;
15770             }
15771             
15772             if(this.labellg > 0){
15773                 labelCfg.cls += ' col-lg-' + this.labellg;
15774                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15775             }
15776             
15777             if(this.labelmd > 0){
15778                 labelCfg.cls += ' col-md-' + this.labelmd;
15779                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15780             }
15781             
15782             if(this.labelsm > 0){
15783                 labelCfg.cls += ' col-sm-' + this.labelsm;
15784                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15785             }
15786             
15787             if(this.labelxs > 0){
15788                 labelCfg.cls += ' col-xs-' + this.labelxs;
15789                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15790             }
15791                 
15792                 
15793         } else if ( this.fieldLabel.length) {
15794             cfg.cn = [
15795                 {
15796                    tag : 'i',
15797                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15798                    tooltip : 'This field is required'
15799                 },
15800                 {
15801                     tag: 'label',
15802                     cls : 'control-label',
15803                     html : this.fieldLabel
15804
15805                 },
15806                 {
15807                     cls : '', 
15808                     cn: [
15809                         combobox
15810                     ]
15811                 }
15812             ];
15813             
15814             if(this.indicatorpos == 'right'){
15815                 cfg.cn = [
15816                     {
15817                         tag: 'label',
15818                         cls : 'control-label',
15819                         html : this.fieldLabel,
15820                         cn : [
15821                             {
15822                                tag : 'i',
15823                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15824                                tooltip : 'This field is required'
15825                             }
15826                         ]
15827                     },
15828                     {
15829                         cls : '', 
15830                         cn: [
15831                             combobox
15832                         ]
15833                     }
15834                 ];
15835             }
15836         } else {
15837             cfg.cn = combobox;    
15838         }
15839         
15840         
15841         var settings = this;
15842         
15843         ['xs','sm','md','lg'].map(function(size){
15844             if (settings[size]) {
15845                 cfg.cls += ' col-' + size + '-' + settings[size];
15846             }
15847         });
15848         
15849         return cfg;
15850     },
15851     
15852     initTouchView : function()
15853     {
15854         this.renderTouchView();
15855         
15856         this.touchViewEl.on('scroll', function(){
15857             this.el.dom.scrollTop = 0;
15858         }, this);
15859         
15860         this.originalValue = this.getValue();
15861         
15862         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15863         
15864         this.inputEl().on("click", this.showTouchView, this);
15865         if (this.triggerEl) {
15866             this.triggerEl.on("click", this.showTouchView, this);
15867         }
15868         
15869         
15870         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15871         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15872         
15873         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15874         
15875         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15876         this.store.on('load', this.onTouchViewLoad, this);
15877         this.store.on('loadexception', this.onTouchViewLoadException, this);
15878         
15879         if(this.hiddenName){
15880             
15881             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15882             
15883             this.hiddenField.dom.value =
15884                 this.hiddenValue !== undefined ? this.hiddenValue :
15885                 this.value !== undefined ? this.value : '';
15886         
15887             this.el.dom.removeAttribute('name');
15888             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15889         }
15890         
15891         if(this.multiple){
15892             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15893             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15894         }
15895         
15896         if(this.removable && !this.multiple){
15897             var close = this.closeTriggerEl();
15898             if(close){
15899                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15900                 close.on('click', this.removeBtnClick, this, close);
15901             }
15902         }
15903         /*
15904          * fix the bug in Safari iOS8
15905          */
15906         this.inputEl().on("focus", function(e){
15907             document.activeElement.blur();
15908         }, this);
15909         
15910         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15911         
15912         return;
15913         
15914         
15915     },
15916     
15917     renderTouchView : function()
15918     {
15919         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15920         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15921         
15922         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15923         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15924         
15925         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15926         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15927         this.touchViewBodyEl.setStyle('overflow', 'auto');
15928         
15929         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15930         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15931         
15932         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15933         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15934         
15935     },
15936     
15937     showTouchView : function()
15938     {
15939         if(this.disabled){
15940             return;
15941         }
15942         
15943         this.touchViewHeaderEl.hide();
15944
15945         if(this.modalTitle.length){
15946             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15947             this.touchViewHeaderEl.show();
15948         }
15949
15950         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15951         this.touchViewEl.show();
15952
15953         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15954         
15955         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15956         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15957
15958         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15959
15960         if(this.modalTitle.length){
15961             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15962         }
15963         
15964         this.touchViewBodyEl.setHeight(bodyHeight);
15965
15966         if(this.animate){
15967             var _this = this;
15968             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15969         }else{
15970             this.touchViewEl.addClass('in');
15971         }
15972         
15973         if(this._touchViewMask){
15974             Roo.get(document.body).addClass("x-body-masked");
15975             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15976             this._touchViewMask.setStyle('z-index', 10000);
15977             this._touchViewMask.addClass('show');
15978         }
15979         
15980         this.doTouchViewQuery();
15981         
15982     },
15983     
15984     hideTouchView : function()
15985     {
15986         this.touchViewEl.removeClass('in');
15987
15988         if(this.animate){
15989             var _this = this;
15990             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15991         }else{
15992             this.touchViewEl.setStyle('display', 'none');
15993         }
15994         
15995         if(this._touchViewMask){
15996             this._touchViewMask.removeClass('show');
15997             Roo.get(document.body).removeClass("x-body-masked");
15998         }
15999     },
16000     
16001     setTouchViewValue : function()
16002     {
16003         if(this.multiple){
16004             this.clearItem();
16005         
16006             var _this = this;
16007
16008             Roo.each(this.tickItems, function(o){
16009                 this.addItem(o);
16010             }, this);
16011         }
16012         
16013         this.hideTouchView();
16014     },
16015     
16016     doTouchViewQuery : function()
16017     {
16018         var qe = {
16019             query: '',
16020             forceAll: true,
16021             combo: this,
16022             cancel:false
16023         };
16024         
16025         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
16026             return false;
16027         }
16028         
16029         if(!this.alwaysQuery || this.mode == 'local'){
16030             this.onTouchViewLoad();
16031             return;
16032         }
16033         
16034         this.store.load();
16035     },
16036     
16037     onTouchViewBeforeLoad : function(combo,opts)
16038     {
16039         return;
16040     },
16041
16042     // private
16043     onTouchViewLoad : function()
16044     {
16045         if(this.store.getCount() < 1){
16046             this.onTouchViewEmptyResults();
16047             return;
16048         }
16049         
16050         this.clearTouchView();
16051         
16052         var rawValue = this.getRawValue();
16053         
16054         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
16055         
16056         this.tickItems = [];
16057         
16058         this.store.data.each(function(d, rowIndex){
16059             var row = this.touchViewListGroup.createChild(template);
16060             
16061             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
16062                 row.addClass(d.data.cls);
16063             }
16064             
16065             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16066                 var cfg = {
16067                     data : d.data,
16068                     html : d.data[this.displayField]
16069                 };
16070                 
16071                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
16072                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
16073                 }
16074             }
16075             row.removeClass('selected');
16076             if(!this.multiple && this.valueField &&
16077                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
16078             {
16079                 // radio buttons..
16080                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16081                 row.addClass('selected');
16082             }
16083             
16084             if(this.multiple && this.valueField &&
16085                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
16086             {
16087                 
16088                 // checkboxes...
16089                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16090                 this.tickItems.push(d.data);
16091             }
16092             
16093             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
16094             
16095         }, this);
16096         
16097         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
16098         
16099         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16100
16101         if(this.modalTitle.length){
16102             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16103         }
16104
16105         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
16106         
16107         if(this.mobile_restrict_height && listHeight < bodyHeight){
16108             this.touchViewBodyEl.setHeight(listHeight);
16109         }
16110         
16111         var _this = this;
16112         
16113         if(firstChecked && listHeight > bodyHeight){
16114             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
16115         }
16116         
16117     },
16118     
16119     onTouchViewLoadException : function()
16120     {
16121         this.hideTouchView();
16122     },
16123     
16124     onTouchViewEmptyResults : function()
16125     {
16126         this.clearTouchView();
16127         
16128         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
16129         
16130         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
16131         
16132     },
16133     
16134     clearTouchView : function()
16135     {
16136         this.touchViewListGroup.dom.innerHTML = '';
16137     },
16138     
16139     onTouchViewClick : function(e, el, o)
16140     {
16141         e.preventDefault();
16142         
16143         var row = o.row;
16144         var rowIndex = o.rowIndex;
16145         
16146         var r = this.store.getAt(rowIndex);
16147         
16148         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
16149             
16150             if(!this.multiple){
16151                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
16152                     c.dom.removeAttribute('checked');
16153                 }, this);
16154
16155                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16156
16157                 this.setFromData(r.data);
16158
16159                 var close = this.closeTriggerEl();
16160
16161                 if(close){
16162                     close.show();
16163                 }
16164
16165                 this.hideTouchView();
16166
16167                 this.fireEvent('select', this, r, rowIndex);
16168
16169                 return;
16170             }
16171
16172             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
16173                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
16174                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
16175                 return;
16176             }
16177
16178             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16179             this.addItem(r.data);
16180             this.tickItems.push(r.data);
16181         }
16182     },
16183     
16184     getAutoCreateNativeIOS : function()
16185     {
16186         var cfg = {
16187             cls: 'form-group' //input-group,
16188         };
16189         
16190         var combobox =  {
16191             tag: 'select',
16192             cls : 'roo-ios-select'
16193         };
16194         
16195         if (this.name) {
16196             combobox.name = this.name;
16197         }
16198         
16199         if (this.disabled) {
16200             combobox.disabled = true;
16201         }
16202         
16203         var settings = this;
16204         
16205         ['xs','sm','md','lg'].map(function(size){
16206             if (settings[size]) {
16207                 cfg.cls += ' col-' + size + '-' + settings[size];
16208             }
16209         });
16210         
16211         cfg.cn = combobox;
16212         
16213         return cfg;
16214         
16215     },
16216     
16217     initIOSView : function()
16218     {
16219         this.store.on('load', this.onIOSViewLoad, this);
16220         
16221         return;
16222     },
16223     
16224     onIOSViewLoad : function()
16225     {
16226         if(this.store.getCount() < 1){
16227             return;
16228         }
16229         
16230         this.clearIOSView();
16231         
16232         if(this.allowBlank) {
16233             
16234             var default_text = '-- SELECT --';
16235             
16236             if(this.placeholder.length){
16237                 default_text = this.placeholder;
16238             }
16239             
16240             if(this.emptyTitle.length){
16241                 default_text += ' - ' + this.emptyTitle + ' -';
16242             }
16243             
16244             var opt = this.inputEl().createChild({
16245                 tag: 'option',
16246                 value : 0,
16247                 html : default_text
16248             });
16249             
16250             var o = {};
16251             o[this.valueField] = 0;
16252             o[this.displayField] = default_text;
16253             
16254             this.ios_options.push({
16255                 data : o,
16256                 el : opt
16257             });
16258             
16259         }
16260         
16261         this.store.data.each(function(d, rowIndex){
16262             
16263             var html = '';
16264             
16265             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16266                 html = d.data[this.displayField];
16267             }
16268             
16269             var value = '';
16270             
16271             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
16272                 value = d.data[this.valueField];
16273             }
16274             
16275             var option = {
16276                 tag: 'option',
16277                 value : value,
16278                 html : html
16279             };
16280             
16281             if(this.value == d.data[this.valueField]){
16282                 option['selected'] = true;
16283             }
16284             
16285             var opt = this.inputEl().createChild(option);
16286             
16287             this.ios_options.push({
16288                 data : d.data,
16289                 el : opt
16290             });
16291             
16292         }, this);
16293         
16294         this.inputEl().on('change', function(){
16295            this.fireEvent('select', this);
16296         }, this);
16297         
16298     },
16299     
16300     clearIOSView: function()
16301     {
16302         this.inputEl().dom.innerHTML = '';
16303         
16304         this.ios_options = [];
16305     },
16306     
16307     setIOSValue: function(v)
16308     {
16309         this.value = v;
16310         
16311         if(!this.ios_options){
16312             return;
16313         }
16314         
16315         Roo.each(this.ios_options, function(opts){
16316            
16317            opts.el.dom.removeAttribute('selected');
16318            
16319            if(opts.data[this.valueField] != v){
16320                return;
16321            }
16322            
16323            opts.el.dom.setAttribute('selected', true);
16324            
16325         }, this);
16326     }
16327
16328     /** 
16329     * @cfg {Boolean} grow 
16330     * @hide 
16331     */
16332     /** 
16333     * @cfg {Number} growMin 
16334     * @hide 
16335     */
16336     /** 
16337     * @cfg {Number} growMax 
16338     * @hide 
16339     */
16340     /**
16341      * @hide
16342      * @method autoSize
16343      */
16344 });
16345
16346 Roo.apply(Roo.bootstrap.ComboBox,  {
16347     
16348     header : {
16349         tag: 'div',
16350         cls: 'modal-header',
16351         cn: [
16352             {
16353                 tag: 'h4',
16354                 cls: 'modal-title'
16355             }
16356         ]
16357     },
16358     
16359     body : {
16360         tag: 'div',
16361         cls: 'modal-body',
16362         cn: [
16363             {
16364                 tag: 'ul',
16365                 cls: 'list-group'
16366             }
16367         ]
16368     },
16369     
16370     listItemRadio : {
16371         tag: 'li',
16372         cls: 'list-group-item',
16373         cn: [
16374             {
16375                 tag: 'span',
16376                 cls: 'roo-combobox-list-group-item-value'
16377             },
16378             {
16379                 tag: 'div',
16380                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16381                 cn: [
16382                     {
16383                         tag: 'input',
16384                         type: 'radio'
16385                     },
16386                     {
16387                         tag: 'label'
16388                     }
16389                 ]
16390             }
16391         ]
16392     },
16393     
16394     listItemCheckbox : {
16395         tag: 'li',
16396         cls: 'list-group-item',
16397         cn: [
16398             {
16399                 tag: 'span',
16400                 cls: 'roo-combobox-list-group-item-value'
16401             },
16402             {
16403                 tag: 'div',
16404                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16405                 cn: [
16406                     {
16407                         tag: 'input',
16408                         type: 'checkbox'
16409                     },
16410                     {
16411                         tag: 'label'
16412                     }
16413                 ]
16414             }
16415         ]
16416     },
16417     
16418     emptyResult : {
16419         tag: 'div',
16420         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16421     },
16422     
16423     footer : {
16424         tag: 'div',
16425         cls: 'modal-footer',
16426         cn: [
16427             {
16428                 tag: 'div',
16429                 cls: 'row',
16430                 cn: [
16431                     {
16432                         tag: 'div',
16433                         cls: 'col-xs-6 text-left',
16434                         cn: {
16435                             tag: 'button',
16436                             cls: 'btn btn-danger roo-touch-view-cancel',
16437                             html: 'Cancel'
16438                         }
16439                     },
16440                     {
16441                         tag: 'div',
16442                         cls: 'col-xs-6 text-right',
16443                         cn: {
16444                             tag: 'button',
16445                             cls: 'btn btn-success roo-touch-view-ok',
16446                             html: 'OK'
16447                         }
16448                     }
16449                 ]
16450             }
16451         ]
16452         
16453     }
16454 });
16455
16456 Roo.apply(Roo.bootstrap.ComboBox,  {
16457     
16458     touchViewTemplate : {
16459         tag: 'div',
16460         cls: 'modal fade roo-combobox-touch-view',
16461         cn: [
16462             {
16463                 tag: 'div',
16464                 cls: 'modal-dialog',
16465                 style : 'position:fixed', // we have to fix position....
16466                 cn: [
16467                     {
16468                         tag: 'div',
16469                         cls: 'modal-content',
16470                         cn: [
16471                             Roo.bootstrap.ComboBox.header,
16472                             Roo.bootstrap.ComboBox.body,
16473                             Roo.bootstrap.ComboBox.footer
16474                         ]
16475                     }
16476                 ]
16477             }
16478         ]
16479     }
16480 });/*
16481  * Based on:
16482  * Ext JS Library 1.1.1
16483  * Copyright(c) 2006-2007, Ext JS, LLC.
16484  *
16485  * Originally Released Under LGPL - original licence link has changed is not relivant.
16486  *
16487  * Fork - LGPL
16488  * <script type="text/javascript">
16489  */
16490
16491 /**
16492  * @class Roo.View
16493  * @extends Roo.util.Observable
16494  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16495  * This class also supports single and multi selection modes. <br>
16496  * Create a data model bound view:
16497  <pre><code>
16498  var store = new Roo.data.Store(...);
16499
16500  var view = new Roo.View({
16501     el : "my-element",
16502     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16503  
16504     singleSelect: true,
16505     selectedClass: "ydataview-selected",
16506     store: store
16507  });
16508
16509  // listen for node click?
16510  view.on("click", function(vw, index, node, e){
16511  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16512  });
16513
16514  // load XML data
16515  dataModel.load("foobar.xml");
16516  </code></pre>
16517  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16518  * <br><br>
16519  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16520  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16521  * 
16522  * Note: old style constructor is still suported (container, template, config)
16523  * 
16524  * @constructor
16525  * Create a new View
16526  * @param {Object} config The config object
16527  * 
16528  */
16529 Roo.View = function(config, depreciated_tpl, depreciated_config){
16530     
16531     this.parent = false;
16532     
16533     if (typeof(depreciated_tpl) == 'undefined') {
16534         // new way.. - universal constructor.
16535         Roo.apply(this, config);
16536         this.el  = Roo.get(this.el);
16537     } else {
16538         // old format..
16539         this.el  = Roo.get(config);
16540         this.tpl = depreciated_tpl;
16541         Roo.apply(this, depreciated_config);
16542     }
16543     this.wrapEl  = this.el.wrap().wrap();
16544     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16545     
16546     
16547     if(typeof(this.tpl) == "string"){
16548         this.tpl = new Roo.Template(this.tpl);
16549     } else {
16550         // support xtype ctors..
16551         this.tpl = new Roo.factory(this.tpl, Roo);
16552     }
16553     
16554     
16555     this.tpl.compile();
16556     
16557     /** @private */
16558     this.addEvents({
16559         /**
16560          * @event beforeclick
16561          * Fires before a click is processed. Returns false to cancel the default action.
16562          * @param {Roo.View} this
16563          * @param {Number} index The index of the target node
16564          * @param {HTMLElement} node The target node
16565          * @param {Roo.EventObject} e The raw event object
16566          */
16567             "beforeclick" : true,
16568         /**
16569          * @event click
16570          * Fires when a template node is clicked.
16571          * @param {Roo.View} this
16572          * @param {Number} index The index of the target node
16573          * @param {HTMLElement} node The target node
16574          * @param {Roo.EventObject} e The raw event object
16575          */
16576             "click" : true,
16577         /**
16578          * @event dblclick
16579          * Fires when a template node is double clicked.
16580          * @param {Roo.View} this
16581          * @param {Number} index The index of the target node
16582          * @param {HTMLElement} node The target node
16583          * @param {Roo.EventObject} e The raw event object
16584          */
16585             "dblclick" : true,
16586         /**
16587          * @event contextmenu
16588          * Fires when a template node is right clicked.
16589          * @param {Roo.View} this
16590          * @param {Number} index The index of the target node
16591          * @param {HTMLElement} node The target node
16592          * @param {Roo.EventObject} e The raw event object
16593          */
16594             "contextmenu" : true,
16595         /**
16596          * @event selectionchange
16597          * Fires when the selected nodes change.
16598          * @param {Roo.View} this
16599          * @param {Array} selections Array of the selected nodes
16600          */
16601             "selectionchange" : true,
16602     
16603         /**
16604          * @event beforeselect
16605          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16606          * @param {Roo.View} this
16607          * @param {HTMLElement} node The node to be selected
16608          * @param {Array} selections Array of currently selected nodes
16609          */
16610             "beforeselect" : true,
16611         /**
16612          * @event preparedata
16613          * Fires on every row to render, to allow you to change the data.
16614          * @param {Roo.View} this
16615          * @param {Object} data to be rendered (change this)
16616          */
16617           "preparedata" : true
16618           
16619           
16620         });
16621
16622
16623
16624     this.el.on({
16625         "click": this.onClick,
16626         "dblclick": this.onDblClick,
16627         "contextmenu": this.onContextMenu,
16628         scope:this
16629     });
16630
16631     this.selections = [];
16632     this.nodes = [];
16633     this.cmp = new Roo.CompositeElementLite([]);
16634     if(this.store){
16635         this.store = Roo.factory(this.store, Roo.data);
16636         this.setStore(this.store, true);
16637     }
16638     
16639     if ( this.footer && this.footer.xtype) {
16640            
16641          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16642         
16643         this.footer.dataSource = this.store;
16644         this.footer.container = fctr;
16645         this.footer = Roo.factory(this.footer, Roo);
16646         fctr.insertFirst(this.el);
16647         
16648         // this is a bit insane - as the paging toolbar seems to detach the el..
16649 //        dom.parentNode.parentNode.parentNode
16650          // they get detached?
16651     }
16652     
16653     
16654     Roo.View.superclass.constructor.call(this);
16655     
16656     
16657 };
16658
16659 Roo.extend(Roo.View, Roo.util.Observable, {
16660     
16661      /**
16662      * @cfg {Roo.data.Store} store Data store to load data from.
16663      */
16664     store : false,
16665     
16666     /**
16667      * @cfg {String|Roo.Element} el The container element.
16668      */
16669     el : '',
16670     
16671     /**
16672      * @cfg {String|Roo.Template} tpl The template used by this View 
16673      */
16674     tpl : false,
16675     /**
16676      * @cfg {String} dataName the named area of the template to use as the data area
16677      *                          Works with domtemplates roo-name="name"
16678      */
16679     dataName: false,
16680     /**
16681      * @cfg {String} selectedClass The css class to add to selected nodes
16682      */
16683     selectedClass : "x-view-selected",
16684      /**
16685      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16686      */
16687     emptyText : "",
16688     
16689     /**
16690      * @cfg {String} text to display on mask (default Loading)
16691      */
16692     mask : false,
16693     /**
16694      * @cfg {Boolean} multiSelect Allow multiple selection
16695      */
16696     multiSelect : false,
16697     /**
16698      * @cfg {Boolean} singleSelect Allow single selection
16699      */
16700     singleSelect:  false,
16701     
16702     /**
16703      * @cfg {Boolean} toggleSelect - selecting 
16704      */
16705     toggleSelect : false,
16706     
16707     /**
16708      * @cfg {Boolean} tickable - selecting 
16709      */
16710     tickable : false,
16711     
16712     /**
16713      * Returns the element this view is bound to.
16714      * @return {Roo.Element}
16715      */
16716     getEl : function(){
16717         return this.wrapEl;
16718     },
16719     
16720     
16721
16722     /**
16723      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16724      */
16725     refresh : function(){
16726         //Roo.log('refresh');
16727         var t = this.tpl;
16728         
16729         // if we are using something like 'domtemplate', then
16730         // the what gets used is:
16731         // t.applySubtemplate(NAME, data, wrapping data..)
16732         // the outer template then get' applied with
16733         //     the store 'extra data'
16734         // and the body get's added to the
16735         //      roo-name="data" node?
16736         //      <span class='roo-tpl-{name}'></span> ?????
16737         
16738         
16739         
16740         this.clearSelections();
16741         this.el.update("");
16742         var html = [];
16743         var records = this.store.getRange();
16744         if(records.length < 1) {
16745             
16746             // is this valid??  = should it render a template??
16747             
16748             this.el.update(this.emptyText);
16749             return;
16750         }
16751         var el = this.el;
16752         if (this.dataName) {
16753             this.el.update(t.apply(this.store.meta)); //????
16754             el = this.el.child('.roo-tpl-' + this.dataName);
16755         }
16756         
16757         for(var i = 0, len = records.length; i < len; i++){
16758             var data = this.prepareData(records[i].data, i, records[i]);
16759             this.fireEvent("preparedata", this, data, i, records[i]);
16760             
16761             var d = Roo.apply({}, data);
16762             
16763             if(this.tickable){
16764                 Roo.apply(d, {'roo-id' : Roo.id()});
16765                 
16766                 var _this = this;
16767             
16768                 Roo.each(this.parent.item, function(item){
16769                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16770                         return;
16771                     }
16772                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16773                 });
16774             }
16775             
16776             html[html.length] = Roo.util.Format.trim(
16777                 this.dataName ?
16778                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16779                     t.apply(d)
16780             );
16781         }
16782         
16783         
16784         
16785         el.update(html.join(""));
16786         this.nodes = el.dom.childNodes;
16787         this.updateIndexes(0);
16788     },
16789     
16790
16791     /**
16792      * Function to override to reformat the data that is sent to
16793      * the template for each node.
16794      * DEPRICATED - use the preparedata event handler.
16795      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16796      * a JSON object for an UpdateManager bound view).
16797      */
16798     prepareData : function(data, index, record)
16799     {
16800         this.fireEvent("preparedata", this, data, index, record);
16801         return data;
16802     },
16803
16804     onUpdate : function(ds, record){
16805         // Roo.log('on update');   
16806         this.clearSelections();
16807         var index = this.store.indexOf(record);
16808         var n = this.nodes[index];
16809         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16810         n.parentNode.removeChild(n);
16811         this.updateIndexes(index, index);
16812     },
16813
16814     
16815     
16816 // --------- FIXME     
16817     onAdd : function(ds, records, index)
16818     {
16819         //Roo.log(['on Add', ds, records, index] );        
16820         this.clearSelections();
16821         if(this.nodes.length == 0){
16822             this.refresh();
16823             return;
16824         }
16825         var n = this.nodes[index];
16826         for(var i = 0, len = records.length; i < len; i++){
16827             var d = this.prepareData(records[i].data, i, records[i]);
16828             if(n){
16829                 this.tpl.insertBefore(n, d);
16830             }else{
16831                 
16832                 this.tpl.append(this.el, d);
16833             }
16834         }
16835         this.updateIndexes(index);
16836     },
16837
16838     onRemove : function(ds, record, index){
16839        // Roo.log('onRemove');
16840         this.clearSelections();
16841         var el = this.dataName  ?
16842             this.el.child('.roo-tpl-' + this.dataName) :
16843             this.el; 
16844         
16845         el.dom.removeChild(this.nodes[index]);
16846         this.updateIndexes(index);
16847     },
16848
16849     /**
16850      * Refresh an individual node.
16851      * @param {Number} index
16852      */
16853     refreshNode : function(index){
16854         this.onUpdate(this.store, this.store.getAt(index));
16855     },
16856
16857     updateIndexes : function(startIndex, endIndex){
16858         var ns = this.nodes;
16859         startIndex = startIndex || 0;
16860         endIndex = endIndex || ns.length - 1;
16861         for(var i = startIndex; i <= endIndex; i++){
16862             ns[i].nodeIndex = i;
16863         }
16864     },
16865
16866     /**
16867      * Changes the data store this view uses and refresh the view.
16868      * @param {Store} store
16869      */
16870     setStore : function(store, initial){
16871         if(!initial && this.store){
16872             this.store.un("datachanged", this.refresh);
16873             this.store.un("add", this.onAdd);
16874             this.store.un("remove", this.onRemove);
16875             this.store.un("update", this.onUpdate);
16876             this.store.un("clear", this.refresh);
16877             this.store.un("beforeload", this.onBeforeLoad);
16878             this.store.un("load", this.onLoad);
16879             this.store.un("loadexception", this.onLoad);
16880         }
16881         if(store){
16882           
16883             store.on("datachanged", this.refresh, this);
16884             store.on("add", this.onAdd, this);
16885             store.on("remove", this.onRemove, this);
16886             store.on("update", this.onUpdate, this);
16887             store.on("clear", this.refresh, this);
16888             store.on("beforeload", this.onBeforeLoad, this);
16889             store.on("load", this.onLoad, this);
16890             store.on("loadexception", this.onLoad, this);
16891         }
16892         
16893         if(store){
16894             this.refresh();
16895         }
16896     },
16897     /**
16898      * onbeforeLoad - masks the loading area.
16899      *
16900      */
16901     onBeforeLoad : function(store,opts)
16902     {
16903          //Roo.log('onBeforeLoad');   
16904         if (!opts.add) {
16905             this.el.update("");
16906         }
16907         this.el.mask(this.mask ? this.mask : "Loading" ); 
16908     },
16909     onLoad : function ()
16910     {
16911         this.el.unmask();
16912     },
16913     
16914
16915     /**
16916      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16917      * @param {HTMLElement} node
16918      * @return {HTMLElement} The template node
16919      */
16920     findItemFromChild : function(node){
16921         var el = this.dataName  ?
16922             this.el.child('.roo-tpl-' + this.dataName,true) :
16923             this.el.dom; 
16924         
16925         if(!node || node.parentNode == el){
16926                     return node;
16927             }
16928             var p = node.parentNode;
16929             while(p && p != el){
16930             if(p.parentNode == el){
16931                 return p;
16932             }
16933             p = p.parentNode;
16934         }
16935             return null;
16936     },
16937
16938     /** @ignore */
16939     onClick : function(e){
16940         var item = this.findItemFromChild(e.getTarget());
16941         if(item){
16942             var index = this.indexOf(item);
16943             if(this.onItemClick(item, index, e) !== false){
16944                 this.fireEvent("click", this, index, item, e);
16945             }
16946         }else{
16947             this.clearSelections();
16948         }
16949     },
16950
16951     /** @ignore */
16952     onContextMenu : function(e){
16953         var item = this.findItemFromChild(e.getTarget());
16954         if(item){
16955             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16956         }
16957     },
16958
16959     /** @ignore */
16960     onDblClick : function(e){
16961         var item = this.findItemFromChild(e.getTarget());
16962         if(item){
16963             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16964         }
16965     },
16966
16967     onItemClick : function(item, index, e)
16968     {
16969         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16970             return false;
16971         }
16972         if (this.toggleSelect) {
16973             var m = this.isSelected(item) ? 'unselect' : 'select';
16974             //Roo.log(m);
16975             var _t = this;
16976             _t[m](item, true, false);
16977             return true;
16978         }
16979         if(this.multiSelect || this.singleSelect){
16980             if(this.multiSelect && e.shiftKey && this.lastSelection){
16981                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16982             }else{
16983                 this.select(item, this.multiSelect && e.ctrlKey);
16984                 this.lastSelection = item;
16985             }
16986             
16987             if(!this.tickable){
16988                 e.preventDefault();
16989             }
16990             
16991         }
16992         return true;
16993     },
16994
16995     /**
16996      * Get the number of selected nodes.
16997      * @return {Number}
16998      */
16999     getSelectionCount : function(){
17000         return this.selections.length;
17001     },
17002
17003     /**
17004      * Get the currently selected nodes.
17005      * @return {Array} An array of HTMLElements
17006      */
17007     getSelectedNodes : function(){
17008         return this.selections;
17009     },
17010
17011     /**
17012      * Get the indexes of the selected nodes.
17013      * @return {Array}
17014      */
17015     getSelectedIndexes : function(){
17016         var indexes = [], s = this.selections;
17017         for(var i = 0, len = s.length; i < len; i++){
17018             indexes.push(s[i].nodeIndex);
17019         }
17020         return indexes;
17021     },
17022
17023     /**
17024      * Clear all selections
17025      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
17026      */
17027     clearSelections : function(suppressEvent){
17028         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
17029             this.cmp.elements = this.selections;
17030             this.cmp.removeClass(this.selectedClass);
17031             this.selections = [];
17032             if(!suppressEvent){
17033                 this.fireEvent("selectionchange", this, this.selections);
17034             }
17035         }
17036     },
17037
17038     /**
17039      * Returns true if the passed node is selected
17040      * @param {HTMLElement/Number} node The node or node index
17041      * @return {Boolean}
17042      */
17043     isSelected : function(node){
17044         var s = this.selections;
17045         if(s.length < 1){
17046             return false;
17047         }
17048         node = this.getNode(node);
17049         return s.indexOf(node) !== -1;
17050     },
17051
17052     /**
17053      * Selects nodes.
17054      * @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
17055      * @param {Boolean} keepExisting (optional) true to keep existing selections
17056      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17057      */
17058     select : function(nodeInfo, keepExisting, suppressEvent){
17059         if(nodeInfo instanceof Array){
17060             if(!keepExisting){
17061                 this.clearSelections(true);
17062             }
17063             for(var i = 0, len = nodeInfo.length; i < len; i++){
17064                 this.select(nodeInfo[i], true, true);
17065             }
17066             return;
17067         } 
17068         var node = this.getNode(nodeInfo);
17069         if(!node || this.isSelected(node)){
17070             return; // already selected.
17071         }
17072         if(!keepExisting){
17073             this.clearSelections(true);
17074         }
17075         
17076         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
17077             Roo.fly(node).addClass(this.selectedClass);
17078             this.selections.push(node);
17079             if(!suppressEvent){
17080                 this.fireEvent("selectionchange", this, this.selections);
17081             }
17082         }
17083         
17084         
17085     },
17086       /**
17087      * Unselects nodes.
17088      * @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
17089      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
17090      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17091      */
17092     unselect : function(nodeInfo, keepExisting, suppressEvent)
17093     {
17094         if(nodeInfo instanceof Array){
17095             Roo.each(this.selections, function(s) {
17096                 this.unselect(s, nodeInfo);
17097             }, this);
17098             return;
17099         }
17100         var node = this.getNode(nodeInfo);
17101         if(!node || !this.isSelected(node)){
17102             //Roo.log("not selected");
17103             return; // not selected.
17104         }
17105         // fireevent???
17106         var ns = [];
17107         Roo.each(this.selections, function(s) {
17108             if (s == node ) {
17109                 Roo.fly(node).removeClass(this.selectedClass);
17110
17111                 return;
17112             }
17113             ns.push(s);
17114         },this);
17115         
17116         this.selections= ns;
17117         this.fireEvent("selectionchange", this, this.selections);
17118     },
17119
17120     /**
17121      * Gets a template node.
17122      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17123      * @return {HTMLElement} The node or null if it wasn't found
17124      */
17125     getNode : function(nodeInfo){
17126         if(typeof nodeInfo == "string"){
17127             return document.getElementById(nodeInfo);
17128         }else if(typeof nodeInfo == "number"){
17129             return this.nodes[nodeInfo];
17130         }
17131         return nodeInfo;
17132     },
17133
17134     /**
17135      * Gets a range template nodes.
17136      * @param {Number} startIndex
17137      * @param {Number} endIndex
17138      * @return {Array} An array of nodes
17139      */
17140     getNodes : function(start, end){
17141         var ns = this.nodes;
17142         start = start || 0;
17143         end = typeof end == "undefined" ? ns.length - 1 : end;
17144         var nodes = [];
17145         if(start <= end){
17146             for(var i = start; i <= end; i++){
17147                 nodes.push(ns[i]);
17148             }
17149         } else{
17150             for(var i = start; i >= end; i--){
17151                 nodes.push(ns[i]);
17152             }
17153         }
17154         return nodes;
17155     },
17156
17157     /**
17158      * Finds the index of the passed node
17159      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17160      * @return {Number} The index of the node or -1
17161      */
17162     indexOf : function(node){
17163         node = this.getNode(node);
17164         if(typeof node.nodeIndex == "number"){
17165             return node.nodeIndex;
17166         }
17167         var ns = this.nodes;
17168         for(var i = 0, len = ns.length; i < len; i++){
17169             if(ns[i] == node){
17170                 return i;
17171             }
17172         }
17173         return -1;
17174     }
17175 });
17176 /*
17177  * - LGPL
17178  *
17179  * based on jquery fullcalendar
17180  * 
17181  */
17182
17183 Roo.bootstrap = Roo.bootstrap || {};
17184 /**
17185  * @class Roo.bootstrap.Calendar
17186  * @extends Roo.bootstrap.Component
17187  * Bootstrap Calendar class
17188  * @cfg {Boolean} loadMask (true|false) default false
17189  * @cfg {Object} header generate the user specific header of the calendar, default false
17190
17191  * @constructor
17192  * Create a new Container
17193  * @param {Object} config The config object
17194  */
17195
17196
17197
17198 Roo.bootstrap.Calendar = function(config){
17199     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
17200      this.addEvents({
17201         /**
17202              * @event select
17203              * Fires when a date is selected
17204              * @param {DatePicker} this
17205              * @param {Date} date The selected date
17206              */
17207         'select': true,
17208         /**
17209              * @event monthchange
17210              * Fires when the displayed month changes 
17211              * @param {DatePicker} this
17212              * @param {Date} date The selected month
17213              */
17214         'monthchange': true,
17215         /**
17216              * @event evententer
17217              * Fires when mouse over an event
17218              * @param {Calendar} this
17219              * @param {event} Event
17220              */
17221         'evententer': true,
17222         /**
17223              * @event eventleave
17224              * Fires when the mouse leaves an
17225              * @param {Calendar} this
17226              * @param {event}
17227              */
17228         'eventleave': true,
17229         /**
17230              * @event eventclick
17231              * Fires when the mouse click an
17232              * @param {Calendar} this
17233              * @param {event}
17234              */
17235         'eventclick': true
17236         
17237     });
17238
17239 };
17240
17241 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
17242     
17243      /**
17244      * @cfg {Number} startDay
17245      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
17246      */
17247     startDay : 0,
17248     
17249     loadMask : false,
17250     
17251     header : false,
17252       
17253     getAutoCreate : function(){
17254         
17255         
17256         var fc_button = function(name, corner, style, content ) {
17257             return Roo.apply({},{
17258                 tag : 'span',
17259                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
17260                          (corner.length ?
17261                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
17262                             ''
17263                         ),
17264                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
17265                 unselectable: 'on'
17266             });
17267         };
17268         
17269         var header = {};
17270         
17271         if(!this.header){
17272             header = {
17273                 tag : 'table',
17274                 cls : 'fc-header',
17275                 style : 'width:100%',
17276                 cn : [
17277                     {
17278                         tag: 'tr',
17279                         cn : [
17280                             {
17281                                 tag : 'td',
17282                                 cls : 'fc-header-left',
17283                                 cn : [
17284                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
17285                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
17286                                     { tag: 'span', cls: 'fc-header-space' },
17287                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
17288
17289
17290                                 ]
17291                             },
17292
17293                             {
17294                                 tag : 'td',
17295                                 cls : 'fc-header-center',
17296                                 cn : [
17297                                     {
17298                                         tag: 'span',
17299                                         cls: 'fc-header-title',
17300                                         cn : {
17301                                             tag: 'H2',
17302                                             html : 'month / year'
17303                                         }
17304                                     }
17305
17306                                 ]
17307                             },
17308                             {
17309                                 tag : 'td',
17310                                 cls : 'fc-header-right',
17311                                 cn : [
17312                               /*      fc_button('month', 'left', '', 'month' ),
17313                                     fc_button('week', '', '', 'week' ),
17314                                     fc_button('day', 'right', '', 'day' )
17315                                 */    
17316
17317                                 ]
17318                             }
17319
17320                         ]
17321                     }
17322                 ]
17323             };
17324         }
17325         
17326         header = this.header;
17327         
17328        
17329         var cal_heads = function() {
17330             var ret = [];
17331             // fixme - handle this.
17332             
17333             for (var i =0; i < Date.dayNames.length; i++) {
17334                 var d = Date.dayNames[i];
17335                 ret.push({
17336                     tag: 'th',
17337                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17338                     html : d.substring(0,3)
17339                 });
17340                 
17341             }
17342             ret[0].cls += ' fc-first';
17343             ret[6].cls += ' fc-last';
17344             return ret;
17345         };
17346         var cal_cell = function(n) {
17347             return  {
17348                 tag: 'td',
17349                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17350                 cn : [
17351                     {
17352                         cn : [
17353                             {
17354                                 cls: 'fc-day-number',
17355                                 html: 'D'
17356                             },
17357                             {
17358                                 cls: 'fc-day-content',
17359                              
17360                                 cn : [
17361                                      {
17362                                         style: 'position: relative;' // height: 17px;
17363                                     }
17364                                 ]
17365                             }
17366                             
17367                             
17368                         ]
17369                     }
17370                 ]
17371                 
17372             }
17373         };
17374         var cal_rows = function() {
17375             
17376             var ret = [];
17377             for (var r = 0; r < 6; r++) {
17378                 var row= {
17379                     tag : 'tr',
17380                     cls : 'fc-week',
17381                     cn : []
17382                 };
17383                 
17384                 for (var i =0; i < Date.dayNames.length; i++) {
17385                     var d = Date.dayNames[i];
17386                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17387
17388                 }
17389                 row.cn[0].cls+=' fc-first';
17390                 row.cn[0].cn[0].style = 'min-height:90px';
17391                 row.cn[6].cls+=' fc-last';
17392                 ret.push(row);
17393                 
17394             }
17395             ret[0].cls += ' fc-first';
17396             ret[4].cls += ' fc-prev-last';
17397             ret[5].cls += ' fc-last';
17398             return ret;
17399             
17400         };
17401         
17402         var cal_table = {
17403             tag: 'table',
17404             cls: 'fc-border-separate',
17405             style : 'width:100%',
17406             cellspacing  : 0,
17407             cn : [
17408                 { 
17409                     tag: 'thead',
17410                     cn : [
17411                         { 
17412                             tag: 'tr',
17413                             cls : 'fc-first fc-last',
17414                             cn : cal_heads()
17415                         }
17416                     ]
17417                 },
17418                 { 
17419                     tag: 'tbody',
17420                     cn : cal_rows()
17421                 }
17422                   
17423             ]
17424         };
17425          
17426          var cfg = {
17427             cls : 'fc fc-ltr',
17428             cn : [
17429                 header,
17430                 {
17431                     cls : 'fc-content',
17432                     style : "position: relative;",
17433                     cn : [
17434                         {
17435                             cls : 'fc-view fc-view-month fc-grid',
17436                             style : 'position: relative',
17437                             unselectable : 'on',
17438                             cn : [
17439                                 {
17440                                     cls : 'fc-event-container',
17441                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17442                                 },
17443                                 cal_table
17444                             ]
17445                         }
17446                     ]
17447     
17448                 }
17449            ] 
17450             
17451         };
17452         
17453          
17454         
17455         return cfg;
17456     },
17457     
17458     
17459     initEvents : function()
17460     {
17461         if(!this.store){
17462             throw "can not find store for calendar";
17463         }
17464         
17465         var mark = {
17466             tag: "div",
17467             cls:"x-dlg-mask",
17468             style: "text-align:center",
17469             cn: [
17470                 {
17471                     tag: "div",
17472                     style: "background-color:white;width:50%;margin:250 auto",
17473                     cn: [
17474                         {
17475                             tag: "img",
17476                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17477                         },
17478                         {
17479                             tag: "span",
17480                             html: "Loading"
17481                         }
17482                         
17483                     ]
17484                 }
17485             ]
17486         };
17487         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17488         
17489         var size = this.el.select('.fc-content', true).first().getSize();
17490         this.maskEl.setSize(size.width, size.height);
17491         this.maskEl.enableDisplayMode("block");
17492         if(!this.loadMask){
17493             this.maskEl.hide();
17494         }
17495         
17496         this.store = Roo.factory(this.store, Roo.data);
17497         this.store.on('load', this.onLoad, this);
17498         this.store.on('beforeload', this.onBeforeLoad, this);
17499         
17500         this.resize();
17501         
17502         this.cells = this.el.select('.fc-day',true);
17503         //Roo.log(this.cells);
17504         this.textNodes = this.el.query('.fc-day-number');
17505         this.cells.addClassOnOver('fc-state-hover');
17506         
17507         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17508         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17509         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17510         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17511         
17512         this.on('monthchange', this.onMonthChange, this);
17513         
17514         this.update(new Date().clearTime());
17515     },
17516     
17517     resize : function() {
17518         var sz  = this.el.getSize();
17519         
17520         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17521         this.el.select('.fc-day-content div',true).setHeight(34);
17522     },
17523     
17524     
17525     // private
17526     showPrevMonth : function(e){
17527         this.update(this.activeDate.add("mo", -1));
17528     },
17529     showToday : function(e){
17530         this.update(new Date().clearTime());
17531     },
17532     // private
17533     showNextMonth : function(e){
17534         this.update(this.activeDate.add("mo", 1));
17535     },
17536
17537     // private
17538     showPrevYear : function(){
17539         this.update(this.activeDate.add("y", -1));
17540     },
17541
17542     // private
17543     showNextYear : function(){
17544         this.update(this.activeDate.add("y", 1));
17545     },
17546
17547     
17548    // private
17549     update : function(date)
17550     {
17551         var vd = this.activeDate;
17552         this.activeDate = date;
17553 //        if(vd && this.el){
17554 //            var t = date.getTime();
17555 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17556 //                Roo.log('using add remove');
17557 //                
17558 //                this.fireEvent('monthchange', this, date);
17559 //                
17560 //                this.cells.removeClass("fc-state-highlight");
17561 //                this.cells.each(function(c){
17562 //                   if(c.dateValue == t){
17563 //                       c.addClass("fc-state-highlight");
17564 //                       setTimeout(function(){
17565 //                            try{c.dom.firstChild.focus();}catch(e){}
17566 //                       }, 50);
17567 //                       return false;
17568 //                   }
17569 //                   return true;
17570 //                });
17571 //                return;
17572 //            }
17573 //        }
17574         
17575         var days = date.getDaysInMonth();
17576         
17577         var firstOfMonth = date.getFirstDateOfMonth();
17578         var startingPos = firstOfMonth.getDay()-this.startDay;
17579         
17580         if(startingPos < this.startDay){
17581             startingPos += 7;
17582         }
17583         
17584         var pm = date.add(Date.MONTH, -1);
17585         var prevStart = pm.getDaysInMonth()-startingPos;
17586 //        
17587         this.cells = this.el.select('.fc-day',true);
17588         this.textNodes = this.el.query('.fc-day-number');
17589         this.cells.addClassOnOver('fc-state-hover');
17590         
17591         var cells = this.cells.elements;
17592         var textEls = this.textNodes;
17593         
17594         Roo.each(cells, function(cell){
17595             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17596         });
17597         
17598         days += startingPos;
17599
17600         // convert everything to numbers so it's fast
17601         var day = 86400000;
17602         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17603         //Roo.log(d);
17604         //Roo.log(pm);
17605         //Roo.log(prevStart);
17606         
17607         var today = new Date().clearTime().getTime();
17608         var sel = date.clearTime().getTime();
17609         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17610         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17611         var ddMatch = this.disabledDatesRE;
17612         var ddText = this.disabledDatesText;
17613         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17614         var ddaysText = this.disabledDaysText;
17615         var format = this.format;
17616         
17617         var setCellClass = function(cal, cell){
17618             cell.row = 0;
17619             cell.events = [];
17620             cell.more = [];
17621             //Roo.log('set Cell Class');
17622             cell.title = "";
17623             var t = d.getTime();
17624             
17625             //Roo.log(d);
17626             
17627             cell.dateValue = t;
17628             if(t == today){
17629                 cell.className += " fc-today";
17630                 cell.className += " fc-state-highlight";
17631                 cell.title = cal.todayText;
17632             }
17633             if(t == sel){
17634                 // disable highlight in other month..
17635                 //cell.className += " fc-state-highlight";
17636                 
17637             }
17638             // disabling
17639             if(t < min) {
17640                 cell.className = " fc-state-disabled";
17641                 cell.title = cal.minText;
17642                 return;
17643             }
17644             if(t > max) {
17645                 cell.className = " fc-state-disabled";
17646                 cell.title = cal.maxText;
17647                 return;
17648             }
17649             if(ddays){
17650                 if(ddays.indexOf(d.getDay()) != -1){
17651                     cell.title = ddaysText;
17652                     cell.className = " fc-state-disabled";
17653                 }
17654             }
17655             if(ddMatch && format){
17656                 var fvalue = d.dateFormat(format);
17657                 if(ddMatch.test(fvalue)){
17658                     cell.title = ddText.replace("%0", fvalue);
17659                     cell.className = " fc-state-disabled";
17660                 }
17661             }
17662             
17663             if (!cell.initialClassName) {
17664                 cell.initialClassName = cell.dom.className;
17665             }
17666             
17667             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17668         };
17669
17670         var i = 0;
17671         
17672         for(; i < startingPos; i++) {
17673             textEls[i].innerHTML = (++prevStart);
17674             d.setDate(d.getDate()+1);
17675             
17676             cells[i].className = "fc-past fc-other-month";
17677             setCellClass(this, cells[i]);
17678         }
17679         
17680         var intDay = 0;
17681         
17682         for(; i < days; i++){
17683             intDay = i - startingPos + 1;
17684             textEls[i].innerHTML = (intDay);
17685             d.setDate(d.getDate()+1);
17686             
17687             cells[i].className = ''; // "x-date-active";
17688             setCellClass(this, cells[i]);
17689         }
17690         var extraDays = 0;
17691         
17692         for(; i < 42; i++) {
17693             textEls[i].innerHTML = (++extraDays);
17694             d.setDate(d.getDate()+1);
17695             
17696             cells[i].className = "fc-future fc-other-month";
17697             setCellClass(this, cells[i]);
17698         }
17699         
17700         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17701         
17702         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17703         
17704         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17705         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17706         
17707         if(totalRows != 6){
17708             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17709             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17710         }
17711         
17712         this.fireEvent('monthchange', this, date);
17713         
17714         
17715         /*
17716         if(!this.internalRender){
17717             var main = this.el.dom.firstChild;
17718             var w = main.offsetWidth;
17719             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17720             Roo.fly(main).setWidth(w);
17721             this.internalRender = true;
17722             // opera does not respect the auto grow header center column
17723             // then, after it gets a width opera refuses to recalculate
17724             // without a second pass
17725             if(Roo.isOpera && !this.secondPass){
17726                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17727                 this.secondPass = true;
17728                 this.update.defer(10, this, [date]);
17729             }
17730         }
17731         */
17732         
17733     },
17734     
17735     findCell : function(dt) {
17736         dt = dt.clearTime().getTime();
17737         var ret = false;
17738         this.cells.each(function(c){
17739             //Roo.log("check " +c.dateValue + '?=' + dt);
17740             if(c.dateValue == dt){
17741                 ret = c;
17742                 return false;
17743             }
17744             return true;
17745         });
17746         
17747         return ret;
17748     },
17749     
17750     findCells : function(ev) {
17751         var s = ev.start.clone().clearTime().getTime();
17752        // Roo.log(s);
17753         var e= ev.end.clone().clearTime().getTime();
17754        // Roo.log(e);
17755         var ret = [];
17756         this.cells.each(function(c){
17757              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17758             
17759             if(c.dateValue > e){
17760                 return ;
17761             }
17762             if(c.dateValue < s){
17763                 return ;
17764             }
17765             ret.push(c);
17766         });
17767         
17768         return ret;    
17769     },
17770     
17771 //    findBestRow: function(cells)
17772 //    {
17773 //        var ret = 0;
17774 //        
17775 //        for (var i =0 ; i < cells.length;i++) {
17776 //            ret  = Math.max(cells[i].rows || 0,ret);
17777 //        }
17778 //        return ret;
17779 //        
17780 //    },
17781     
17782     
17783     addItem : function(ev)
17784     {
17785         // look for vertical location slot in
17786         var cells = this.findCells(ev);
17787         
17788 //        ev.row = this.findBestRow(cells);
17789         
17790         // work out the location.
17791         
17792         var crow = false;
17793         var rows = [];
17794         for(var i =0; i < cells.length; i++) {
17795             
17796             cells[i].row = cells[0].row;
17797             
17798             if(i == 0){
17799                 cells[i].row = cells[i].row + 1;
17800             }
17801             
17802             if (!crow) {
17803                 crow = {
17804                     start : cells[i],
17805                     end :  cells[i]
17806                 };
17807                 continue;
17808             }
17809             if (crow.start.getY() == cells[i].getY()) {
17810                 // on same row.
17811                 crow.end = cells[i];
17812                 continue;
17813             }
17814             // different row.
17815             rows.push(crow);
17816             crow = {
17817                 start: cells[i],
17818                 end : cells[i]
17819             };
17820             
17821         }
17822         
17823         rows.push(crow);
17824         ev.els = [];
17825         ev.rows = rows;
17826         ev.cells = cells;
17827         
17828         cells[0].events.push(ev);
17829         
17830         this.calevents.push(ev);
17831     },
17832     
17833     clearEvents: function() {
17834         
17835         if(!this.calevents){
17836             return;
17837         }
17838         
17839         Roo.each(this.cells.elements, function(c){
17840             c.row = 0;
17841             c.events = [];
17842             c.more = [];
17843         });
17844         
17845         Roo.each(this.calevents, function(e) {
17846             Roo.each(e.els, function(el) {
17847                 el.un('mouseenter' ,this.onEventEnter, this);
17848                 el.un('mouseleave' ,this.onEventLeave, this);
17849                 el.remove();
17850             },this);
17851         },this);
17852         
17853         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17854             e.remove();
17855         });
17856         
17857     },
17858     
17859     renderEvents: function()
17860     {   
17861         var _this = this;
17862         
17863         this.cells.each(function(c) {
17864             
17865             if(c.row < 5){
17866                 return;
17867             }
17868             
17869             var ev = c.events;
17870             
17871             var r = 4;
17872             if(c.row != c.events.length){
17873                 r = 4 - (4 - (c.row - c.events.length));
17874             }
17875             
17876             c.events = ev.slice(0, r);
17877             c.more = ev.slice(r);
17878             
17879             if(c.more.length && c.more.length == 1){
17880                 c.events.push(c.more.pop());
17881             }
17882             
17883             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17884             
17885         });
17886             
17887         this.cells.each(function(c) {
17888             
17889             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17890             
17891             
17892             for (var e = 0; e < c.events.length; e++){
17893                 var ev = c.events[e];
17894                 var rows = ev.rows;
17895                 
17896                 for(var i = 0; i < rows.length; i++) {
17897                 
17898                     // how many rows should it span..
17899
17900                     var  cfg = {
17901                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17902                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17903
17904                         unselectable : "on",
17905                         cn : [
17906                             {
17907                                 cls: 'fc-event-inner',
17908                                 cn : [
17909     //                                {
17910     //                                  tag:'span',
17911     //                                  cls: 'fc-event-time',
17912     //                                  html : cells.length > 1 ? '' : ev.time
17913     //                                },
17914                                     {
17915                                       tag:'span',
17916                                       cls: 'fc-event-title',
17917                                       html : String.format('{0}', ev.title)
17918                                     }
17919
17920
17921                                 ]
17922                             },
17923                             {
17924                                 cls: 'ui-resizable-handle ui-resizable-e',
17925                                 html : '&nbsp;&nbsp;&nbsp'
17926                             }
17927
17928                         ]
17929                     };
17930
17931                     if (i == 0) {
17932                         cfg.cls += ' fc-event-start';
17933                     }
17934                     if ((i+1) == rows.length) {
17935                         cfg.cls += ' fc-event-end';
17936                     }
17937
17938                     var ctr = _this.el.select('.fc-event-container',true).first();
17939                     var cg = ctr.createChild(cfg);
17940
17941                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17942                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17943
17944                     var r = (c.more.length) ? 1 : 0;
17945                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17946                     cg.setWidth(ebox.right - sbox.x -2);
17947
17948                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17949                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17950                     cg.on('click', _this.onEventClick, _this, ev);
17951
17952                     ev.els.push(cg);
17953                     
17954                 }
17955                 
17956             }
17957             
17958             
17959             if(c.more.length){
17960                 var  cfg = {
17961                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17962                     style : 'position: absolute',
17963                     unselectable : "on",
17964                     cn : [
17965                         {
17966                             cls: 'fc-event-inner',
17967                             cn : [
17968                                 {
17969                                   tag:'span',
17970                                   cls: 'fc-event-title',
17971                                   html : 'More'
17972                                 }
17973
17974
17975                             ]
17976                         },
17977                         {
17978                             cls: 'ui-resizable-handle ui-resizable-e',
17979                             html : '&nbsp;&nbsp;&nbsp'
17980                         }
17981
17982                     ]
17983                 };
17984
17985                 var ctr = _this.el.select('.fc-event-container',true).first();
17986                 var cg = ctr.createChild(cfg);
17987
17988                 var sbox = c.select('.fc-day-content',true).first().getBox();
17989                 var ebox = c.select('.fc-day-content',true).first().getBox();
17990                 //Roo.log(cg);
17991                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17992                 cg.setWidth(ebox.right - sbox.x -2);
17993
17994                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17995                 
17996             }
17997             
17998         });
17999         
18000         
18001         
18002     },
18003     
18004     onEventEnter: function (e, el,event,d) {
18005         this.fireEvent('evententer', this, el, event);
18006     },
18007     
18008     onEventLeave: function (e, el,event,d) {
18009         this.fireEvent('eventleave', this, el, event);
18010     },
18011     
18012     onEventClick: function (e, el,event,d) {
18013         this.fireEvent('eventclick', this, el, event);
18014     },
18015     
18016     onMonthChange: function () {
18017         this.store.load();
18018     },
18019     
18020     onMoreEventClick: function(e, el, more)
18021     {
18022         var _this = this;
18023         
18024         this.calpopover.placement = 'right';
18025         this.calpopover.setTitle('More');
18026         
18027         this.calpopover.setContent('');
18028         
18029         var ctr = this.calpopover.el.select('.popover-content', true).first();
18030         
18031         Roo.each(more, function(m){
18032             var cfg = {
18033                 cls : 'fc-event-hori fc-event-draggable',
18034                 html : m.title
18035             };
18036             var cg = ctr.createChild(cfg);
18037             
18038             cg.on('click', _this.onEventClick, _this, m);
18039         });
18040         
18041         this.calpopover.show(el);
18042         
18043         
18044     },
18045     
18046     onLoad: function () 
18047     {   
18048         this.calevents = [];
18049         var cal = this;
18050         
18051         if(this.store.getCount() > 0){
18052             this.store.data.each(function(d){
18053                cal.addItem({
18054                     id : d.data.id,
18055                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
18056                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
18057                     time : d.data.start_time,
18058                     title : d.data.title,
18059                     description : d.data.description,
18060                     venue : d.data.venue
18061                 });
18062             });
18063         }
18064         
18065         this.renderEvents();
18066         
18067         if(this.calevents.length && this.loadMask){
18068             this.maskEl.hide();
18069         }
18070     },
18071     
18072     onBeforeLoad: function()
18073     {
18074         this.clearEvents();
18075         if(this.loadMask){
18076             this.maskEl.show();
18077         }
18078     }
18079 });
18080
18081  
18082  /*
18083  * - LGPL
18084  *
18085  * element
18086  * 
18087  */
18088
18089 /**
18090  * @class Roo.bootstrap.Popover
18091  * @extends Roo.bootstrap.Component
18092  * Bootstrap Popover class
18093  * @cfg {String} html contents of the popover   (or false to use children..)
18094  * @cfg {String} title of popover (or false to hide)
18095  * @cfg {String} placement how it is placed
18096  * @cfg {String} trigger click || hover (or false to trigger manually)
18097  * @cfg {String} over what (parent or false to trigger manually.)
18098  * @cfg {Number} delay - delay before showing
18099  
18100  * @constructor
18101  * Create a new Popover
18102  * @param {Object} config The config object
18103  */
18104
18105 Roo.bootstrap.Popover = function(config){
18106     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
18107     
18108     this.addEvents({
18109         // raw events
18110          /**
18111          * @event show
18112          * After the popover show
18113          * 
18114          * @param {Roo.bootstrap.Popover} this
18115          */
18116         "show" : true,
18117         /**
18118          * @event hide
18119          * After the popover hide
18120          * 
18121          * @param {Roo.bootstrap.Popover} this
18122          */
18123         "hide" : true
18124     });
18125 };
18126
18127 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
18128     
18129     title: 'Fill in a title',
18130     html: false,
18131     
18132     placement : 'right',
18133     trigger : 'hover', // hover
18134     
18135     delay : 0,
18136     
18137     over: 'parent',
18138     
18139     can_build_overlaid : false,
18140     
18141     getChildContainer : function()
18142     {
18143         return this.el.select('.popover-content',true).first();
18144     },
18145     
18146     getAutoCreate : function(){
18147          
18148         var cfg = {
18149            cls : 'popover roo-dynamic',
18150            style: 'display:block',
18151            cn : [
18152                 {
18153                     cls : 'arrow'
18154                 },
18155                 {
18156                     cls : 'popover-inner',
18157                     cn : [
18158                         {
18159                             tag: 'h3',
18160                             cls: 'popover-title popover-header',
18161                             html : this.title
18162                         },
18163                         {
18164                             cls : 'popover-content popover-body',
18165                             html : this.html
18166                         }
18167                     ]
18168                     
18169                 }
18170            ]
18171         };
18172         
18173         return cfg;
18174     },
18175     setTitle: function(str)
18176     {
18177         this.title = str;
18178         this.el.select('.popover-title',true).first().dom.innerHTML = str;
18179     },
18180     setContent: function(str)
18181     {
18182         this.html = str;
18183         this.el.select('.popover-content',true).first().dom.innerHTML = str;
18184     },
18185     // as it get's added to the bottom of the page.
18186     onRender : function(ct, position)
18187     {
18188         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
18189         if(!this.el){
18190             var cfg = Roo.apply({},  this.getAutoCreate());
18191             cfg.id = Roo.id();
18192             
18193             if (this.cls) {
18194                 cfg.cls += ' ' + this.cls;
18195             }
18196             if (this.style) {
18197                 cfg.style = this.style;
18198             }
18199             //Roo.log("adding to ");
18200             this.el = Roo.get(document.body).createChild(cfg, position);
18201 //            Roo.log(this.el);
18202         }
18203         this.initEvents();
18204     },
18205     
18206     initEvents : function()
18207     {
18208         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
18209         this.el.enableDisplayMode('block');
18210         this.el.hide();
18211         if (this.over === false) {
18212             return; 
18213         }
18214         if (this.triggers === false) {
18215             return;
18216         }
18217         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18218         var triggers = this.trigger ? this.trigger.split(' ') : [];
18219         Roo.each(triggers, function(trigger) {
18220         
18221             if (trigger == 'click') {
18222                 on_el.on('click', this.toggle, this);
18223             } else if (trigger != 'manual') {
18224                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
18225                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
18226       
18227                 on_el.on(eventIn  ,this.enter, this);
18228                 on_el.on(eventOut, this.leave, this);
18229             }
18230         }, this);
18231         
18232     },
18233     
18234     
18235     // private
18236     timeout : null,
18237     hoverState : null,
18238     
18239     toggle : function () {
18240         this.hoverState == 'in' ? this.leave() : this.enter();
18241     },
18242     
18243     enter : function () {
18244         
18245         clearTimeout(this.timeout);
18246     
18247         this.hoverState = 'in';
18248     
18249         if (!this.delay || !this.delay.show) {
18250             this.show();
18251             return;
18252         }
18253         var _t = this;
18254         this.timeout = setTimeout(function () {
18255             if (_t.hoverState == 'in') {
18256                 _t.show();
18257             }
18258         }, this.delay.show)
18259     },
18260     
18261     leave : function() {
18262         clearTimeout(this.timeout);
18263     
18264         this.hoverState = 'out';
18265     
18266         if (!this.delay || !this.delay.hide) {
18267             this.hide();
18268             return;
18269         }
18270         var _t = this;
18271         this.timeout = setTimeout(function () {
18272             if (_t.hoverState == 'out') {
18273                 _t.hide();
18274             }
18275         }, this.delay.hide)
18276     },
18277     
18278     show : function (on_el)
18279     {
18280         if (!on_el) {
18281             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18282         }
18283         
18284         // set content.
18285         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
18286         if (this.html !== false) {
18287             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18288         }
18289         this.el.removeClass([
18290             'fade','top','bottom', 'left', 'right','in',
18291             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18292         ]);
18293         if (!this.title.length) {
18294             this.el.select('.popover-title',true).hide();
18295         }
18296         
18297         var placement = typeof this.placement == 'function' ?
18298             this.placement.call(this, this.el, on_el) :
18299             this.placement;
18300             
18301         var autoToken = /\s?auto?\s?/i;
18302         var autoPlace = autoToken.test(placement);
18303         if (autoPlace) {
18304             placement = placement.replace(autoToken, '') || 'top';
18305         }
18306         
18307         //this.el.detach()
18308         //this.el.setXY([0,0]);
18309         this.el.show();
18310         this.el.dom.style.display='block';
18311         this.el.addClass(placement);
18312         
18313         //this.el.appendTo(on_el);
18314         
18315         var p = this.getPosition();
18316         var box = this.el.getBox();
18317         
18318         if (autoPlace) {
18319             // fixme..
18320         }
18321         var align = Roo.bootstrap.Popover.alignment[placement];
18322         
18323 //        Roo.log(align);
18324         this.el.alignTo(on_el, align[0],align[1]);
18325         //var arrow = this.el.select('.arrow',true).first();
18326         //arrow.set(align[2], 
18327         
18328         this.el.addClass('in');
18329         
18330         
18331         if (this.el.hasClass('fade')) {
18332             // fade it?
18333         }
18334         
18335         this.hoverState = 'in';
18336         
18337         this.fireEvent('show', this);
18338         
18339     },
18340     hide : function()
18341     {
18342         this.el.setXY([0,0]);
18343         this.el.removeClass('in');
18344         this.el.hide();
18345         this.hoverState = null;
18346         
18347         this.fireEvent('hide', this);
18348     }
18349     
18350 });
18351
18352 Roo.bootstrap.Popover.alignment = {
18353     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18354     'right' : ['l-r', [10,0], 'left bs-popover-left'],
18355     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18356     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18357 };
18358
18359  /*
18360  * - LGPL
18361  *
18362  * Progress
18363  * 
18364  */
18365
18366 /**
18367  * @class Roo.bootstrap.Progress
18368  * @extends Roo.bootstrap.Component
18369  * Bootstrap Progress class
18370  * @cfg {Boolean} striped striped of the progress bar
18371  * @cfg {Boolean} active animated of the progress bar
18372  * 
18373  * 
18374  * @constructor
18375  * Create a new Progress
18376  * @param {Object} config The config object
18377  */
18378
18379 Roo.bootstrap.Progress = function(config){
18380     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18381 };
18382
18383 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
18384     
18385     striped : false,
18386     active: false,
18387     
18388     getAutoCreate : function(){
18389         var cfg = {
18390             tag: 'div',
18391             cls: 'progress'
18392         };
18393         
18394         
18395         if(this.striped){
18396             cfg.cls += ' progress-striped';
18397         }
18398       
18399         if(this.active){
18400             cfg.cls += ' active';
18401         }
18402         
18403         
18404         return cfg;
18405     }
18406    
18407 });
18408
18409  
18410
18411  /*
18412  * - LGPL
18413  *
18414  * ProgressBar
18415  * 
18416  */
18417
18418 /**
18419  * @class Roo.bootstrap.ProgressBar
18420  * @extends Roo.bootstrap.Component
18421  * Bootstrap ProgressBar class
18422  * @cfg {Number} aria_valuenow aria-value now
18423  * @cfg {Number} aria_valuemin aria-value min
18424  * @cfg {Number} aria_valuemax aria-value max
18425  * @cfg {String} label label for the progress bar
18426  * @cfg {String} panel (success | info | warning | danger )
18427  * @cfg {String} role role of the progress bar
18428  * @cfg {String} sr_only text
18429  * 
18430  * 
18431  * @constructor
18432  * Create a new ProgressBar
18433  * @param {Object} config The config object
18434  */
18435
18436 Roo.bootstrap.ProgressBar = function(config){
18437     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18438 };
18439
18440 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18441     
18442     aria_valuenow : 0,
18443     aria_valuemin : 0,
18444     aria_valuemax : 100,
18445     label : false,
18446     panel : false,
18447     role : false,
18448     sr_only: false,
18449     
18450     getAutoCreate : function()
18451     {
18452         
18453         var cfg = {
18454             tag: 'div',
18455             cls: 'progress-bar',
18456             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18457         };
18458         
18459         if(this.sr_only){
18460             cfg.cn = {
18461                 tag: 'span',
18462                 cls: 'sr-only',
18463                 html: this.sr_only
18464             }
18465         }
18466         
18467         if(this.role){
18468             cfg.role = this.role;
18469         }
18470         
18471         if(this.aria_valuenow){
18472             cfg['aria-valuenow'] = this.aria_valuenow;
18473         }
18474         
18475         if(this.aria_valuemin){
18476             cfg['aria-valuemin'] = this.aria_valuemin;
18477         }
18478         
18479         if(this.aria_valuemax){
18480             cfg['aria-valuemax'] = this.aria_valuemax;
18481         }
18482         
18483         if(this.label && !this.sr_only){
18484             cfg.html = this.label;
18485         }
18486         
18487         if(this.panel){
18488             cfg.cls += ' progress-bar-' + this.panel;
18489         }
18490         
18491         return cfg;
18492     },
18493     
18494     update : function(aria_valuenow)
18495     {
18496         this.aria_valuenow = aria_valuenow;
18497         
18498         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18499     }
18500    
18501 });
18502
18503  
18504
18505  /*
18506  * - LGPL
18507  *
18508  * column
18509  * 
18510  */
18511
18512 /**
18513  * @class Roo.bootstrap.TabGroup
18514  * @extends Roo.bootstrap.Column
18515  * Bootstrap Column class
18516  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18517  * @cfg {Boolean} carousel true to make the group behave like a carousel
18518  * @cfg {Boolean} bullets show bullets for the panels
18519  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18520  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18521  * @cfg {Boolean} showarrow (true|false) show arrow default true
18522  * 
18523  * @constructor
18524  * Create a new TabGroup
18525  * @param {Object} config The config object
18526  */
18527
18528 Roo.bootstrap.TabGroup = function(config){
18529     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18530     if (!this.navId) {
18531         this.navId = Roo.id();
18532     }
18533     this.tabs = [];
18534     Roo.bootstrap.TabGroup.register(this);
18535     
18536 };
18537
18538 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18539     
18540     carousel : false,
18541     transition : false,
18542     bullets : 0,
18543     timer : 0,
18544     autoslide : false,
18545     slideFn : false,
18546     slideOnTouch : false,
18547     showarrow : true,
18548     
18549     getAutoCreate : function()
18550     {
18551         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18552         
18553         cfg.cls += ' tab-content';
18554         
18555         if (this.carousel) {
18556             cfg.cls += ' carousel slide';
18557             
18558             cfg.cn = [{
18559                cls : 'carousel-inner',
18560                cn : []
18561             }];
18562         
18563             if(this.bullets  && !Roo.isTouch){
18564                 
18565                 var bullets = {
18566                     cls : 'carousel-bullets',
18567                     cn : []
18568                 };
18569                
18570                 if(this.bullets_cls){
18571                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18572                 }
18573                 
18574                 bullets.cn.push({
18575                     cls : 'clear'
18576                 });
18577                 
18578                 cfg.cn[0].cn.push(bullets);
18579             }
18580             
18581             if(this.showarrow){
18582                 cfg.cn[0].cn.push({
18583                     tag : 'div',
18584                     class : 'carousel-arrow',
18585                     cn : [
18586                         {
18587                             tag : 'div',
18588                             class : 'carousel-prev',
18589                             cn : [
18590                                 {
18591                                     tag : 'i',
18592                                     class : 'fa fa-chevron-left'
18593                                 }
18594                             ]
18595                         },
18596                         {
18597                             tag : 'div',
18598                             class : 'carousel-next',
18599                             cn : [
18600                                 {
18601                                     tag : 'i',
18602                                     class : 'fa fa-chevron-right'
18603                                 }
18604                             ]
18605                         }
18606                     ]
18607                 });
18608             }
18609             
18610         }
18611         
18612         return cfg;
18613     },
18614     
18615     initEvents:  function()
18616     {
18617 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18618 //            this.el.on("touchstart", this.onTouchStart, this);
18619 //        }
18620         
18621         if(this.autoslide){
18622             var _this = this;
18623             
18624             this.slideFn = window.setInterval(function() {
18625                 _this.showPanelNext();
18626             }, this.timer);
18627         }
18628         
18629         if(this.showarrow){
18630             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18631             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18632         }
18633         
18634         
18635     },
18636     
18637 //    onTouchStart : function(e, el, o)
18638 //    {
18639 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18640 //            return;
18641 //        }
18642 //        
18643 //        this.showPanelNext();
18644 //    },
18645     
18646     
18647     getChildContainer : function()
18648     {
18649         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18650     },
18651     
18652     /**
18653     * register a Navigation item
18654     * @param {Roo.bootstrap.NavItem} the navitem to add
18655     */
18656     register : function(item)
18657     {
18658         this.tabs.push( item);
18659         item.navId = this.navId; // not really needed..
18660         this.addBullet();
18661     
18662     },
18663     
18664     getActivePanel : function()
18665     {
18666         var r = false;
18667         Roo.each(this.tabs, function(t) {
18668             if (t.active) {
18669                 r = t;
18670                 return false;
18671             }
18672             return null;
18673         });
18674         return r;
18675         
18676     },
18677     getPanelByName : function(n)
18678     {
18679         var r = false;
18680         Roo.each(this.tabs, function(t) {
18681             if (t.tabId == n) {
18682                 r = t;
18683                 return false;
18684             }
18685             return null;
18686         });
18687         return r;
18688     },
18689     indexOfPanel : function(p)
18690     {
18691         var r = false;
18692         Roo.each(this.tabs, function(t,i) {
18693             if (t.tabId == p.tabId) {
18694                 r = i;
18695                 return false;
18696             }
18697             return null;
18698         });
18699         return r;
18700     },
18701     /**
18702      * show a specific panel
18703      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18704      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18705      */
18706     showPanel : function (pan)
18707     {
18708         if(this.transition || typeof(pan) == 'undefined'){
18709             Roo.log("waiting for the transitionend");
18710             return false;
18711         }
18712         
18713         if (typeof(pan) == 'number') {
18714             pan = this.tabs[pan];
18715         }
18716         
18717         if (typeof(pan) == 'string') {
18718             pan = this.getPanelByName(pan);
18719         }
18720         
18721         var cur = this.getActivePanel();
18722         
18723         if(!pan || !cur){
18724             Roo.log('pan or acitve pan is undefined');
18725             return false;
18726         }
18727         
18728         if (pan.tabId == this.getActivePanel().tabId) {
18729             return true;
18730         }
18731         
18732         if (false === cur.fireEvent('beforedeactivate')) {
18733             return false;
18734         }
18735         
18736         if(this.bullets > 0 && !Roo.isTouch){
18737             this.setActiveBullet(this.indexOfPanel(pan));
18738         }
18739         
18740         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18741             
18742             //class="carousel-item carousel-item-next carousel-item-left"
18743             
18744             this.transition = true;
18745             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18746             var lr = dir == 'next' ? 'left' : 'right';
18747             pan.el.addClass(dir); // or prev
18748             pan.el.addClass('carousel-item-' + dir); // or prev
18749             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18750             cur.el.addClass(lr); // or right
18751             pan.el.addClass(lr);
18752             cur.el.addClass('carousel-item-' +lr); // or right
18753             pan.el.addClass('carousel-item-' +lr);
18754             
18755             
18756             var _this = this;
18757             cur.el.on('transitionend', function() {
18758                 Roo.log("trans end?");
18759                 
18760                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18761                 pan.setActive(true);
18762                 
18763                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18764                 cur.setActive(false);
18765                 
18766                 _this.transition = false;
18767                 
18768             }, this, { single:  true } );
18769             
18770             return true;
18771         }
18772         
18773         cur.setActive(false);
18774         pan.setActive(true);
18775         
18776         return true;
18777         
18778     },
18779     showPanelNext : function()
18780     {
18781         var i = this.indexOfPanel(this.getActivePanel());
18782         
18783         if (i >= this.tabs.length - 1 && !this.autoslide) {
18784             return;
18785         }
18786         
18787         if (i >= this.tabs.length - 1 && this.autoslide) {
18788             i = -1;
18789         }
18790         
18791         this.showPanel(this.tabs[i+1]);
18792     },
18793     
18794     showPanelPrev : function()
18795     {
18796         var i = this.indexOfPanel(this.getActivePanel());
18797         
18798         if (i  < 1 && !this.autoslide) {
18799             return;
18800         }
18801         
18802         if (i < 1 && this.autoslide) {
18803             i = this.tabs.length;
18804         }
18805         
18806         this.showPanel(this.tabs[i-1]);
18807     },
18808     
18809     
18810     addBullet: function()
18811     {
18812         if(!this.bullets || Roo.isTouch){
18813             return;
18814         }
18815         var ctr = this.el.select('.carousel-bullets',true).first();
18816         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18817         var bullet = ctr.createChild({
18818             cls : 'bullet bullet-' + i
18819         },ctr.dom.lastChild);
18820         
18821         
18822         var _this = this;
18823         
18824         bullet.on('click', (function(e, el, o, ii, t){
18825
18826             e.preventDefault();
18827
18828             this.showPanel(ii);
18829
18830             if(this.autoslide && this.slideFn){
18831                 clearInterval(this.slideFn);
18832                 this.slideFn = window.setInterval(function() {
18833                     _this.showPanelNext();
18834                 }, this.timer);
18835             }
18836
18837         }).createDelegate(this, [i, bullet], true));
18838                 
18839         
18840     },
18841      
18842     setActiveBullet : function(i)
18843     {
18844         if(Roo.isTouch){
18845             return;
18846         }
18847         
18848         Roo.each(this.el.select('.bullet', true).elements, function(el){
18849             el.removeClass('selected');
18850         });
18851
18852         var bullet = this.el.select('.bullet-' + i, true).first();
18853         
18854         if(!bullet){
18855             return;
18856         }
18857         
18858         bullet.addClass('selected');
18859     }
18860     
18861     
18862   
18863 });
18864
18865  
18866
18867  
18868  
18869 Roo.apply(Roo.bootstrap.TabGroup, {
18870     
18871     groups: {},
18872      /**
18873     * register a Navigation Group
18874     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18875     */
18876     register : function(navgrp)
18877     {
18878         this.groups[navgrp.navId] = navgrp;
18879         
18880     },
18881     /**
18882     * fetch a Navigation Group based on the navigation ID
18883     * if one does not exist , it will get created.
18884     * @param {string} the navgroup to add
18885     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18886     */
18887     get: function(navId) {
18888         if (typeof(this.groups[navId]) == 'undefined') {
18889             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18890         }
18891         return this.groups[navId] ;
18892     }
18893     
18894     
18895     
18896 });
18897
18898  /*
18899  * - LGPL
18900  *
18901  * TabPanel
18902  * 
18903  */
18904
18905 /**
18906  * @class Roo.bootstrap.TabPanel
18907  * @extends Roo.bootstrap.Component
18908  * Bootstrap TabPanel class
18909  * @cfg {Boolean} active panel active
18910  * @cfg {String} html panel content
18911  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18912  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18913  * @cfg {String} href click to link..
18914  * 
18915  * 
18916  * @constructor
18917  * Create a new TabPanel
18918  * @param {Object} config The config object
18919  */
18920
18921 Roo.bootstrap.TabPanel = function(config){
18922     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18923     this.addEvents({
18924         /**
18925              * @event changed
18926              * Fires when the active status changes
18927              * @param {Roo.bootstrap.TabPanel} this
18928              * @param {Boolean} state the new state
18929             
18930          */
18931         'changed': true,
18932         /**
18933              * @event beforedeactivate
18934              * Fires before a tab is de-activated - can be used to do validation on a form.
18935              * @param {Roo.bootstrap.TabPanel} this
18936              * @return {Boolean} false if there is an error
18937             
18938          */
18939         'beforedeactivate': true
18940      });
18941     
18942     this.tabId = this.tabId || Roo.id();
18943   
18944 };
18945
18946 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18947     
18948     active: false,
18949     html: false,
18950     tabId: false,
18951     navId : false,
18952     href : '',
18953     
18954     getAutoCreate : function(){
18955         
18956         
18957         var cfg = {
18958             tag: 'div',
18959             // item is needed for carousel - not sure if it has any effect otherwise
18960             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18961             html: this.html || ''
18962         };
18963         
18964         if(this.active){
18965             cfg.cls += ' active';
18966         }
18967         
18968         if(this.tabId){
18969             cfg.tabId = this.tabId;
18970         }
18971         
18972         
18973         
18974         return cfg;
18975     },
18976     
18977     initEvents:  function()
18978     {
18979         var p = this.parent();
18980         
18981         this.navId = this.navId || p.navId;
18982         
18983         if (typeof(this.navId) != 'undefined') {
18984             // not really needed.. but just in case.. parent should be a NavGroup.
18985             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18986             
18987             tg.register(this);
18988             
18989             var i = tg.tabs.length - 1;
18990             
18991             if(this.active && tg.bullets > 0 && i < tg.bullets){
18992                 tg.setActiveBullet(i);
18993             }
18994         }
18995         
18996         this.el.on('click', this.onClick, this);
18997         
18998         if(Roo.isTouch){
18999             this.el.on("touchstart", this.onTouchStart, this);
19000             this.el.on("touchmove", this.onTouchMove, this);
19001             this.el.on("touchend", this.onTouchEnd, this);
19002         }
19003         
19004     },
19005     
19006     onRender : function(ct, position)
19007     {
19008         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
19009     },
19010     
19011     setActive : function(state)
19012     {
19013         Roo.log("panel - set active " + this.tabId + "=" + state);
19014         
19015         this.active = state;
19016         if (!state) {
19017             this.el.removeClass('active');
19018             
19019         } else  if (!this.el.hasClass('active')) {
19020             this.el.addClass('active');
19021         }
19022         
19023         this.fireEvent('changed', this, state);
19024     },
19025     
19026     onClick : function(e)
19027     {
19028         e.preventDefault();
19029         
19030         if(!this.href.length){
19031             return;
19032         }
19033         
19034         window.location.href = this.href;
19035     },
19036     
19037     startX : 0,
19038     startY : 0,
19039     endX : 0,
19040     endY : 0,
19041     swiping : false,
19042     
19043     onTouchStart : function(e)
19044     {
19045         this.swiping = false;
19046         
19047         this.startX = e.browserEvent.touches[0].clientX;
19048         this.startY = e.browserEvent.touches[0].clientY;
19049     },
19050     
19051     onTouchMove : function(e)
19052     {
19053         this.swiping = true;
19054         
19055         this.endX = e.browserEvent.touches[0].clientX;
19056         this.endY = e.browserEvent.touches[0].clientY;
19057     },
19058     
19059     onTouchEnd : function(e)
19060     {
19061         if(!this.swiping){
19062             this.onClick(e);
19063             return;
19064         }
19065         
19066         var tabGroup = this.parent();
19067         
19068         if(this.endX > this.startX){ // swiping right
19069             tabGroup.showPanelPrev();
19070             return;
19071         }
19072         
19073         if(this.startX > this.endX){ // swiping left
19074             tabGroup.showPanelNext();
19075             return;
19076         }
19077     }
19078     
19079     
19080 });
19081  
19082
19083  
19084
19085  /*
19086  * - LGPL
19087  *
19088  * DateField
19089  * 
19090  */
19091
19092 /**
19093  * @class Roo.bootstrap.DateField
19094  * @extends Roo.bootstrap.Input
19095  * Bootstrap DateField class
19096  * @cfg {Number} weekStart default 0
19097  * @cfg {String} viewMode default empty, (months|years)
19098  * @cfg {String} minViewMode default empty, (months|years)
19099  * @cfg {Number} startDate default -Infinity
19100  * @cfg {Number} endDate default Infinity
19101  * @cfg {Boolean} todayHighlight default false
19102  * @cfg {Boolean} todayBtn default false
19103  * @cfg {Boolean} calendarWeeks default false
19104  * @cfg {Object} daysOfWeekDisabled default empty
19105  * @cfg {Boolean} singleMode default false (true | false)
19106  * 
19107  * @cfg {Boolean} keyboardNavigation default true
19108  * @cfg {String} language default en
19109  * 
19110  * @constructor
19111  * Create a new DateField
19112  * @param {Object} config The config object
19113  */
19114
19115 Roo.bootstrap.DateField = function(config){
19116     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
19117      this.addEvents({
19118             /**
19119              * @event show
19120              * Fires when this field show.
19121              * @param {Roo.bootstrap.DateField} this
19122              * @param {Mixed} date The date value
19123              */
19124             show : true,
19125             /**
19126              * @event show
19127              * Fires when this field hide.
19128              * @param {Roo.bootstrap.DateField} this
19129              * @param {Mixed} date The date value
19130              */
19131             hide : true,
19132             /**
19133              * @event select
19134              * Fires when select a date.
19135              * @param {Roo.bootstrap.DateField} this
19136              * @param {Mixed} date The date value
19137              */
19138             select : true,
19139             /**
19140              * @event beforeselect
19141              * Fires when before select a date.
19142              * @param {Roo.bootstrap.DateField} this
19143              * @param {Mixed} date The date value
19144              */
19145             beforeselect : true
19146         });
19147 };
19148
19149 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
19150     
19151     /**
19152      * @cfg {String} format
19153      * The default date format string which can be overriden for localization support.  The format must be
19154      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
19155      */
19156     format : "m/d/y",
19157     /**
19158      * @cfg {String} altFormats
19159      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
19160      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
19161      */
19162     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
19163     
19164     weekStart : 0,
19165     
19166     viewMode : '',
19167     
19168     minViewMode : '',
19169     
19170     todayHighlight : false,
19171     
19172     todayBtn: false,
19173     
19174     language: 'en',
19175     
19176     keyboardNavigation: true,
19177     
19178     calendarWeeks: false,
19179     
19180     startDate: -Infinity,
19181     
19182     endDate: Infinity,
19183     
19184     daysOfWeekDisabled: [],
19185     
19186     _events: [],
19187     
19188     singleMode : false,
19189     
19190     UTCDate: function()
19191     {
19192         return new Date(Date.UTC.apply(Date, arguments));
19193     },
19194     
19195     UTCToday: function()
19196     {
19197         var today = new Date();
19198         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
19199     },
19200     
19201     getDate: function() {
19202             var d = this.getUTCDate();
19203             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
19204     },
19205     
19206     getUTCDate: function() {
19207             return this.date;
19208     },
19209     
19210     setDate: function(d) {
19211             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
19212     },
19213     
19214     setUTCDate: function(d) {
19215             this.date = d;
19216             this.setValue(this.formatDate(this.date));
19217     },
19218         
19219     onRender: function(ct, position)
19220     {
19221         
19222         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
19223         
19224         this.language = this.language || 'en';
19225         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
19226         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
19227         
19228         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
19229         this.format = this.format || 'm/d/y';
19230         this.isInline = false;
19231         this.isInput = true;
19232         this.component = this.el.select('.add-on', true).first() || false;
19233         this.component = (this.component && this.component.length === 0) ? false : this.component;
19234         this.hasInput = this.component && this.inputEl().length;
19235         
19236         if (typeof(this.minViewMode === 'string')) {
19237             switch (this.minViewMode) {
19238                 case 'months':
19239                     this.minViewMode = 1;
19240                     break;
19241                 case 'years':
19242                     this.minViewMode = 2;
19243                     break;
19244                 default:
19245                     this.minViewMode = 0;
19246                     break;
19247             }
19248         }
19249         
19250         if (typeof(this.viewMode === 'string')) {
19251             switch (this.viewMode) {
19252                 case 'months':
19253                     this.viewMode = 1;
19254                     break;
19255                 case 'years':
19256                     this.viewMode = 2;
19257                     break;
19258                 default:
19259                     this.viewMode = 0;
19260                     break;
19261             }
19262         }
19263                 
19264         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
19265         
19266 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
19267         
19268         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19269         
19270         this.picker().on('mousedown', this.onMousedown, this);
19271         this.picker().on('click', this.onClick, this);
19272         
19273         this.picker().addClass('datepicker-dropdown');
19274         
19275         this.startViewMode = this.viewMode;
19276         
19277         if(this.singleMode){
19278             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
19279                 v.setVisibilityMode(Roo.Element.DISPLAY);
19280                 v.hide();
19281             });
19282             
19283             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19284                 v.setStyle('width', '189px');
19285             });
19286         }
19287         
19288         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19289             if(!this.calendarWeeks){
19290                 v.remove();
19291                 return;
19292             }
19293             
19294             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19295             v.attr('colspan', function(i, val){
19296                 return parseInt(val) + 1;
19297             });
19298         });
19299                         
19300         
19301         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19302         
19303         this.setStartDate(this.startDate);
19304         this.setEndDate(this.endDate);
19305         
19306         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19307         
19308         this.fillDow();
19309         this.fillMonths();
19310         this.update();
19311         this.showMode();
19312         
19313         if(this.isInline) {
19314             this.showPopup();
19315         }
19316     },
19317     
19318     picker : function()
19319     {
19320         return this.pickerEl;
19321 //        return this.el.select('.datepicker', true).first();
19322     },
19323     
19324     fillDow: function()
19325     {
19326         var dowCnt = this.weekStart;
19327         
19328         var dow = {
19329             tag: 'tr',
19330             cn: [
19331                 
19332             ]
19333         };
19334         
19335         if(this.calendarWeeks){
19336             dow.cn.push({
19337                 tag: 'th',
19338                 cls: 'cw',
19339                 html: '&nbsp;'
19340             })
19341         }
19342         
19343         while (dowCnt < this.weekStart + 7) {
19344             dow.cn.push({
19345                 tag: 'th',
19346                 cls: 'dow',
19347                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19348             });
19349         }
19350         
19351         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19352     },
19353     
19354     fillMonths: function()
19355     {    
19356         var i = 0;
19357         var months = this.picker().select('>.datepicker-months td', true).first();
19358         
19359         months.dom.innerHTML = '';
19360         
19361         while (i < 12) {
19362             var month = {
19363                 tag: 'span',
19364                 cls: 'month',
19365                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19366             };
19367             
19368             months.createChild(month);
19369         }
19370         
19371     },
19372     
19373     update: function()
19374     {
19375         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;
19376         
19377         if (this.date < this.startDate) {
19378             this.viewDate = new Date(this.startDate);
19379         } else if (this.date > this.endDate) {
19380             this.viewDate = new Date(this.endDate);
19381         } else {
19382             this.viewDate = new Date(this.date);
19383         }
19384         
19385         this.fill();
19386     },
19387     
19388     fill: function() 
19389     {
19390         var d = new Date(this.viewDate),
19391                 year = d.getUTCFullYear(),
19392                 month = d.getUTCMonth(),
19393                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19394                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19395                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19396                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19397                 currentDate = this.date && this.date.valueOf(),
19398                 today = this.UTCToday();
19399         
19400         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19401         
19402 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19403         
19404 //        this.picker.select('>tfoot th.today').
19405 //                                              .text(dates[this.language].today)
19406 //                                              .toggle(this.todayBtn !== false);
19407     
19408         this.updateNavArrows();
19409         this.fillMonths();
19410                                                 
19411         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19412         
19413         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19414          
19415         prevMonth.setUTCDate(day);
19416         
19417         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19418         
19419         var nextMonth = new Date(prevMonth);
19420         
19421         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19422         
19423         nextMonth = nextMonth.valueOf();
19424         
19425         var fillMonths = false;
19426         
19427         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19428         
19429         while(prevMonth.valueOf() <= nextMonth) {
19430             var clsName = '';
19431             
19432             if (prevMonth.getUTCDay() === this.weekStart) {
19433                 if(fillMonths){
19434                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19435                 }
19436                     
19437                 fillMonths = {
19438                     tag: 'tr',
19439                     cn: []
19440                 };
19441                 
19442                 if(this.calendarWeeks){
19443                     // ISO 8601: First week contains first thursday.
19444                     // ISO also states week starts on Monday, but we can be more abstract here.
19445                     var
19446                     // Start of current week: based on weekstart/current date
19447                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19448                     // Thursday of this week
19449                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19450                     // First Thursday of year, year from thursday
19451                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19452                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19453                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19454                     
19455                     fillMonths.cn.push({
19456                         tag: 'td',
19457                         cls: 'cw',
19458                         html: calWeek
19459                     });
19460                 }
19461             }
19462             
19463             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19464                 clsName += ' old';
19465             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19466                 clsName += ' new';
19467             }
19468             if (this.todayHighlight &&
19469                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19470                 prevMonth.getUTCMonth() == today.getMonth() &&
19471                 prevMonth.getUTCDate() == today.getDate()) {
19472                 clsName += ' today';
19473             }
19474             
19475             if (currentDate && prevMonth.valueOf() === currentDate) {
19476                 clsName += ' active';
19477             }
19478             
19479             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19480                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19481                     clsName += ' disabled';
19482             }
19483             
19484             fillMonths.cn.push({
19485                 tag: 'td',
19486                 cls: 'day ' + clsName,
19487                 html: prevMonth.getDate()
19488             });
19489             
19490             prevMonth.setDate(prevMonth.getDate()+1);
19491         }
19492           
19493         var currentYear = this.date && this.date.getUTCFullYear();
19494         var currentMonth = this.date && this.date.getUTCMonth();
19495         
19496         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19497         
19498         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19499             v.removeClass('active');
19500             
19501             if(currentYear === year && k === currentMonth){
19502                 v.addClass('active');
19503             }
19504             
19505             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19506                 v.addClass('disabled');
19507             }
19508             
19509         });
19510         
19511         
19512         year = parseInt(year/10, 10) * 10;
19513         
19514         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19515         
19516         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19517         
19518         year -= 1;
19519         for (var i = -1; i < 11; i++) {
19520             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19521                 tag: 'span',
19522                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19523                 html: year
19524             });
19525             
19526             year += 1;
19527         }
19528     },
19529     
19530     showMode: function(dir) 
19531     {
19532         if (dir) {
19533             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19534         }
19535         
19536         Roo.each(this.picker().select('>div',true).elements, function(v){
19537             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19538             v.hide();
19539         });
19540         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19541     },
19542     
19543     place: function()
19544     {
19545         if(this.isInline) {
19546             return;
19547         }
19548         
19549         this.picker().removeClass(['bottom', 'top']);
19550         
19551         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19552             /*
19553              * place to the top of element!
19554              *
19555              */
19556             
19557             this.picker().addClass('top');
19558             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19559             
19560             return;
19561         }
19562         
19563         this.picker().addClass('bottom');
19564         
19565         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19566     },
19567     
19568     parseDate : function(value)
19569     {
19570         if(!value || value instanceof Date){
19571             return value;
19572         }
19573         var v = Date.parseDate(value, this.format);
19574         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19575             v = Date.parseDate(value, 'Y-m-d');
19576         }
19577         if(!v && this.altFormats){
19578             if(!this.altFormatsArray){
19579                 this.altFormatsArray = this.altFormats.split("|");
19580             }
19581             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19582                 v = Date.parseDate(value, this.altFormatsArray[i]);
19583             }
19584         }
19585         return v;
19586     },
19587     
19588     formatDate : function(date, fmt)
19589     {   
19590         return (!date || !(date instanceof Date)) ?
19591         date : date.dateFormat(fmt || this.format);
19592     },
19593     
19594     onFocus : function()
19595     {
19596         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19597         this.showPopup();
19598     },
19599     
19600     onBlur : function()
19601     {
19602         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19603         
19604         var d = this.inputEl().getValue();
19605         
19606         this.setValue(d);
19607                 
19608         this.hidePopup();
19609     },
19610     
19611     showPopup : function()
19612     {
19613         this.picker().show();
19614         this.update();
19615         this.place();
19616         
19617         this.fireEvent('showpopup', this, this.date);
19618     },
19619     
19620     hidePopup : function()
19621     {
19622         if(this.isInline) {
19623             return;
19624         }
19625         this.picker().hide();
19626         this.viewMode = this.startViewMode;
19627         this.showMode();
19628         
19629         this.fireEvent('hidepopup', this, this.date);
19630         
19631     },
19632     
19633     onMousedown: function(e)
19634     {
19635         e.stopPropagation();
19636         e.preventDefault();
19637     },
19638     
19639     keyup: function(e)
19640     {
19641         Roo.bootstrap.DateField.superclass.keyup.call(this);
19642         this.update();
19643     },
19644
19645     setValue: function(v)
19646     {
19647         if(this.fireEvent('beforeselect', this, v) !== false){
19648             var d = new Date(this.parseDate(v) ).clearTime();
19649         
19650             if(isNaN(d.getTime())){
19651                 this.date = this.viewDate = '';
19652                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19653                 return;
19654             }
19655
19656             v = this.formatDate(d);
19657
19658             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19659
19660             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19661
19662             this.update();
19663
19664             this.fireEvent('select', this, this.date);
19665         }
19666     },
19667     
19668     getValue: function()
19669     {
19670         return this.formatDate(this.date);
19671     },
19672     
19673     fireKey: function(e)
19674     {
19675         if (!this.picker().isVisible()){
19676             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19677                 this.showPopup();
19678             }
19679             return;
19680         }
19681         
19682         var dateChanged = false,
19683         dir, day, month,
19684         newDate, newViewDate;
19685         
19686         switch(e.keyCode){
19687             case 27: // escape
19688                 this.hidePopup();
19689                 e.preventDefault();
19690                 break;
19691             case 37: // left
19692             case 39: // right
19693                 if (!this.keyboardNavigation) {
19694                     break;
19695                 }
19696                 dir = e.keyCode == 37 ? -1 : 1;
19697                 
19698                 if (e.ctrlKey){
19699                     newDate = this.moveYear(this.date, dir);
19700                     newViewDate = this.moveYear(this.viewDate, dir);
19701                 } else if (e.shiftKey){
19702                     newDate = this.moveMonth(this.date, dir);
19703                     newViewDate = this.moveMonth(this.viewDate, dir);
19704                 } else {
19705                     newDate = new Date(this.date);
19706                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19707                     newViewDate = new Date(this.viewDate);
19708                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19709                 }
19710                 if (this.dateWithinRange(newDate)){
19711                     this.date = newDate;
19712                     this.viewDate = newViewDate;
19713                     this.setValue(this.formatDate(this.date));
19714 //                    this.update();
19715                     e.preventDefault();
19716                     dateChanged = true;
19717                 }
19718                 break;
19719             case 38: // up
19720             case 40: // down
19721                 if (!this.keyboardNavigation) {
19722                     break;
19723                 }
19724                 dir = e.keyCode == 38 ? -1 : 1;
19725                 if (e.ctrlKey){
19726                     newDate = this.moveYear(this.date, dir);
19727                     newViewDate = this.moveYear(this.viewDate, dir);
19728                 } else if (e.shiftKey){
19729                     newDate = this.moveMonth(this.date, dir);
19730                     newViewDate = this.moveMonth(this.viewDate, dir);
19731                 } else {
19732                     newDate = new Date(this.date);
19733                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19734                     newViewDate = new Date(this.viewDate);
19735                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19736                 }
19737                 if (this.dateWithinRange(newDate)){
19738                     this.date = newDate;
19739                     this.viewDate = newViewDate;
19740                     this.setValue(this.formatDate(this.date));
19741 //                    this.update();
19742                     e.preventDefault();
19743                     dateChanged = true;
19744                 }
19745                 break;
19746             case 13: // enter
19747                 this.setValue(this.formatDate(this.date));
19748                 this.hidePopup();
19749                 e.preventDefault();
19750                 break;
19751             case 9: // tab
19752                 this.setValue(this.formatDate(this.date));
19753                 this.hidePopup();
19754                 break;
19755             case 16: // shift
19756             case 17: // ctrl
19757             case 18: // alt
19758                 break;
19759             default :
19760                 this.hidePopup();
19761                 
19762         }
19763     },
19764     
19765     
19766     onClick: function(e) 
19767     {
19768         e.stopPropagation();
19769         e.preventDefault();
19770         
19771         var target = e.getTarget();
19772         
19773         if(target.nodeName.toLowerCase() === 'i'){
19774             target = Roo.get(target).dom.parentNode;
19775         }
19776         
19777         var nodeName = target.nodeName;
19778         var className = target.className;
19779         var html = target.innerHTML;
19780         //Roo.log(nodeName);
19781         
19782         switch(nodeName.toLowerCase()) {
19783             case 'th':
19784                 switch(className) {
19785                     case 'switch':
19786                         this.showMode(1);
19787                         break;
19788                     case 'prev':
19789                     case 'next':
19790                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19791                         switch(this.viewMode){
19792                                 case 0:
19793                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19794                                         break;
19795                                 case 1:
19796                                 case 2:
19797                                         this.viewDate = this.moveYear(this.viewDate, dir);
19798                                         break;
19799                         }
19800                         this.fill();
19801                         break;
19802                     case 'today':
19803                         var date = new Date();
19804                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19805 //                        this.fill()
19806                         this.setValue(this.formatDate(this.date));
19807                         
19808                         this.hidePopup();
19809                         break;
19810                 }
19811                 break;
19812             case 'span':
19813                 if (className.indexOf('disabled') < 0) {
19814                     this.viewDate.setUTCDate(1);
19815                     if (className.indexOf('month') > -1) {
19816                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19817                     } else {
19818                         var year = parseInt(html, 10) || 0;
19819                         this.viewDate.setUTCFullYear(year);
19820                         
19821                     }
19822                     
19823                     if(this.singleMode){
19824                         this.setValue(this.formatDate(this.viewDate));
19825                         this.hidePopup();
19826                         return;
19827                     }
19828                     
19829                     this.showMode(-1);
19830                     this.fill();
19831                 }
19832                 break;
19833                 
19834             case 'td':
19835                 //Roo.log(className);
19836                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19837                     var day = parseInt(html, 10) || 1;
19838                     var year = this.viewDate.getUTCFullYear(),
19839                         month = this.viewDate.getUTCMonth();
19840
19841                     if (className.indexOf('old') > -1) {
19842                         if(month === 0 ){
19843                             month = 11;
19844                             year -= 1;
19845                         }else{
19846                             month -= 1;
19847                         }
19848                     } else if (className.indexOf('new') > -1) {
19849                         if (month == 11) {
19850                             month = 0;
19851                             year += 1;
19852                         } else {
19853                             month += 1;
19854                         }
19855                     }
19856                     //Roo.log([year,month,day]);
19857                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19858                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19859 //                    this.fill();
19860                     //Roo.log(this.formatDate(this.date));
19861                     this.setValue(this.formatDate(this.date));
19862                     this.hidePopup();
19863                 }
19864                 break;
19865         }
19866     },
19867     
19868     setStartDate: function(startDate)
19869     {
19870         this.startDate = startDate || -Infinity;
19871         if (this.startDate !== -Infinity) {
19872             this.startDate = this.parseDate(this.startDate);
19873         }
19874         this.update();
19875         this.updateNavArrows();
19876     },
19877
19878     setEndDate: function(endDate)
19879     {
19880         this.endDate = endDate || Infinity;
19881         if (this.endDate !== Infinity) {
19882             this.endDate = this.parseDate(this.endDate);
19883         }
19884         this.update();
19885         this.updateNavArrows();
19886     },
19887     
19888     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19889     {
19890         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19891         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19892             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19893         }
19894         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19895             return parseInt(d, 10);
19896         });
19897         this.update();
19898         this.updateNavArrows();
19899     },
19900     
19901     updateNavArrows: function() 
19902     {
19903         if(this.singleMode){
19904             return;
19905         }
19906         
19907         var d = new Date(this.viewDate),
19908         year = d.getUTCFullYear(),
19909         month = d.getUTCMonth();
19910         
19911         Roo.each(this.picker().select('.prev', true).elements, function(v){
19912             v.show();
19913             switch (this.viewMode) {
19914                 case 0:
19915
19916                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19917                         v.hide();
19918                     }
19919                     break;
19920                 case 1:
19921                 case 2:
19922                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19923                         v.hide();
19924                     }
19925                     break;
19926             }
19927         });
19928         
19929         Roo.each(this.picker().select('.next', true).elements, function(v){
19930             v.show();
19931             switch (this.viewMode) {
19932                 case 0:
19933
19934                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19935                         v.hide();
19936                     }
19937                     break;
19938                 case 1:
19939                 case 2:
19940                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19941                         v.hide();
19942                     }
19943                     break;
19944             }
19945         })
19946     },
19947     
19948     moveMonth: function(date, dir)
19949     {
19950         if (!dir) {
19951             return date;
19952         }
19953         var new_date = new Date(date.valueOf()),
19954         day = new_date.getUTCDate(),
19955         month = new_date.getUTCMonth(),
19956         mag = Math.abs(dir),
19957         new_month, test;
19958         dir = dir > 0 ? 1 : -1;
19959         if (mag == 1){
19960             test = dir == -1
19961             // If going back one month, make sure month is not current month
19962             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19963             ? function(){
19964                 return new_date.getUTCMonth() == month;
19965             }
19966             // If going forward one month, make sure month is as expected
19967             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19968             : function(){
19969                 return new_date.getUTCMonth() != new_month;
19970             };
19971             new_month = month + dir;
19972             new_date.setUTCMonth(new_month);
19973             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19974             if (new_month < 0 || new_month > 11) {
19975                 new_month = (new_month + 12) % 12;
19976             }
19977         } else {
19978             // For magnitudes >1, move one month at a time...
19979             for (var i=0; i<mag; i++) {
19980                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19981                 new_date = this.moveMonth(new_date, dir);
19982             }
19983             // ...then reset the day, keeping it in the new month
19984             new_month = new_date.getUTCMonth();
19985             new_date.setUTCDate(day);
19986             test = function(){
19987                 return new_month != new_date.getUTCMonth();
19988             };
19989         }
19990         // Common date-resetting loop -- if date is beyond end of month, make it
19991         // end of month
19992         while (test()){
19993             new_date.setUTCDate(--day);
19994             new_date.setUTCMonth(new_month);
19995         }
19996         return new_date;
19997     },
19998
19999     moveYear: function(date, dir)
20000     {
20001         return this.moveMonth(date, dir*12);
20002     },
20003
20004     dateWithinRange: function(date)
20005     {
20006         return date >= this.startDate && date <= this.endDate;
20007     },
20008
20009     
20010     remove: function() 
20011     {
20012         this.picker().remove();
20013     },
20014     
20015     validateValue : function(value)
20016     {
20017         if(this.getVisibilityEl().hasClass('hidden')){
20018             return true;
20019         }
20020         
20021         if(value.length < 1)  {
20022             if(this.allowBlank){
20023                 return true;
20024             }
20025             return false;
20026         }
20027         
20028         if(value.length < this.minLength){
20029             return false;
20030         }
20031         if(value.length > this.maxLength){
20032             return false;
20033         }
20034         if(this.vtype){
20035             var vt = Roo.form.VTypes;
20036             if(!vt[this.vtype](value, this)){
20037                 return false;
20038             }
20039         }
20040         if(typeof this.validator == "function"){
20041             var msg = this.validator(value);
20042             if(msg !== true){
20043                 return false;
20044             }
20045         }
20046         
20047         if(this.regex && !this.regex.test(value)){
20048             return false;
20049         }
20050         
20051         if(typeof(this.parseDate(value)) == 'undefined'){
20052             return false;
20053         }
20054         
20055         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
20056             return false;
20057         }      
20058         
20059         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
20060             return false;
20061         } 
20062         
20063         
20064         return true;
20065     },
20066     
20067     reset : function()
20068     {
20069         this.date = this.viewDate = '';
20070         
20071         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20072     }
20073    
20074 });
20075
20076 Roo.apply(Roo.bootstrap.DateField,  {
20077     
20078     head : {
20079         tag: 'thead',
20080         cn: [
20081         {
20082             tag: 'tr',
20083             cn: [
20084             {
20085                 tag: 'th',
20086                 cls: 'prev',
20087                 html: '<i class="fa fa-arrow-left"/>'
20088             },
20089             {
20090                 tag: 'th',
20091                 cls: 'switch',
20092                 colspan: '5'
20093             },
20094             {
20095                 tag: 'th',
20096                 cls: 'next',
20097                 html: '<i class="fa fa-arrow-right"/>'
20098             }
20099
20100             ]
20101         }
20102         ]
20103     },
20104     
20105     content : {
20106         tag: 'tbody',
20107         cn: [
20108         {
20109             tag: 'tr',
20110             cn: [
20111             {
20112                 tag: 'td',
20113                 colspan: '7'
20114             }
20115             ]
20116         }
20117         ]
20118     },
20119     
20120     footer : {
20121         tag: 'tfoot',
20122         cn: [
20123         {
20124             tag: 'tr',
20125             cn: [
20126             {
20127                 tag: 'th',
20128                 colspan: '7',
20129                 cls: 'today'
20130             }
20131                     
20132             ]
20133         }
20134         ]
20135     },
20136     
20137     dates:{
20138         en: {
20139             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
20140             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
20141             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
20142             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20143             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
20144             today: "Today"
20145         }
20146     },
20147     
20148     modes: [
20149     {
20150         clsName: 'days',
20151         navFnc: 'Month',
20152         navStep: 1
20153     },
20154     {
20155         clsName: 'months',
20156         navFnc: 'FullYear',
20157         navStep: 1
20158     },
20159     {
20160         clsName: 'years',
20161         navFnc: 'FullYear',
20162         navStep: 10
20163     }]
20164 });
20165
20166 Roo.apply(Roo.bootstrap.DateField,  {
20167   
20168     template : {
20169         tag: 'div',
20170         cls: 'datepicker dropdown-menu roo-dynamic',
20171         cn: [
20172         {
20173             tag: 'div',
20174             cls: 'datepicker-days',
20175             cn: [
20176             {
20177                 tag: 'table',
20178                 cls: 'table-condensed',
20179                 cn:[
20180                 Roo.bootstrap.DateField.head,
20181                 {
20182                     tag: 'tbody'
20183                 },
20184                 Roo.bootstrap.DateField.footer
20185                 ]
20186             }
20187             ]
20188         },
20189         {
20190             tag: 'div',
20191             cls: 'datepicker-months',
20192             cn: [
20193             {
20194                 tag: 'table',
20195                 cls: 'table-condensed',
20196                 cn:[
20197                 Roo.bootstrap.DateField.head,
20198                 Roo.bootstrap.DateField.content,
20199                 Roo.bootstrap.DateField.footer
20200                 ]
20201             }
20202             ]
20203         },
20204         {
20205             tag: 'div',
20206             cls: 'datepicker-years',
20207             cn: [
20208             {
20209                 tag: 'table',
20210                 cls: 'table-condensed',
20211                 cn:[
20212                 Roo.bootstrap.DateField.head,
20213                 Roo.bootstrap.DateField.content,
20214                 Roo.bootstrap.DateField.footer
20215                 ]
20216             }
20217             ]
20218         }
20219         ]
20220     }
20221 });
20222
20223  
20224
20225  /*
20226  * - LGPL
20227  *
20228  * TimeField
20229  * 
20230  */
20231
20232 /**
20233  * @class Roo.bootstrap.TimeField
20234  * @extends Roo.bootstrap.Input
20235  * Bootstrap DateField class
20236  * 
20237  * 
20238  * @constructor
20239  * Create a new TimeField
20240  * @param {Object} config The config object
20241  */
20242
20243 Roo.bootstrap.TimeField = function(config){
20244     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
20245     this.addEvents({
20246             /**
20247              * @event show
20248              * Fires when this field show.
20249              * @param {Roo.bootstrap.DateField} thisthis
20250              * @param {Mixed} date The date value
20251              */
20252             show : true,
20253             /**
20254              * @event show
20255              * Fires when this field hide.
20256              * @param {Roo.bootstrap.DateField} this
20257              * @param {Mixed} date The date value
20258              */
20259             hide : true,
20260             /**
20261              * @event select
20262              * Fires when select a date.
20263              * @param {Roo.bootstrap.DateField} this
20264              * @param {Mixed} date The date value
20265              */
20266             select : true
20267         });
20268 };
20269
20270 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
20271     
20272     /**
20273      * @cfg {String} format
20274      * The default time format string which can be overriden for localization support.  The format must be
20275      * valid according to {@link Date#parseDate} (defaults to 'H:i').
20276      */
20277     format : "H:i",
20278        
20279     onRender: function(ct, position)
20280     {
20281         
20282         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
20283                 
20284         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
20285         
20286         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20287         
20288         this.pop = this.picker().select('>.datepicker-time',true).first();
20289         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20290         
20291         this.picker().on('mousedown', this.onMousedown, this);
20292         this.picker().on('click', this.onClick, this);
20293         
20294         this.picker().addClass('datepicker-dropdown');
20295     
20296         this.fillTime();
20297         this.update();
20298             
20299         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20300         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20301         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20302         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20303         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20304         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20305
20306     },
20307     
20308     fireKey: function(e){
20309         if (!this.picker().isVisible()){
20310             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20311                 this.show();
20312             }
20313             return;
20314         }
20315
20316         e.preventDefault();
20317         
20318         switch(e.keyCode){
20319             case 27: // escape
20320                 this.hide();
20321                 break;
20322             case 37: // left
20323             case 39: // right
20324                 this.onTogglePeriod();
20325                 break;
20326             case 38: // up
20327                 this.onIncrementMinutes();
20328                 break;
20329             case 40: // down
20330                 this.onDecrementMinutes();
20331                 break;
20332             case 13: // enter
20333             case 9: // tab
20334                 this.setTime();
20335                 break;
20336         }
20337     },
20338     
20339     onClick: function(e) {
20340         e.stopPropagation();
20341         e.preventDefault();
20342     },
20343     
20344     picker : function()
20345     {
20346         return this.el.select('.datepicker', true).first();
20347     },
20348     
20349     fillTime: function()
20350     {    
20351         var time = this.pop.select('tbody', true).first();
20352         
20353         time.dom.innerHTML = '';
20354         
20355         time.createChild({
20356             tag: 'tr',
20357             cn: [
20358                 {
20359                     tag: 'td',
20360                     cn: [
20361                         {
20362                             tag: 'a',
20363                             href: '#',
20364                             cls: 'btn',
20365                             cn: [
20366                                 {
20367                                     tag: 'span',
20368                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
20369                                 }
20370                             ]
20371                         } 
20372                     ]
20373                 },
20374                 {
20375                     tag: 'td',
20376                     cls: 'separator'
20377                 },
20378                 {
20379                     tag: 'td',
20380                     cn: [
20381                         {
20382                             tag: 'a',
20383                             href: '#',
20384                             cls: 'btn',
20385                             cn: [
20386                                 {
20387                                     tag: 'span',
20388                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
20389                                 }
20390                             ]
20391                         }
20392                     ]
20393                 },
20394                 {
20395                     tag: 'td',
20396                     cls: 'separator'
20397                 }
20398             ]
20399         });
20400         
20401         time.createChild({
20402             tag: 'tr',
20403             cn: [
20404                 {
20405                     tag: 'td',
20406                     cn: [
20407                         {
20408                             tag: 'span',
20409                             cls: 'timepicker-hour',
20410                             html: '00'
20411                         }  
20412                     ]
20413                 },
20414                 {
20415                     tag: 'td',
20416                     cls: 'separator',
20417                     html: ':'
20418                 },
20419                 {
20420                     tag: 'td',
20421                     cn: [
20422                         {
20423                             tag: 'span',
20424                             cls: 'timepicker-minute',
20425                             html: '00'
20426                         }  
20427                     ]
20428                 },
20429                 {
20430                     tag: 'td',
20431                     cls: 'separator'
20432                 },
20433                 {
20434                     tag: 'td',
20435                     cn: [
20436                         {
20437                             tag: 'button',
20438                             type: 'button',
20439                             cls: 'btn btn-primary period',
20440                             html: 'AM'
20441                             
20442                         }
20443                     ]
20444                 }
20445             ]
20446         });
20447         
20448         time.createChild({
20449             tag: 'tr',
20450             cn: [
20451                 {
20452                     tag: 'td',
20453                     cn: [
20454                         {
20455                             tag: 'a',
20456                             href: '#',
20457                             cls: 'btn',
20458                             cn: [
20459                                 {
20460                                     tag: 'span',
20461                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20462                                 }
20463                             ]
20464                         }
20465                     ]
20466                 },
20467                 {
20468                     tag: 'td',
20469                     cls: 'separator'
20470                 },
20471                 {
20472                     tag: 'td',
20473                     cn: [
20474                         {
20475                             tag: 'a',
20476                             href: '#',
20477                             cls: 'btn',
20478                             cn: [
20479                                 {
20480                                     tag: 'span',
20481                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20482                                 }
20483                             ]
20484                         }
20485                     ]
20486                 },
20487                 {
20488                     tag: 'td',
20489                     cls: 'separator'
20490                 }
20491             ]
20492         });
20493         
20494     },
20495     
20496     update: function()
20497     {
20498         
20499         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20500         
20501         this.fill();
20502     },
20503     
20504     fill: function() 
20505     {
20506         var hours = this.time.getHours();
20507         var minutes = this.time.getMinutes();
20508         var period = 'AM';
20509         
20510         if(hours > 11){
20511             period = 'PM';
20512         }
20513         
20514         if(hours == 0){
20515             hours = 12;
20516         }
20517         
20518         
20519         if(hours > 12){
20520             hours = hours - 12;
20521         }
20522         
20523         if(hours < 10){
20524             hours = '0' + hours;
20525         }
20526         
20527         if(minutes < 10){
20528             minutes = '0' + minutes;
20529         }
20530         
20531         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20532         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20533         this.pop.select('button', true).first().dom.innerHTML = period;
20534         
20535     },
20536     
20537     place: function()
20538     {   
20539         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20540         
20541         var cls = ['bottom'];
20542         
20543         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20544             cls.pop();
20545             cls.push('top');
20546         }
20547         
20548         cls.push('right');
20549         
20550         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20551             cls.pop();
20552             cls.push('left');
20553         }
20554         
20555         this.picker().addClass(cls.join('-'));
20556         
20557         var _this = this;
20558         
20559         Roo.each(cls, function(c){
20560             if(c == 'bottom'){
20561                 _this.picker().setTop(_this.inputEl().getHeight());
20562                 return;
20563             }
20564             if(c == 'top'){
20565                 _this.picker().setTop(0 - _this.picker().getHeight());
20566                 return;
20567             }
20568             
20569             if(c == 'left'){
20570                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20571                 return;
20572             }
20573             if(c == 'right'){
20574                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20575                 return;
20576             }
20577         });
20578         
20579     },
20580   
20581     onFocus : function()
20582     {
20583         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20584         this.show();
20585     },
20586     
20587     onBlur : function()
20588     {
20589         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20590         this.hide();
20591     },
20592     
20593     show : function()
20594     {
20595         this.picker().show();
20596         this.pop.show();
20597         this.update();
20598         this.place();
20599         
20600         this.fireEvent('show', this, this.date);
20601     },
20602     
20603     hide : function()
20604     {
20605         this.picker().hide();
20606         this.pop.hide();
20607         
20608         this.fireEvent('hide', this, this.date);
20609     },
20610     
20611     setTime : function()
20612     {
20613         this.hide();
20614         this.setValue(this.time.format(this.format));
20615         
20616         this.fireEvent('select', this, this.date);
20617         
20618         
20619     },
20620     
20621     onMousedown: function(e){
20622         e.stopPropagation();
20623         e.preventDefault();
20624     },
20625     
20626     onIncrementHours: function()
20627     {
20628         Roo.log('onIncrementHours');
20629         this.time = this.time.add(Date.HOUR, 1);
20630         this.update();
20631         
20632     },
20633     
20634     onDecrementHours: function()
20635     {
20636         Roo.log('onDecrementHours');
20637         this.time = this.time.add(Date.HOUR, -1);
20638         this.update();
20639     },
20640     
20641     onIncrementMinutes: function()
20642     {
20643         Roo.log('onIncrementMinutes');
20644         this.time = this.time.add(Date.MINUTE, 1);
20645         this.update();
20646     },
20647     
20648     onDecrementMinutes: function()
20649     {
20650         Roo.log('onDecrementMinutes');
20651         this.time = this.time.add(Date.MINUTE, -1);
20652         this.update();
20653     },
20654     
20655     onTogglePeriod: function()
20656     {
20657         Roo.log('onTogglePeriod');
20658         this.time = this.time.add(Date.HOUR, 12);
20659         this.update();
20660     }
20661     
20662    
20663 });
20664
20665 Roo.apply(Roo.bootstrap.TimeField,  {
20666     
20667     content : {
20668         tag: 'tbody',
20669         cn: [
20670             {
20671                 tag: 'tr',
20672                 cn: [
20673                 {
20674                     tag: 'td',
20675                     colspan: '7'
20676                 }
20677                 ]
20678             }
20679         ]
20680     },
20681     
20682     footer : {
20683         tag: 'tfoot',
20684         cn: [
20685             {
20686                 tag: 'tr',
20687                 cn: [
20688                 {
20689                     tag: 'th',
20690                     colspan: '7',
20691                     cls: '',
20692                     cn: [
20693                         {
20694                             tag: 'button',
20695                             cls: 'btn btn-info ok',
20696                             html: 'OK'
20697                         }
20698                     ]
20699                 }
20700
20701                 ]
20702             }
20703         ]
20704     }
20705 });
20706
20707 Roo.apply(Roo.bootstrap.TimeField,  {
20708   
20709     template : {
20710         tag: 'div',
20711         cls: 'datepicker dropdown-menu',
20712         cn: [
20713             {
20714                 tag: 'div',
20715                 cls: 'datepicker-time',
20716                 cn: [
20717                 {
20718                     tag: 'table',
20719                     cls: 'table-condensed',
20720                     cn:[
20721                     Roo.bootstrap.TimeField.content,
20722                     Roo.bootstrap.TimeField.footer
20723                     ]
20724                 }
20725                 ]
20726             }
20727         ]
20728     }
20729 });
20730
20731  
20732
20733  /*
20734  * - LGPL
20735  *
20736  * MonthField
20737  * 
20738  */
20739
20740 /**
20741  * @class Roo.bootstrap.MonthField
20742  * @extends Roo.bootstrap.Input
20743  * Bootstrap MonthField class
20744  * 
20745  * @cfg {String} language default en
20746  * 
20747  * @constructor
20748  * Create a new MonthField
20749  * @param {Object} config The config object
20750  */
20751
20752 Roo.bootstrap.MonthField = function(config){
20753     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20754     
20755     this.addEvents({
20756         /**
20757          * @event show
20758          * Fires when this field show.
20759          * @param {Roo.bootstrap.MonthField} this
20760          * @param {Mixed} date The date value
20761          */
20762         show : true,
20763         /**
20764          * @event show
20765          * Fires when this field hide.
20766          * @param {Roo.bootstrap.MonthField} this
20767          * @param {Mixed} date The date value
20768          */
20769         hide : true,
20770         /**
20771          * @event select
20772          * Fires when select a date.
20773          * @param {Roo.bootstrap.MonthField} this
20774          * @param {String} oldvalue The old value
20775          * @param {String} newvalue The new value
20776          */
20777         select : true
20778     });
20779 };
20780
20781 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20782     
20783     onRender: function(ct, position)
20784     {
20785         
20786         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20787         
20788         this.language = this.language || 'en';
20789         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20790         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20791         
20792         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20793         this.isInline = false;
20794         this.isInput = true;
20795         this.component = this.el.select('.add-on', true).first() || false;
20796         this.component = (this.component && this.component.length === 0) ? false : this.component;
20797         this.hasInput = this.component && this.inputEL().length;
20798         
20799         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20800         
20801         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20802         
20803         this.picker().on('mousedown', this.onMousedown, this);
20804         this.picker().on('click', this.onClick, this);
20805         
20806         this.picker().addClass('datepicker-dropdown');
20807         
20808         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20809             v.setStyle('width', '189px');
20810         });
20811         
20812         this.fillMonths();
20813         
20814         this.update();
20815         
20816         if(this.isInline) {
20817             this.show();
20818         }
20819         
20820     },
20821     
20822     setValue: function(v, suppressEvent)
20823     {   
20824         var o = this.getValue();
20825         
20826         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20827         
20828         this.update();
20829
20830         if(suppressEvent !== true){
20831             this.fireEvent('select', this, o, v);
20832         }
20833         
20834     },
20835     
20836     getValue: function()
20837     {
20838         return this.value;
20839     },
20840     
20841     onClick: function(e) 
20842     {
20843         e.stopPropagation();
20844         e.preventDefault();
20845         
20846         var target = e.getTarget();
20847         
20848         if(target.nodeName.toLowerCase() === 'i'){
20849             target = Roo.get(target).dom.parentNode;
20850         }
20851         
20852         var nodeName = target.nodeName;
20853         var className = target.className;
20854         var html = target.innerHTML;
20855         
20856         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20857             return;
20858         }
20859         
20860         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20861         
20862         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20863         
20864         this.hide();
20865                         
20866     },
20867     
20868     picker : function()
20869     {
20870         return this.pickerEl;
20871     },
20872     
20873     fillMonths: function()
20874     {    
20875         var i = 0;
20876         var months = this.picker().select('>.datepicker-months td', true).first();
20877         
20878         months.dom.innerHTML = '';
20879         
20880         while (i < 12) {
20881             var month = {
20882                 tag: 'span',
20883                 cls: 'month',
20884                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20885             };
20886             
20887             months.createChild(month);
20888         }
20889         
20890     },
20891     
20892     update: function()
20893     {
20894         var _this = this;
20895         
20896         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20897             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20898         }
20899         
20900         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20901             e.removeClass('active');
20902             
20903             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20904                 e.addClass('active');
20905             }
20906         })
20907     },
20908     
20909     place: function()
20910     {
20911         if(this.isInline) {
20912             return;
20913         }
20914         
20915         this.picker().removeClass(['bottom', 'top']);
20916         
20917         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20918             /*
20919              * place to the top of element!
20920              *
20921              */
20922             
20923             this.picker().addClass('top');
20924             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20925             
20926             return;
20927         }
20928         
20929         this.picker().addClass('bottom');
20930         
20931         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20932     },
20933     
20934     onFocus : function()
20935     {
20936         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20937         this.show();
20938     },
20939     
20940     onBlur : function()
20941     {
20942         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20943         
20944         var d = this.inputEl().getValue();
20945         
20946         this.setValue(d);
20947                 
20948         this.hide();
20949     },
20950     
20951     show : function()
20952     {
20953         this.picker().show();
20954         this.picker().select('>.datepicker-months', true).first().show();
20955         this.update();
20956         this.place();
20957         
20958         this.fireEvent('show', this, this.date);
20959     },
20960     
20961     hide : function()
20962     {
20963         if(this.isInline) {
20964             return;
20965         }
20966         this.picker().hide();
20967         this.fireEvent('hide', this, this.date);
20968         
20969     },
20970     
20971     onMousedown: function(e)
20972     {
20973         e.stopPropagation();
20974         e.preventDefault();
20975     },
20976     
20977     keyup: function(e)
20978     {
20979         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20980         this.update();
20981     },
20982
20983     fireKey: function(e)
20984     {
20985         if (!this.picker().isVisible()){
20986             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20987                 this.show();
20988             }
20989             return;
20990         }
20991         
20992         var dir;
20993         
20994         switch(e.keyCode){
20995             case 27: // escape
20996                 this.hide();
20997                 e.preventDefault();
20998                 break;
20999             case 37: // left
21000             case 39: // right
21001                 dir = e.keyCode == 37 ? -1 : 1;
21002                 
21003                 this.vIndex = this.vIndex + dir;
21004                 
21005                 if(this.vIndex < 0){
21006                     this.vIndex = 0;
21007                 }
21008                 
21009                 if(this.vIndex > 11){
21010                     this.vIndex = 11;
21011                 }
21012                 
21013                 if(isNaN(this.vIndex)){
21014                     this.vIndex = 0;
21015                 }
21016                 
21017                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21018                 
21019                 break;
21020             case 38: // up
21021             case 40: // down
21022                 
21023                 dir = e.keyCode == 38 ? -1 : 1;
21024                 
21025                 this.vIndex = this.vIndex + dir * 4;
21026                 
21027                 if(this.vIndex < 0){
21028                     this.vIndex = 0;
21029                 }
21030                 
21031                 if(this.vIndex > 11){
21032                     this.vIndex = 11;
21033                 }
21034                 
21035                 if(isNaN(this.vIndex)){
21036                     this.vIndex = 0;
21037                 }
21038                 
21039                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21040                 break;
21041                 
21042             case 13: // enter
21043                 
21044                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21045                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21046                 }
21047                 
21048                 this.hide();
21049                 e.preventDefault();
21050                 break;
21051             case 9: // tab
21052                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21053                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21054                 }
21055                 this.hide();
21056                 break;
21057             case 16: // shift
21058             case 17: // ctrl
21059             case 18: // alt
21060                 break;
21061             default :
21062                 this.hide();
21063                 
21064         }
21065     },
21066     
21067     remove: function() 
21068     {
21069         this.picker().remove();
21070     }
21071    
21072 });
21073
21074 Roo.apply(Roo.bootstrap.MonthField,  {
21075     
21076     content : {
21077         tag: 'tbody',
21078         cn: [
21079         {
21080             tag: 'tr',
21081             cn: [
21082             {
21083                 tag: 'td',
21084                 colspan: '7'
21085             }
21086             ]
21087         }
21088         ]
21089     },
21090     
21091     dates:{
21092         en: {
21093             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21094             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
21095         }
21096     }
21097 });
21098
21099 Roo.apply(Roo.bootstrap.MonthField,  {
21100   
21101     template : {
21102         tag: 'div',
21103         cls: 'datepicker dropdown-menu roo-dynamic',
21104         cn: [
21105             {
21106                 tag: 'div',
21107                 cls: 'datepicker-months',
21108                 cn: [
21109                 {
21110                     tag: 'table',
21111                     cls: 'table-condensed',
21112                     cn:[
21113                         Roo.bootstrap.DateField.content
21114                     ]
21115                 }
21116                 ]
21117             }
21118         ]
21119     }
21120 });
21121
21122  
21123
21124  
21125  /*
21126  * - LGPL
21127  *
21128  * CheckBox
21129  * 
21130  */
21131
21132 /**
21133  * @class Roo.bootstrap.CheckBox
21134  * @extends Roo.bootstrap.Input
21135  * Bootstrap CheckBox class
21136  * 
21137  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
21138  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
21139  * @cfg {String} boxLabel The text that appears beside the checkbox
21140  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
21141  * @cfg {Boolean} checked initnal the element
21142  * @cfg {Boolean} inline inline the element (default false)
21143  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
21144  * @cfg {String} tooltip label tooltip
21145  * 
21146  * @constructor
21147  * Create a new CheckBox
21148  * @param {Object} config The config object
21149  */
21150
21151 Roo.bootstrap.CheckBox = function(config){
21152     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
21153    
21154     this.addEvents({
21155         /**
21156         * @event check
21157         * Fires when the element is checked or unchecked.
21158         * @param {Roo.bootstrap.CheckBox} this This input
21159         * @param {Boolean} checked The new checked value
21160         */
21161        check : true,
21162        /**
21163         * @event click
21164         * Fires when the element is click.
21165         * @param {Roo.bootstrap.CheckBox} this This input
21166         */
21167        click : true
21168     });
21169     
21170 };
21171
21172 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
21173   
21174     inputType: 'checkbox',
21175     inputValue: 1,
21176     valueOff: 0,
21177     boxLabel: false,
21178     checked: false,
21179     weight : false,
21180     inline: false,
21181     tooltip : '',
21182     
21183     // checkbox success does not make any sense really.. 
21184     invalidClass : "",
21185     validClass : "",
21186     
21187     
21188     getAutoCreate : function()
21189     {
21190         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
21191         
21192         var id = Roo.id();
21193         
21194         var cfg = {};
21195         
21196         cfg.cls = 'form-group ' + this.inputType; //input-group
21197         
21198         if(this.inline){
21199             cfg.cls += ' ' + this.inputType + '-inline';
21200         }
21201         
21202         var input =  {
21203             tag: 'input',
21204             id : id,
21205             type : this.inputType,
21206             value : this.inputValue,
21207             cls : 'roo-' + this.inputType, //'form-box',
21208             placeholder : this.placeholder || ''
21209             
21210         };
21211         
21212         if(this.inputType != 'radio'){
21213             var hidden =  {
21214                 tag: 'input',
21215                 type : 'hidden',
21216                 cls : 'roo-hidden-value',
21217                 value : this.checked ? this.inputValue : this.valueOff
21218             };
21219         }
21220         
21221             
21222         if (this.weight) { // Validity check?
21223             cfg.cls += " " + this.inputType + "-" + this.weight;
21224         }
21225         
21226         if (this.disabled) {
21227             input.disabled=true;
21228         }
21229         
21230         if(this.checked){
21231             input.checked = this.checked;
21232         }
21233         
21234         if (this.name) {
21235             
21236             input.name = this.name;
21237             
21238             if(this.inputType != 'radio'){
21239                 hidden.name = this.name;
21240                 input.name = '_hidden_' + this.name;
21241             }
21242         }
21243         
21244         if (this.size) {
21245             input.cls += ' input-' + this.size;
21246         }
21247         
21248         var settings=this;
21249         
21250         ['xs','sm','md','lg'].map(function(size){
21251             if (settings[size]) {
21252                 cfg.cls += ' col-' + size + '-' + settings[size];
21253             }
21254         });
21255         
21256         var inputblock = input;
21257          
21258         if (this.before || this.after) {
21259             
21260             inputblock = {
21261                 cls : 'input-group',
21262                 cn :  [] 
21263             };
21264             
21265             if (this.before) {
21266                 inputblock.cn.push({
21267                     tag :'span',
21268                     cls : 'input-group-addon',
21269                     html : this.before
21270                 });
21271             }
21272             
21273             inputblock.cn.push(input);
21274             
21275             if(this.inputType != 'radio'){
21276                 inputblock.cn.push(hidden);
21277             }
21278             
21279             if (this.after) {
21280                 inputblock.cn.push({
21281                     tag :'span',
21282                     cls : 'input-group-addon',
21283                     html : this.after
21284                 });
21285             }
21286             
21287         }
21288         var boxLabelCfg = false;
21289         
21290         if(this.boxLabel){
21291            
21292             boxLabelCfg = {
21293                 tag: 'label',
21294                 //'for': id, // box label is handled by onclick - so no for...
21295                 cls: 'box-label',
21296                 html: this.boxLabel
21297             };
21298             if(this.tooltip){
21299                 boxLabelCfg.tooltip = this.tooltip;
21300             }
21301              
21302         }
21303         
21304         
21305         if (align ==='left' && this.fieldLabel.length) {
21306 //                Roo.log("left and has label");
21307             cfg.cn = [
21308                 {
21309                     tag: 'label',
21310                     'for' :  id,
21311                     cls : 'control-label',
21312                     html : this.fieldLabel
21313                 },
21314                 {
21315                     cls : "", 
21316                     cn: [
21317                         inputblock
21318                     ]
21319                 }
21320             ];
21321             
21322             if (boxLabelCfg) {
21323                 cfg.cn[1].cn.push(boxLabelCfg);
21324             }
21325             
21326             if(this.labelWidth > 12){
21327                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21328             }
21329             
21330             if(this.labelWidth < 13 && this.labelmd == 0){
21331                 this.labelmd = this.labelWidth;
21332             }
21333             
21334             if(this.labellg > 0){
21335                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21336                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21337             }
21338             
21339             if(this.labelmd > 0){
21340                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21341                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21342             }
21343             
21344             if(this.labelsm > 0){
21345                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21346                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21347             }
21348             
21349             if(this.labelxs > 0){
21350                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21351                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21352             }
21353             
21354         } else if ( this.fieldLabel.length) {
21355 //                Roo.log(" label");
21356                 cfg.cn = [
21357                    
21358                     {
21359                         tag: this.boxLabel ? 'span' : 'label',
21360                         'for': id,
21361                         cls: 'control-label box-input-label',
21362                         //cls : 'input-group-addon',
21363                         html : this.fieldLabel
21364                     },
21365                     
21366                     inputblock
21367                     
21368                 ];
21369                 if (boxLabelCfg) {
21370                     cfg.cn.push(boxLabelCfg);
21371                 }
21372
21373         } else {
21374             
21375 //                Roo.log(" no label && no align");
21376                 cfg.cn = [  inputblock ] ;
21377                 if (boxLabelCfg) {
21378                     cfg.cn.push(boxLabelCfg);
21379                 }
21380
21381                 
21382         }
21383         
21384        
21385         
21386         if(this.inputType != 'radio'){
21387             cfg.cn.push(hidden);
21388         }
21389         
21390         return cfg;
21391         
21392     },
21393     
21394     /**
21395      * return the real input element.
21396      */
21397     inputEl: function ()
21398     {
21399         return this.el.select('input.roo-' + this.inputType,true).first();
21400     },
21401     hiddenEl: function ()
21402     {
21403         return this.el.select('input.roo-hidden-value',true).first();
21404     },
21405     
21406     labelEl: function()
21407     {
21408         return this.el.select('label.control-label',true).first();
21409     },
21410     /* depricated... */
21411     
21412     label: function()
21413     {
21414         return this.labelEl();
21415     },
21416     
21417     boxLabelEl: function()
21418     {
21419         return this.el.select('label.box-label',true).first();
21420     },
21421     
21422     initEvents : function()
21423     {
21424 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21425         
21426         this.inputEl().on('click', this.onClick,  this);
21427         
21428         if (this.boxLabel) { 
21429             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
21430         }
21431         
21432         this.startValue = this.getValue();
21433         
21434         if(this.groupId){
21435             Roo.bootstrap.CheckBox.register(this);
21436         }
21437     },
21438     
21439     onClick : function(e)
21440     {   
21441         if(this.fireEvent('click', this, e) !== false){
21442             this.setChecked(!this.checked);
21443         }
21444         
21445     },
21446     
21447     setChecked : function(state,suppressEvent)
21448     {
21449         this.startValue = this.getValue();
21450
21451         if(this.inputType == 'radio'){
21452             
21453             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21454                 e.dom.checked = false;
21455             });
21456             
21457             this.inputEl().dom.checked = true;
21458             
21459             this.inputEl().dom.value = this.inputValue;
21460             
21461             if(suppressEvent !== true){
21462                 this.fireEvent('check', this, true);
21463             }
21464             
21465             this.validate();
21466             
21467             return;
21468         }
21469         
21470         this.checked = state;
21471         
21472         this.inputEl().dom.checked = state;
21473         
21474         
21475         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21476         
21477         if(suppressEvent !== true){
21478             this.fireEvent('check', this, state);
21479         }
21480         
21481         this.validate();
21482     },
21483     
21484     getValue : function()
21485     {
21486         if(this.inputType == 'radio'){
21487             return this.getGroupValue();
21488         }
21489         
21490         return this.hiddenEl().dom.value;
21491         
21492     },
21493     
21494     getGroupValue : function()
21495     {
21496         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21497             return '';
21498         }
21499         
21500         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21501     },
21502     
21503     setValue : function(v,suppressEvent)
21504     {
21505         if(this.inputType == 'radio'){
21506             this.setGroupValue(v, suppressEvent);
21507             return;
21508         }
21509         
21510         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21511         
21512         this.validate();
21513     },
21514     
21515     setGroupValue : function(v, suppressEvent)
21516     {
21517         this.startValue = this.getValue();
21518         
21519         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21520             e.dom.checked = false;
21521             
21522             if(e.dom.value == v){
21523                 e.dom.checked = true;
21524             }
21525         });
21526         
21527         if(suppressEvent !== true){
21528             this.fireEvent('check', this, true);
21529         }
21530
21531         this.validate();
21532         
21533         return;
21534     },
21535     
21536     validate : function()
21537     {
21538         if(this.getVisibilityEl().hasClass('hidden')){
21539             return true;
21540         }
21541         
21542         if(
21543                 this.disabled || 
21544                 (this.inputType == 'radio' && this.validateRadio()) ||
21545                 (this.inputType == 'checkbox' && this.validateCheckbox())
21546         ){
21547             this.markValid();
21548             return true;
21549         }
21550         
21551         this.markInvalid();
21552         return false;
21553     },
21554     
21555     validateRadio : function()
21556     {
21557         if(this.getVisibilityEl().hasClass('hidden')){
21558             return true;
21559         }
21560         
21561         if(this.allowBlank){
21562             return true;
21563         }
21564         
21565         var valid = false;
21566         
21567         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21568             if(!e.dom.checked){
21569                 return;
21570             }
21571             
21572             valid = true;
21573             
21574             return false;
21575         });
21576         
21577         return valid;
21578     },
21579     
21580     validateCheckbox : function()
21581     {
21582         if(!this.groupId){
21583             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21584             //return (this.getValue() == this.inputValue) ? true : false;
21585         }
21586         
21587         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21588         
21589         if(!group){
21590             return false;
21591         }
21592         
21593         var r = false;
21594         
21595         for(var i in group){
21596             if(group[i].el.isVisible(true)){
21597                 r = false;
21598                 break;
21599             }
21600             
21601             r = true;
21602         }
21603         
21604         for(var i in group){
21605             if(r){
21606                 break;
21607             }
21608             
21609             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21610         }
21611         
21612         return r;
21613     },
21614     
21615     /**
21616      * Mark this field as valid
21617      */
21618     markValid : function()
21619     {
21620         var _this = this;
21621         
21622         this.fireEvent('valid', this);
21623         
21624         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21625         
21626         if(this.groupId){
21627             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21628         }
21629         
21630         if(label){
21631             label.markValid();
21632         }
21633
21634         if(this.inputType == 'radio'){
21635             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21636                 var fg = e.findParent('.form-group', false, true);
21637                 if (Roo.bootstrap.version == 3) {
21638                     fg.removeClass([_this.invalidClass, _this.validClass]);
21639                     fg.addClass(_this.validClass);
21640                 } else {
21641                     fg.removeClass(['is-valid', 'is-invalid']);
21642                     fg.addClass('is-valid');
21643                 }
21644             });
21645             
21646             return;
21647         }
21648
21649         if(!this.groupId){
21650             var fg = this.el.findParent('.form-group', false, true);
21651             if (Roo.bootstrap.version == 3) {
21652                 fg.removeClass([this.invalidClass, this.validClass]);
21653                 fg.addClass(this.validClass);
21654             } else {
21655                 fg.removeClass(['is-valid', 'is-invalid']);
21656                 fg.addClass('is-valid');
21657             }
21658             return;
21659         }
21660         
21661         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21662         
21663         if(!group){
21664             return;
21665         }
21666         
21667         for(var i in group){
21668             var fg = group[i].el.findParent('.form-group', false, true);
21669             if (Roo.bootstrap.version == 3) {
21670                 fg.removeClass([this.invalidClass, this.validClass]);
21671                 fg.addClass(this.validClass);
21672             } else {
21673                 fg.removeClass(['is-valid', 'is-invalid']);
21674                 fg.addClass('is-valid');
21675             }
21676         }
21677     },
21678     
21679      /**
21680      * Mark this field as invalid
21681      * @param {String} msg The validation message
21682      */
21683     markInvalid : function(msg)
21684     {
21685         if(this.allowBlank){
21686             return;
21687         }
21688         
21689         var _this = this;
21690         
21691         this.fireEvent('invalid', this, msg);
21692         
21693         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21694         
21695         if(this.groupId){
21696             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21697         }
21698         
21699         if(label){
21700             label.markInvalid();
21701         }
21702             
21703         if(this.inputType == 'radio'){
21704             
21705             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21706                 var fg = e.findParent('.form-group', false, true);
21707                 if (Roo.bootstrap.version == 3) {
21708                     fg.removeClass([_this.invalidClass, _this.validClass]);
21709                     fg.addClass(_this.invalidClass);
21710                 } else {
21711                     fg.removeClass(['is-invalid', 'is-valid']);
21712                     fg.addClass('is-invalid');
21713                 }
21714             });
21715             
21716             return;
21717         }
21718         
21719         if(!this.groupId){
21720             var fg = this.el.findParent('.form-group', false, true);
21721             if (Roo.bootstrap.version == 3) {
21722                 fg.removeClass([_this.invalidClass, _this.validClass]);
21723                 fg.addClass(_this.invalidClass);
21724             } else {
21725                 fg.removeClass(['is-invalid', 'is-valid']);
21726                 fg.addClass('is-invalid');
21727             }
21728             return;
21729         }
21730         
21731         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21732         
21733         if(!group){
21734             return;
21735         }
21736         
21737         for(var i in group){
21738             var fg = group[i].el.findParent('.form-group', false, true);
21739             if (Roo.bootstrap.version == 3) {
21740                 fg.removeClass([_this.invalidClass, _this.validClass]);
21741                 fg.addClass(_this.invalidClass);
21742             } else {
21743                 fg.removeClass(['is-invalid', 'is-valid']);
21744                 fg.addClass('is-invalid');
21745             }
21746         }
21747         
21748     },
21749     
21750     clearInvalid : function()
21751     {
21752         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21753         
21754         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21755         
21756         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21757         
21758         if (label && label.iconEl) {
21759             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21760             label.iconEl.removeClass(['is-invalid', 'is-valid']);
21761         }
21762     },
21763     
21764     disable : function()
21765     {
21766         if(this.inputType != 'radio'){
21767             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21768             return;
21769         }
21770         
21771         var _this = this;
21772         
21773         if(this.rendered){
21774             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21775                 _this.getActionEl().addClass(this.disabledClass);
21776                 e.dom.disabled = true;
21777             });
21778         }
21779         
21780         this.disabled = true;
21781         this.fireEvent("disable", this);
21782         return this;
21783     },
21784
21785     enable : function()
21786     {
21787         if(this.inputType != 'radio'){
21788             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21789             return;
21790         }
21791         
21792         var _this = this;
21793         
21794         if(this.rendered){
21795             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21796                 _this.getActionEl().removeClass(this.disabledClass);
21797                 e.dom.disabled = false;
21798             });
21799         }
21800         
21801         this.disabled = false;
21802         this.fireEvent("enable", this);
21803         return this;
21804     },
21805     
21806     setBoxLabel : function(v)
21807     {
21808         this.boxLabel = v;
21809         
21810         if(this.rendered){
21811             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21812         }
21813     }
21814
21815 });
21816
21817 Roo.apply(Roo.bootstrap.CheckBox, {
21818     
21819     groups: {},
21820     
21821      /**
21822     * register a CheckBox Group
21823     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21824     */
21825     register : function(checkbox)
21826     {
21827         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21828             this.groups[checkbox.groupId] = {};
21829         }
21830         
21831         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21832             return;
21833         }
21834         
21835         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21836         
21837     },
21838     /**
21839     * fetch a CheckBox Group based on the group ID
21840     * @param {string} the group ID
21841     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21842     */
21843     get: function(groupId) {
21844         if (typeof(this.groups[groupId]) == 'undefined') {
21845             return false;
21846         }
21847         
21848         return this.groups[groupId] ;
21849     }
21850     
21851     
21852 });
21853 /*
21854  * - LGPL
21855  *
21856  * RadioItem
21857  * 
21858  */
21859
21860 /**
21861  * @class Roo.bootstrap.Radio
21862  * @extends Roo.bootstrap.Component
21863  * Bootstrap Radio class
21864  * @cfg {String} boxLabel - the label associated
21865  * @cfg {String} value - the value of radio
21866  * 
21867  * @constructor
21868  * Create a new Radio
21869  * @param {Object} config The config object
21870  */
21871 Roo.bootstrap.Radio = function(config){
21872     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21873     
21874 };
21875
21876 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21877     
21878     boxLabel : '',
21879     
21880     value : '',
21881     
21882     getAutoCreate : function()
21883     {
21884         var cfg = {
21885             tag : 'div',
21886             cls : 'form-group radio',
21887             cn : [
21888                 {
21889                     tag : 'label',
21890                     cls : 'box-label',
21891                     html : this.boxLabel
21892                 }
21893             ]
21894         };
21895         
21896         return cfg;
21897     },
21898     
21899     initEvents : function() 
21900     {
21901         this.parent().register(this);
21902         
21903         this.el.on('click', this.onClick, this);
21904         
21905     },
21906     
21907     onClick : function(e)
21908     {
21909         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21910             this.setChecked(true);
21911         }
21912     },
21913     
21914     setChecked : function(state, suppressEvent)
21915     {
21916         this.parent().setValue(this.value, suppressEvent);
21917         
21918     },
21919     
21920     setBoxLabel : function(v)
21921     {
21922         this.boxLabel = v;
21923         
21924         if(this.rendered){
21925             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21926         }
21927     }
21928     
21929 });
21930  
21931
21932  /*
21933  * - LGPL
21934  *
21935  * Input
21936  * 
21937  */
21938
21939 /**
21940  * @class Roo.bootstrap.SecurePass
21941  * @extends Roo.bootstrap.Input
21942  * Bootstrap SecurePass class
21943  *
21944  * 
21945  * @constructor
21946  * Create a new SecurePass
21947  * @param {Object} config The config object
21948  */
21949  
21950 Roo.bootstrap.SecurePass = function (config) {
21951     // these go here, so the translation tool can replace them..
21952     this.errors = {
21953         PwdEmpty: "Please type a password, and then retype it to confirm.",
21954         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21955         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21956         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21957         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21958         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21959         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21960         TooWeak: "Your password is Too Weak."
21961     },
21962     this.meterLabel = "Password strength:";
21963     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21964     this.meterClass = [
21965         "roo-password-meter-tooweak", 
21966         "roo-password-meter-weak", 
21967         "roo-password-meter-medium", 
21968         "roo-password-meter-strong", 
21969         "roo-password-meter-grey"
21970     ];
21971     
21972     this.errors = {};
21973     
21974     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21975 }
21976
21977 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21978     /**
21979      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21980      * {
21981      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21982      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21983      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21984      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21985      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21986      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21987      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21988      * })
21989      */
21990     // private
21991     
21992     meterWidth: 300,
21993     errorMsg :'',    
21994     errors: false,
21995     imageRoot: '/',
21996     /**
21997      * @cfg {String/Object} Label for the strength meter (defaults to
21998      * 'Password strength:')
21999      */
22000     // private
22001     meterLabel: '',
22002     /**
22003      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
22004      * ['Weak', 'Medium', 'Strong'])
22005      */
22006     // private    
22007     pwdStrengths: false,    
22008     // private
22009     strength: 0,
22010     // private
22011     _lastPwd: null,
22012     // private
22013     kCapitalLetter: 0,
22014     kSmallLetter: 1,
22015     kDigit: 2,
22016     kPunctuation: 3,
22017     
22018     insecure: false,
22019     // private
22020     initEvents: function ()
22021     {
22022         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
22023
22024         if (this.el.is('input[type=password]') && Roo.isSafari) {
22025             this.el.on('keydown', this.SafariOnKeyDown, this);
22026         }
22027
22028         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
22029     },
22030     // private
22031     onRender: function (ct, position)
22032     {
22033         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
22034         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
22035         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
22036
22037         this.trigger.createChild({
22038                    cn: [
22039                     {
22040                     //id: 'PwdMeter',
22041                     tag: 'div',
22042                     cls: 'roo-password-meter-grey col-xs-12',
22043                     style: {
22044                         //width: 0,
22045                         //width: this.meterWidth + 'px'                                                
22046                         }
22047                     },
22048                     {                            
22049                          cls: 'roo-password-meter-text'                          
22050                     }
22051                 ]            
22052         });
22053
22054          
22055         if (this.hideTrigger) {
22056             this.trigger.setDisplayed(false);
22057         }
22058         this.setSize(this.width || '', this.height || '');
22059     },
22060     // private
22061     onDestroy: function ()
22062     {
22063         if (this.trigger) {
22064             this.trigger.removeAllListeners();
22065             this.trigger.remove();
22066         }
22067         if (this.wrap) {
22068             this.wrap.remove();
22069         }
22070         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
22071     },
22072     // private
22073     checkStrength: function ()
22074     {
22075         var pwd = this.inputEl().getValue();
22076         if (pwd == this._lastPwd) {
22077             return;
22078         }
22079
22080         var strength;
22081         if (this.ClientSideStrongPassword(pwd)) {
22082             strength = 3;
22083         } else if (this.ClientSideMediumPassword(pwd)) {
22084             strength = 2;
22085         } else if (this.ClientSideWeakPassword(pwd)) {
22086             strength = 1;
22087         } else {
22088             strength = 0;
22089         }
22090         
22091         Roo.log('strength1: ' + strength);
22092         
22093         //var pm = this.trigger.child('div/div/div').dom;
22094         var pm = this.trigger.child('div/div');
22095         pm.removeClass(this.meterClass);
22096         pm.addClass(this.meterClass[strength]);
22097                 
22098         
22099         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
22100                 
22101         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
22102         
22103         this._lastPwd = pwd;
22104     },
22105     reset: function ()
22106     {
22107         Roo.bootstrap.SecurePass.superclass.reset.call(this);
22108         
22109         this._lastPwd = '';
22110         
22111         var pm = this.trigger.child('div/div');
22112         pm.removeClass(this.meterClass);
22113         pm.addClass('roo-password-meter-grey');        
22114         
22115         
22116         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
22117         
22118         pt.innerHTML = '';
22119         this.inputEl().dom.type='password';
22120     },
22121     // private
22122     validateValue: function (value)
22123     {
22124         
22125         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
22126             return false;
22127         }
22128         if (value.length == 0) {
22129             if (this.allowBlank) {
22130                 this.clearInvalid();
22131                 return true;
22132             }
22133
22134             this.markInvalid(this.errors.PwdEmpty);
22135             this.errorMsg = this.errors.PwdEmpty;
22136             return false;
22137         }
22138         
22139         if(this.insecure){
22140             return true;
22141         }
22142         
22143         if ('[\x21-\x7e]*'.match(value)) {
22144             this.markInvalid(this.errors.PwdBadChar);
22145             this.errorMsg = this.errors.PwdBadChar;
22146             return false;
22147         }
22148         if (value.length < 6) {
22149             this.markInvalid(this.errors.PwdShort);
22150             this.errorMsg = this.errors.PwdShort;
22151             return false;
22152         }
22153         if (value.length > 16) {
22154             this.markInvalid(this.errors.PwdLong);
22155             this.errorMsg = this.errors.PwdLong;
22156             return false;
22157         }
22158         var strength;
22159         if (this.ClientSideStrongPassword(value)) {
22160             strength = 3;
22161         } else if (this.ClientSideMediumPassword(value)) {
22162             strength = 2;
22163         } else if (this.ClientSideWeakPassword(value)) {
22164             strength = 1;
22165         } else {
22166             strength = 0;
22167         }
22168
22169         
22170         if (strength < 2) {
22171             //this.markInvalid(this.errors.TooWeak);
22172             this.errorMsg = this.errors.TooWeak;
22173             //return false;
22174         }
22175         
22176         
22177         console.log('strength2: ' + strength);
22178         
22179         //var pm = this.trigger.child('div/div/div').dom;
22180         
22181         var pm = this.trigger.child('div/div');
22182         pm.removeClass(this.meterClass);
22183         pm.addClass(this.meterClass[strength]);
22184                 
22185         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
22186                 
22187         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
22188         
22189         this.errorMsg = ''; 
22190         return true;
22191     },
22192     // private
22193     CharacterSetChecks: function (type)
22194     {
22195         this.type = type;
22196         this.fResult = false;
22197     },
22198     // private
22199     isctype: function (character, type)
22200     {
22201         switch (type) {  
22202             case this.kCapitalLetter:
22203                 if (character >= 'A' && character <= 'Z') {
22204                     return true;
22205                 }
22206                 break;
22207             
22208             case this.kSmallLetter:
22209                 if (character >= 'a' && character <= 'z') {
22210                     return true;
22211                 }
22212                 break;
22213             
22214             case this.kDigit:
22215                 if (character >= '0' && character <= '9') {
22216                     return true;
22217                 }
22218                 break;
22219             
22220             case this.kPunctuation:
22221                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
22222                     return true;
22223                 }
22224                 break;
22225             
22226             default:
22227                 return false;
22228         }
22229
22230     },
22231     // private
22232     IsLongEnough: function (pwd, size)
22233     {
22234         return !(pwd == null || isNaN(size) || pwd.length < size);
22235     },
22236     // private
22237     SpansEnoughCharacterSets: function (word, nb)
22238     {
22239         if (!this.IsLongEnough(word, nb))
22240         {
22241             return false;
22242         }
22243
22244         var characterSetChecks = new Array(
22245             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
22246             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
22247         );
22248         
22249         for (var index = 0; index < word.length; ++index) {
22250             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22251                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
22252                     characterSetChecks[nCharSet].fResult = true;
22253                     break;
22254                 }
22255             }
22256         }
22257
22258         var nCharSets = 0;
22259         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22260             if (characterSetChecks[nCharSet].fResult) {
22261                 ++nCharSets;
22262             }
22263         }
22264
22265         if (nCharSets < nb) {
22266             return false;
22267         }
22268         return true;
22269     },
22270     // private
22271     ClientSideStrongPassword: function (pwd)
22272     {
22273         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
22274     },
22275     // private
22276     ClientSideMediumPassword: function (pwd)
22277     {
22278         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
22279     },
22280     // private
22281     ClientSideWeakPassword: function (pwd)
22282     {
22283         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
22284     }
22285           
22286 })//<script type="text/javascript">
22287
22288 /*
22289  * Based  Ext JS Library 1.1.1
22290  * Copyright(c) 2006-2007, Ext JS, LLC.
22291  * LGPL
22292  *
22293  */
22294  
22295 /**
22296  * @class Roo.HtmlEditorCore
22297  * @extends Roo.Component
22298  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
22299  *
22300  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22301  */
22302
22303 Roo.HtmlEditorCore = function(config){
22304     
22305     
22306     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22307     
22308     
22309     this.addEvents({
22310         /**
22311          * @event initialize
22312          * Fires when the editor is fully initialized (including the iframe)
22313          * @param {Roo.HtmlEditorCore} this
22314          */
22315         initialize: true,
22316         /**
22317          * @event activate
22318          * Fires when the editor is first receives the focus. Any insertion must wait
22319          * until after this event.
22320          * @param {Roo.HtmlEditorCore} this
22321          */
22322         activate: true,
22323          /**
22324          * @event beforesync
22325          * Fires before the textarea is updated with content from the editor iframe. Return false
22326          * to cancel the sync.
22327          * @param {Roo.HtmlEditorCore} this
22328          * @param {String} html
22329          */
22330         beforesync: true,
22331          /**
22332          * @event beforepush
22333          * Fires before the iframe editor is updated with content from the textarea. Return false
22334          * to cancel the push.
22335          * @param {Roo.HtmlEditorCore} this
22336          * @param {String} html
22337          */
22338         beforepush: true,
22339          /**
22340          * @event sync
22341          * Fires when the textarea is updated with content from the editor iframe.
22342          * @param {Roo.HtmlEditorCore} this
22343          * @param {String} html
22344          */
22345         sync: true,
22346          /**
22347          * @event push
22348          * Fires when the iframe editor is updated with content from the textarea.
22349          * @param {Roo.HtmlEditorCore} this
22350          * @param {String} html
22351          */
22352         push: true,
22353         
22354         /**
22355          * @event editorevent
22356          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22357          * @param {Roo.HtmlEditorCore} this
22358          */
22359         editorevent: true
22360         
22361     });
22362     
22363     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22364     
22365     // defaults : white / black...
22366     this.applyBlacklists();
22367     
22368     
22369     
22370 };
22371
22372
22373 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
22374
22375
22376      /**
22377      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
22378      */
22379     
22380     owner : false,
22381     
22382      /**
22383      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22384      *                        Roo.resizable.
22385      */
22386     resizable : false,
22387      /**
22388      * @cfg {Number} height (in pixels)
22389      */   
22390     height: 300,
22391    /**
22392      * @cfg {Number} width (in pixels)
22393      */   
22394     width: 500,
22395     
22396     /**
22397      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22398      * 
22399      */
22400     stylesheets: false,
22401     
22402     // id of frame..
22403     frameId: false,
22404     
22405     // private properties
22406     validationEvent : false,
22407     deferHeight: true,
22408     initialized : false,
22409     activated : false,
22410     sourceEditMode : false,
22411     onFocus : Roo.emptyFn,
22412     iframePad:3,
22413     hideMode:'offsets',
22414     
22415     clearUp: true,
22416     
22417     // blacklist + whitelisted elements..
22418     black: false,
22419     white: false,
22420      
22421     bodyCls : '',
22422
22423     /**
22424      * Protected method that will not generally be called directly. It
22425      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22426      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22427      */
22428     getDocMarkup : function(){
22429         // body styles..
22430         var st = '';
22431         
22432         // inherit styels from page...?? 
22433         if (this.stylesheets === false) {
22434             
22435             Roo.get(document.head).select('style').each(function(node) {
22436                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22437             });
22438             
22439             Roo.get(document.head).select('link').each(function(node) { 
22440                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22441             });
22442             
22443         } else if (!this.stylesheets.length) {
22444                 // simple..
22445                 st = '<style type="text/css">' +
22446                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22447                    '</style>';
22448         } else { 
22449             st = '<style type="text/css">' +
22450                     this.stylesheets +
22451                 '</style>';
22452         }
22453         
22454         st +=  '<style type="text/css">' +
22455             'IMG { cursor: pointer } ' +
22456         '</style>';
22457
22458         var cls = 'roo-htmleditor-body';
22459         
22460         if(this.bodyCls.length){
22461             cls += ' ' + this.bodyCls;
22462         }
22463         
22464         return '<html><head>' + st  +
22465             //<style type="text/css">' +
22466             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22467             //'</style>' +
22468             ' </head><body class="' +  cls + '"></body></html>';
22469     },
22470
22471     // private
22472     onRender : function(ct, position)
22473     {
22474         var _t = this;
22475         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22476         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22477         
22478         
22479         this.el.dom.style.border = '0 none';
22480         this.el.dom.setAttribute('tabIndex', -1);
22481         this.el.addClass('x-hidden hide');
22482         
22483         
22484         
22485         if(Roo.isIE){ // fix IE 1px bogus margin
22486             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22487         }
22488        
22489         
22490         this.frameId = Roo.id();
22491         
22492          
22493         
22494         var iframe = this.owner.wrap.createChild({
22495             tag: 'iframe',
22496             cls: 'form-control', // bootstrap..
22497             id: this.frameId,
22498             name: this.frameId,
22499             frameBorder : 'no',
22500             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22501         }, this.el
22502         );
22503         
22504         
22505         this.iframe = iframe.dom;
22506
22507          this.assignDocWin();
22508         
22509         this.doc.designMode = 'on';
22510        
22511         this.doc.open();
22512         this.doc.write(this.getDocMarkup());
22513         this.doc.close();
22514
22515         
22516         var task = { // must defer to wait for browser to be ready
22517             run : function(){
22518                 //console.log("run task?" + this.doc.readyState);
22519                 this.assignDocWin();
22520                 if(this.doc.body || this.doc.readyState == 'complete'){
22521                     try {
22522                         this.doc.designMode="on";
22523                     } catch (e) {
22524                         return;
22525                     }
22526                     Roo.TaskMgr.stop(task);
22527                     this.initEditor.defer(10, this);
22528                 }
22529             },
22530             interval : 10,
22531             duration: 10000,
22532             scope: this
22533         };
22534         Roo.TaskMgr.start(task);
22535
22536     },
22537
22538     // private
22539     onResize : function(w, h)
22540     {
22541          Roo.log('resize: ' +w + ',' + h );
22542         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22543         if(!this.iframe){
22544             return;
22545         }
22546         if(typeof w == 'number'){
22547             
22548             this.iframe.style.width = w + 'px';
22549         }
22550         if(typeof h == 'number'){
22551             
22552             this.iframe.style.height = h + 'px';
22553             if(this.doc){
22554                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22555             }
22556         }
22557         
22558     },
22559
22560     /**
22561      * Toggles the editor between standard and source edit mode.
22562      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22563      */
22564     toggleSourceEdit : function(sourceEditMode){
22565         
22566         this.sourceEditMode = sourceEditMode === true;
22567         
22568         if(this.sourceEditMode){
22569  
22570             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22571             
22572         }else{
22573             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22574             //this.iframe.className = '';
22575             this.deferFocus();
22576         }
22577         //this.setSize(this.owner.wrap.getSize());
22578         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22579     },
22580
22581     
22582   
22583
22584     /**
22585      * Protected method that will not generally be called directly. If you need/want
22586      * custom HTML cleanup, this is the method you should override.
22587      * @param {String} html The HTML to be cleaned
22588      * return {String} The cleaned HTML
22589      */
22590     cleanHtml : function(html){
22591         html = String(html);
22592         if(html.length > 5){
22593             if(Roo.isSafari){ // strip safari nonsense
22594                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22595             }
22596         }
22597         if(html == '&nbsp;'){
22598             html = '';
22599         }
22600         return html;
22601     },
22602
22603     /**
22604      * HTML Editor -> Textarea
22605      * Protected method that will not generally be called directly. Syncs the contents
22606      * of the editor iframe with the textarea.
22607      */
22608     syncValue : function(){
22609         if(this.initialized){
22610             var bd = (this.doc.body || this.doc.documentElement);
22611             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22612             var html = bd.innerHTML;
22613             if(Roo.isSafari){
22614                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22615                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22616                 if(m && m[1]){
22617                     html = '<div style="'+m[0]+'">' + html + '</div>';
22618                 }
22619             }
22620             html = this.cleanHtml(html);
22621             // fix up the special chars.. normaly like back quotes in word...
22622             // however we do not want to do this with chinese..
22623             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
22624                 
22625                 var cc = match.charCodeAt();
22626
22627                 // Get the character value, handling surrogate pairs
22628                 if (match.length == 2) {
22629                     // It's a surrogate pair, calculate the Unicode code point
22630                     var high = match.charCodeAt(0) - 0xD800;
22631                     var low  = match.charCodeAt(1) - 0xDC00;
22632                     cc = (high * 0x400) + low + 0x10000;
22633                 }  else if (
22634                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22635                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22636                     (cc >= 0xf900 && cc < 0xfb00 )
22637                 ) {
22638                         return match;
22639                 }  
22640          
22641                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
22642                 return "&#" + cc + ";";
22643                 
22644                 
22645             });
22646             
22647             
22648              
22649             if(this.owner.fireEvent('beforesync', this, html) !== false){
22650                 this.el.dom.value = html;
22651                 this.owner.fireEvent('sync', this, html);
22652             }
22653         }
22654     },
22655
22656     /**
22657      * Protected method that will not generally be called directly. Pushes the value of the textarea
22658      * into the iframe editor.
22659      */
22660     pushValue : function(){
22661         if(this.initialized){
22662             var v = this.el.dom.value.trim();
22663             
22664 //            if(v.length < 1){
22665 //                v = '&#160;';
22666 //            }
22667             
22668             if(this.owner.fireEvent('beforepush', this, v) !== false){
22669                 var d = (this.doc.body || this.doc.documentElement);
22670                 d.innerHTML = v;
22671                 this.cleanUpPaste();
22672                 this.el.dom.value = d.innerHTML;
22673                 this.owner.fireEvent('push', this, v);
22674             }
22675         }
22676     },
22677
22678     // private
22679     deferFocus : function(){
22680         this.focus.defer(10, this);
22681     },
22682
22683     // doc'ed in Field
22684     focus : function(){
22685         if(this.win && !this.sourceEditMode){
22686             this.win.focus();
22687         }else{
22688             this.el.focus();
22689         }
22690     },
22691     
22692     assignDocWin: function()
22693     {
22694         var iframe = this.iframe;
22695         
22696          if(Roo.isIE){
22697             this.doc = iframe.contentWindow.document;
22698             this.win = iframe.contentWindow;
22699         } else {
22700 //            if (!Roo.get(this.frameId)) {
22701 //                return;
22702 //            }
22703 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22704 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22705             
22706             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22707                 return;
22708             }
22709             
22710             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22711             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22712         }
22713     },
22714     
22715     // private
22716     initEditor : function(){
22717         //console.log("INIT EDITOR");
22718         this.assignDocWin();
22719         
22720         
22721         
22722         this.doc.designMode="on";
22723         this.doc.open();
22724         this.doc.write(this.getDocMarkup());
22725         this.doc.close();
22726         
22727         var dbody = (this.doc.body || this.doc.documentElement);
22728         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22729         // this copies styles from the containing element into thsi one..
22730         // not sure why we need all of this..
22731         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22732         
22733         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22734         //ss['background-attachment'] = 'fixed'; // w3c
22735         dbody.bgProperties = 'fixed'; // ie
22736         //Roo.DomHelper.applyStyles(dbody, ss);
22737         Roo.EventManager.on(this.doc, {
22738             //'mousedown': this.onEditorEvent,
22739             'mouseup': this.onEditorEvent,
22740             'dblclick': this.onEditorEvent,
22741             'click': this.onEditorEvent,
22742             'keyup': this.onEditorEvent,
22743             buffer:100,
22744             scope: this
22745         });
22746         if(Roo.isGecko){
22747             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22748         }
22749         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22750             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22751         }
22752         this.initialized = true;
22753
22754         this.owner.fireEvent('initialize', this);
22755         this.pushValue();
22756     },
22757
22758     // private
22759     onDestroy : function(){
22760         
22761         
22762         
22763         if(this.rendered){
22764             
22765             //for (var i =0; i < this.toolbars.length;i++) {
22766             //    // fixme - ask toolbars for heights?
22767             //    this.toolbars[i].onDestroy();
22768            // }
22769             
22770             //this.wrap.dom.innerHTML = '';
22771             //this.wrap.remove();
22772         }
22773     },
22774
22775     // private
22776     onFirstFocus : function(){
22777         
22778         this.assignDocWin();
22779         
22780         
22781         this.activated = true;
22782          
22783     
22784         if(Roo.isGecko){ // prevent silly gecko errors
22785             this.win.focus();
22786             var s = this.win.getSelection();
22787             if(!s.focusNode || s.focusNode.nodeType != 3){
22788                 var r = s.getRangeAt(0);
22789                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22790                 r.collapse(true);
22791                 this.deferFocus();
22792             }
22793             try{
22794                 this.execCmd('useCSS', true);
22795                 this.execCmd('styleWithCSS', false);
22796             }catch(e){}
22797         }
22798         this.owner.fireEvent('activate', this);
22799     },
22800
22801     // private
22802     adjustFont: function(btn){
22803         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22804         //if(Roo.isSafari){ // safari
22805         //    adjust *= 2;
22806        // }
22807         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22808         if(Roo.isSafari){ // safari
22809             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22810             v =  (v < 10) ? 10 : v;
22811             v =  (v > 48) ? 48 : v;
22812             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22813             
22814         }
22815         
22816         
22817         v = Math.max(1, v+adjust);
22818         
22819         this.execCmd('FontSize', v  );
22820     },
22821
22822     onEditorEvent : function(e)
22823     {
22824         this.owner.fireEvent('editorevent', this, e);
22825       //  this.updateToolbar();
22826         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22827     },
22828
22829     insertTag : function(tg)
22830     {
22831         // could be a bit smarter... -> wrap the current selected tRoo..
22832         if (tg.toLowerCase() == 'span' ||
22833             tg.toLowerCase() == 'code' ||
22834             tg.toLowerCase() == 'sup' ||
22835             tg.toLowerCase() == 'sub' 
22836             ) {
22837             
22838             range = this.createRange(this.getSelection());
22839             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22840             wrappingNode.appendChild(range.extractContents());
22841             range.insertNode(wrappingNode);
22842
22843             return;
22844             
22845             
22846             
22847         }
22848         this.execCmd("formatblock",   tg);
22849         
22850     },
22851     
22852     insertText : function(txt)
22853     {
22854         
22855         
22856         var range = this.createRange();
22857         range.deleteContents();
22858                //alert(Sender.getAttribute('label'));
22859                
22860         range.insertNode(this.doc.createTextNode(txt));
22861     } ,
22862     
22863      
22864
22865     /**
22866      * Executes a Midas editor command on the editor document and performs necessary focus and
22867      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22868      * @param {String} cmd The Midas command
22869      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22870      */
22871     relayCmd : function(cmd, value){
22872         this.win.focus();
22873         this.execCmd(cmd, value);
22874         this.owner.fireEvent('editorevent', this);
22875         //this.updateToolbar();
22876         this.owner.deferFocus();
22877     },
22878
22879     /**
22880      * Executes a Midas editor command directly on the editor document.
22881      * For visual commands, you should use {@link #relayCmd} instead.
22882      * <b>This should only be called after the editor is initialized.</b>
22883      * @param {String} cmd The Midas command
22884      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22885      */
22886     execCmd : function(cmd, value){
22887         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22888         this.syncValue();
22889     },
22890  
22891  
22892    
22893     /**
22894      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22895      * to insert tRoo.
22896      * @param {String} text | dom node.. 
22897      */
22898     insertAtCursor : function(text)
22899     {
22900         
22901         if(!this.activated){
22902             return;
22903         }
22904         /*
22905         if(Roo.isIE){
22906             this.win.focus();
22907             var r = this.doc.selection.createRange();
22908             if(r){
22909                 r.collapse(true);
22910                 r.pasteHTML(text);
22911                 this.syncValue();
22912                 this.deferFocus();
22913             
22914             }
22915             return;
22916         }
22917         */
22918         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22919             this.win.focus();
22920             
22921             
22922             // from jquery ui (MIT licenced)
22923             var range, node;
22924             var win = this.win;
22925             
22926             if (win.getSelection && win.getSelection().getRangeAt) {
22927                 range = win.getSelection().getRangeAt(0);
22928                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22929                 range.insertNode(node);
22930             } else if (win.document.selection && win.document.selection.createRange) {
22931                 // no firefox support
22932                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22933                 win.document.selection.createRange().pasteHTML(txt);
22934             } else {
22935                 // no firefox support
22936                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22937                 this.execCmd('InsertHTML', txt);
22938             } 
22939             
22940             this.syncValue();
22941             
22942             this.deferFocus();
22943         }
22944     },
22945  // private
22946     mozKeyPress : function(e){
22947         if(e.ctrlKey){
22948             var c = e.getCharCode(), cmd;
22949           
22950             if(c > 0){
22951                 c = String.fromCharCode(c).toLowerCase();
22952                 switch(c){
22953                     case 'b':
22954                         cmd = 'bold';
22955                         break;
22956                     case 'i':
22957                         cmd = 'italic';
22958                         break;
22959                     
22960                     case 'u':
22961                         cmd = 'underline';
22962                         break;
22963                     
22964                     case 'v':
22965                         this.cleanUpPaste.defer(100, this);
22966                         return;
22967                         
22968                 }
22969                 if(cmd){
22970                     this.win.focus();
22971                     this.execCmd(cmd);
22972                     this.deferFocus();
22973                     e.preventDefault();
22974                 }
22975                 
22976             }
22977         }
22978     },
22979
22980     // private
22981     fixKeys : function(){ // load time branching for fastest keydown performance
22982         if(Roo.isIE){
22983             return function(e){
22984                 var k = e.getKey(), r;
22985                 if(k == e.TAB){
22986                     e.stopEvent();
22987                     r = this.doc.selection.createRange();
22988                     if(r){
22989                         r.collapse(true);
22990                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22991                         this.deferFocus();
22992                     }
22993                     return;
22994                 }
22995                 
22996                 if(k == e.ENTER){
22997                     r = this.doc.selection.createRange();
22998                     if(r){
22999                         var target = r.parentElement();
23000                         if(!target || target.tagName.toLowerCase() != 'li'){
23001                             e.stopEvent();
23002                             r.pasteHTML('<br />');
23003                             r.collapse(false);
23004                             r.select();
23005                         }
23006                     }
23007                 }
23008                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23009                     this.cleanUpPaste.defer(100, this);
23010                     return;
23011                 }
23012                 
23013                 
23014             };
23015         }else if(Roo.isOpera){
23016             return function(e){
23017                 var k = e.getKey();
23018                 if(k == e.TAB){
23019                     e.stopEvent();
23020                     this.win.focus();
23021                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
23022                     this.deferFocus();
23023                 }
23024                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23025                     this.cleanUpPaste.defer(100, this);
23026                     return;
23027                 }
23028                 
23029             };
23030         }else if(Roo.isSafari){
23031             return function(e){
23032                 var k = e.getKey();
23033                 
23034                 if(k == e.TAB){
23035                     e.stopEvent();
23036                     this.execCmd('InsertText','\t');
23037                     this.deferFocus();
23038                     return;
23039                 }
23040                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23041                     this.cleanUpPaste.defer(100, this);
23042                     return;
23043                 }
23044                 
23045              };
23046         }
23047     }(),
23048     
23049     getAllAncestors: function()
23050     {
23051         var p = this.getSelectedNode();
23052         var a = [];
23053         if (!p) {
23054             a.push(p); // push blank onto stack..
23055             p = this.getParentElement();
23056         }
23057         
23058         
23059         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
23060             a.push(p);
23061             p = p.parentNode;
23062         }
23063         a.push(this.doc.body);
23064         return a;
23065     },
23066     lastSel : false,
23067     lastSelNode : false,
23068     
23069     
23070     getSelection : function() 
23071     {
23072         this.assignDocWin();
23073         return Roo.isIE ? this.doc.selection : this.win.getSelection();
23074     },
23075     
23076     getSelectedNode: function() 
23077     {
23078         // this may only work on Gecko!!!
23079         
23080         // should we cache this!!!!
23081         
23082         
23083         
23084          
23085         var range = this.createRange(this.getSelection()).cloneRange();
23086         
23087         if (Roo.isIE) {
23088             var parent = range.parentElement();
23089             while (true) {
23090                 var testRange = range.duplicate();
23091                 testRange.moveToElementText(parent);
23092                 if (testRange.inRange(range)) {
23093                     break;
23094                 }
23095                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
23096                     break;
23097                 }
23098                 parent = parent.parentElement;
23099             }
23100             return parent;
23101         }
23102         
23103         // is ancestor a text element.
23104         var ac =  range.commonAncestorContainer;
23105         if (ac.nodeType == 3) {
23106             ac = ac.parentNode;
23107         }
23108         
23109         var ar = ac.childNodes;
23110          
23111         var nodes = [];
23112         var other_nodes = [];
23113         var has_other_nodes = false;
23114         for (var i=0;i<ar.length;i++) {
23115             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
23116                 continue;
23117             }
23118             // fullly contained node.
23119             
23120             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
23121                 nodes.push(ar[i]);
23122                 continue;
23123             }
23124             
23125             // probably selected..
23126             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
23127                 other_nodes.push(ar[i]);
23128                 continue;
23129             }
23130             // outer..
23131             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
23132                 continue;
23133             }
23134             
23135             
23136             has_other_nodes = true;
23137         }
23138         if (!nodes.length && other_nodes.length) {
23139             nodes= other_nodes;
23140         }
23141         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
23142             return false;
23143         }
23144         
23145         return nodes[0];
23146     },
23147     createRange: function(sel)
23148     {
23149         // this has strange effects when using with 
23150         // top toolbar - not sure if it's a great idea.
23151         //this.editor.contentWindow.focus();
23152         if (typeof sel != "undefined") {
23153             try {
23154                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
23155             } catch(e) {
23156                 return this.doc.createRange();
23157             }
23158         } else {
23159             return this.doc.createRange();
23160         }
23161     },
23162     getParentElement: function()
23163     {
23164         
23165         this.assignDocWin();
23166         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
23167         
23168         var range = this.createRange(sel);
23169          
23170         try {
23171             var p = range.commonAncestorContainer;
23172             while (p.nodeType == 3) { // text node
23173                 p = p.parentNode;
23174             }
23175             return p;
23176         } catch (e) {
23177             return null;
23178         }
23179     
23180     },
23181     /***
23182      *
23183      * Range intersection.. the hard stuff...
23184      *  '-1' = before
23185      *  '0' = hits..
23186      *  '1' = after.
23187      *         [ -- selected range --- ]
23188      *   [fail]                        [fail]
23189      *
23190      *    basically..
23191      *      if end is before start or  hits it. fail.
23192      *      if start is after end or hits it fail.
23193      *
23194      *   if either hits (but other is outside. - then it's not 
23195      *   
23196      *    
23197      **/
23198     
23199     
23200     // @see http://www.thismuchiknow.co.uk/?p=64.
23201     rangeIntersectsNode : function(range, node)
23202     {
23203         var nodeRange = node.ownerDocument.createRange();
23204         try {
23205             nodeRange.selectNode(node);
23206         } catch (e) {
23207             nodeRange.selectNodeContents(node);
23208         }
23209     
23210         var rangeStartRange = range.cloneRange();
23211         rangeStartRange.collapse(true);
23212     
23213         var rangeEndRange = range.cloneRange();
23214         rangeEndRange.collapse(false);
23215     
23216         var nodeStartRange = nodeRange.cloneRange();
23217         nodeStartRange.collapse(true);
23218     
23219         var nodeEndRange = nodeRange.cloneRange();
23220         nodeEndRange.collapse(false);
23221     
23222         return rangeStartRange.compareBoundaryPoints(
23223                  Range.START_TO_START, nodeEndRange) == -1 &&
23224                rangeEndRange.compareBoundaryPoints(
23225                  Range.START_TO_START, nodeStartRange) == 1;
23226         
23227          
23228     },
23229     rangeCompareNode : function(range, node)
23230     {
23231         var nodeRange = node.ownerDocument.createRange();
23232         try {
23233             nodeRange.selectNode(node);
23234         } catch (e) {
23235             nodeRange.selectNodeContents(node);
23236         }
23237         
23238         
23239         range.collapse(true);
23240     
23241         nodeRange.collapse(true);
23242      
23243         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
23244         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
23245          
23246         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
23247         
23248         var nodeIsBefore   =  ss == 1;
23249         var nodeIsAfter    = ee == -1;
23250         
23251         if (nodeIsBefore && nodeIsAfter) {
23252             return 0; // outer
23253         }
23254         if (!nodeIsBefore && nodeIsAfter) {
23255             return 1; //right trailed.
23256         }
23257         
23258         if (nodeIsBefore && !nodeIsAfter) {
23259             return 2;  // left trailed.
23260         }
23261         // fully contined.
23262         return 3;
23263     },
23264
23265     // private? - in a new class?
23266     cleanUpPaste :  function()
23267     {
23268         // cleans up the whole document..
23269         Roo.log('cleanuppaste');
23270         
23271         this.cleanUpChildren(this.doc.body);
23272         var clean = this.cleanWordChars(this.doc.body.innerHTML);
23273         if (clean != this.doc.body.innerHTML) {
23274             this.doc.body.innerHTML = clean;
23275         }
23276         
23277     },
23278     
23279     cleanWordChars : function(input) {// change the chars to hex code
23280         var he = Roo.HtmlEditorCore;
23281         
23282         var output = input;
23283         Roo.each(he.swapCodes, function(sw) { 
23284             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
23285             
23286             output = output.replace(swapper, sw[1]);
23287         });
23288         
23289         return output;
23290     },
23291     
23292     
23293     cleanUpChildren : function (n)
23294     {
23295         if (!n.childNodes.length) {
23296             return;
23297         }
23298         for (var i = n.childNodes.length-1; i > -1 ; i--) {
23299            this.cleanUpChild(n.childNodes[i]);
23300         }
23301     },
23302     
23303     
23304         
23305     
23306     cleanUpChild : function (node)
23307     {
23308         var ed = this;
23309         //console.log(node);
23310         if (node.nodeName == "#text") {
23311             // clean up silly Windows -- stuff?
23312             return; 
23313         }
23314         if (node.nodeName == "#comment") {
23315             node.parentNode.removeChild(node);
23316             // clean up silly Windows -- stuff?
23317             return; 
23318         }
23319         var lcname = node.tagName.toLowerCase();
23320         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
23321         // whitelist of tags..
23322         
23323         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
23324             // remove node.
23325             node.parentNode.removeChild(node);
23326             return;
23327             
23328         }
23329         
23330         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23331         
23332         // spans with no attributes - just remove them..
23333         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
23334             remove_keep_children = true;
23335         }
23336         
23337         // remove <a name=....> as rendering on yahoo mailer is borked with this.
23338         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23339         
23340         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23341         //    remove_keep_children = true;
23342         //}
23343         
23344         if (remove_keep_children) {
23345             this.cleanUpChildren(node);
23346             // inserts everything just before this node...
23347             while (node.childNodes.length) {
23348                 var cn = node.childNodes[0];
23349                 node.removeChild(cn);
23350                 node.parentNode.insertBefore(cn, node);
23351             }
23352             node.parentNode.removeChild(node);
23353             return;
23354         }
23355         
23356         if (!node.attributes || !node.attributes.length) {
23357             
23358           
23359             
23360             
23361             this.cleanUpChildren(node);
23362             return;
23363         }
23364         
23365         function cleanAttr(n,v)
23366         {
23367             
23368             if (v.match(/^\./) || v.match(/^\//)) {
23369                 return;
23370             }
23371             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23372                 return;
23373             }
23374             if (v.match(/^#/)) {
23375                 return;
23376             }
23377 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23378             node.removeAttribute(n);
23379             
23380         }
23381         
23382         var cwhite = this.cwhite;
23383         var cblack = this.cblack;
23384             
23385         function cleanStyle(n,v)
23386         {
23387             if (v.match(/expression/)) { //XSS?? should we even bother..
23388                 node.removeAttribute(n);
23389                 return;
23390             }
23391             
23392             var parts = v.split(/;/);
23393             var clean = [];
23394             
23395             Roo.each(parts, function(p) {
23396                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23397                 if (!p.length) {
23398                     return true;
23399                 }
23400                 var l = p.split(':').shift().replace(/\s+/g,'');
23401                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23402                 
23403                 if ( cwhite.length && cblack.indexOf(l) > -1) {
23404 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23405                     //node.removeAttribute(n);
23406                     return true;
23407                 }
23408                 //Roo.log()
23409                 // only allow 'c whitelisted system attributes'
23410                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
23411 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23412                     //node.removeAttribute(n);
23413                     return true;
23414                 }
23415                 
23416                 
23417                  
23418                 
23419                 clean.push(p);
23420                 return true;
23421             });
23422             if (clean.length) { 
23423                 node.setAttribute(n, clean.join(';'));
23424             } else {
23425                 node.removeAttribute(n);
23426             }
23427             
23428         }
23429         
23430         
23431         for (var i = node.attributes.length-1; i > -1 ; i--) {
23432             var a = node.attributes[i];
23433             //console.log(a);
23434             
23435             if (a.name.toLowerCase().substr(0,2)=='on')  {
23436                 node.removeAttribute(a.name);
23437                 continue;
23438             }
23439             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23440                 node.removeAttribute(a.name);
23441                 continue;
23442             }
23443             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23444                 cleanAttr(a.name,a.value); // fixme..
23445                 continue;
23446             }
23447             if (a.name == 'style') {
23448                 cleanStyle(a.name,a.value);
23449                 continue;
23450             }
23451             /// clean up MS crap..
23452             // tecnically this should be a list of valid class'es..
23453             
23454             
23455             if (a.name == 'class') {
23456                 if (a.value.match(/^Mso/)) {
23457                     node.removeAttribute('class');
23458                 }
23459                 
23460                 if (a.value.match(/^body$/)) {
23461                     node.removeAttribute('class');
23462                 }
23463                 continue;
23464             }
23465             
23466             // style cleanup!?
23467             // class cleanup?
23468             
23469         }
23470         
23471         
23472         this.cleanUpChildren(node);
23473         
23474         
23475     },
23476     
23477     /**
23478      * Clean up MS wordisms...
23479      */
23480     cleanWord : function(node)
23481     {
23482         if (!node) {
23483             this.cleanWord(this.doc.body);
23484             return;
23485         }
23486         
23487         if(
23488                 node.nodeName == 'SPAN' &&
23489                 !node.hasAttributes() &&
23490                 node.childNodes.length == 1 &&
23491                 node.firstChild.nodeName == "#text"  
23492         ) {
23493             var textNode = node.firstChild;
23494             node.removeChild(textNode);
23495             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
23496                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
23497             }
23498             node.parentNode.insertBefore(textNode, node);
23499             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
23500                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
23501             }
23502             node.parentNode.removeChild(node);
23503         }
23504         
23505         if (node.nodeName == "#text") {
23506             // clean up silly Windows -- stuff?
23507             return; 
23508         }
23509         if (node.nodeName == "#comment") {
23510             node.parentNode.removeChild(node);
23511             // clean up silly Windows -- stuff?
23512             return; 
23513         }
23514         
23515         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23516             node.parentNode.removeChild(node);
23517             return;
23518         }
23519         //Roo.log(node.tagName);
23520         // remove - but keep children..
23521         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
23522             //Roo.log('-- removed');
23523             while (node.childNodes.length) {
23524                 var cn = node.childNodes[0];
23525                 node.removeChild(cn);
23526                 node.parentNode.insertBefore(cn, node);
23527                 // move node to parent - and clean it..
23528                 this.cleanWord(cn);
23529             }
23530             node.parentNode.removeChild(node);
23531             /// no need to iterate chidlren = it's got none..
23532             //this.iterateChildren(node, this.cleanWord);
23533             return;
23534         }
23535         // clean styles
23536         if (node.className.length) {
23537             
23538             var cn = node.className.split(/\W+/);
23539             var cna = [];
23540             Roo.each(cn, function(cls) {
23541                 if (cls.match(/Mso[a-zA-Z]+/)) {
23542                     return;
23543                 }
23544                 cna.push(cls);
23545             });
23546             node.className = cna.length ? cna.join(' ') : '';
23547             if (!cna.length) {
23548                 node.removeAttribute("class");
23549             }
23550         }
23551         
23552         if (node.hasAttribute("lang")) {
23553             node.removeAttribute("lang");
23554         }
23555         
23556         if (node.hasAttribute("style")) {
23557             
23558             var styles = node.getAttribute("style").split(";");
23559             var nstyle = [];
23560             Roo.each(styles, function(s) {
23561                 if (!s.match(/:/)) {
23562                     return;
23563                 }
23564                 var kv = s.split(":");
23565                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23566                     return;
23567                 }
23568                 // what ever is left... we allow.
23569                 nstyle.push(s);
23570             });
23571             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23572             if (!nstyle.length) {
23573                 node.removeAttribute('style');
23574             }
23575         }
23576         this.iterateChildren(node, this.cleanWord);
23577         
23578         
23579         
23580     },
23581     /**
23582      * iterateChildren of a Node, calling fn each time, using this as the scole..
23583      * @param {DomNode} node node to iterate children of.
23584      * @param {Function} fn method of this class to call on each item.
23585      */
23586     iterateChildren : function(node, fn)
23587     {
23588         if (!node.childNodes.length) {
23589                 return;
23590         }
23591         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23592            fn.call(this, node.childNodes[i])
23593         }
23594     },
23595     
23596     
23597     /**
23598      * cleanTableWidths.
23599      *
23600      * Quite often pasting from word etc.. results in tables with column and widths.
23601      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23602      *
23603      */
23604     cleanTableWidths : function(node)
23605     {
23606          
23607          
23608         if (!node) {
23609             this.cleanTableWidths(this.doc.body);
23610             return;
23611         }
23612         
23613         // ignore list...
23614         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23615             return; 
23616         }
23617         Roo.log(node.tagName);
23618         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23619             this.iterateChildren(node, this.cleanTableWidths);
23620             return;
23621         }
23622         if (node.hasAttribute('width')) {
23623             node.removeAttribute('width');
23624         }
23625         
23626          
23627         if (node.hasAttribute("style")) {
23628             // pretty basic...
23629             
23630             var styles = node.getAttribute("style").split(";");
23631             var nstyle = [];
23632             Roo.each(styles, function(s) {
23633                 if (!s.match(/:/)) {
23634                     return;
23635                 }
23636                 var kv = s.split(":");
23637                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23638                     return;
23639                 }
23640                 // what ever is left... we allow.
23641                 nstyle.push(s);
23642             });
23643             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23644             if (!nstyle.length) {
23645                 node.removeAttribute('style');
23646             }
23647         }
23648         
23649         this.iterateChildren(node, this.cleanTableWidths);
23650         
23651         
23652     },
23653     
23654     
23655     
23656     
23657     domToHTML : function(currentElement, depth, nopadtext) {
23658         
23659         depth = depth || 0;
23660         nopadtext = nopadtext || false;
23661     
23662         if (!currentElement) {
23663             return this.domToHTML(this.doc.body);
23664         }
23665         
23666         //Roo.log(currentElement);
23667         var j;
23668         var allText = false;
23669         var nodeName = currentElement.nodeName;
23670         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23671         
23672         if  (nodeName == '#text') {
23673             
23674             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23675         }
23676         
23677         
23678         var ret = '';
23679         if (nodeName != 'BODY') {
23680              
23681             var i = 0;
23682             // Prints the node tagName, such as <A>, <IMG>, etc
23683             if (tagName) {
23684                 var attr = [];
23685                 for(i = 0; i < currentElement.attributes.length;i++) {
23686                     // quoting?
23687                     var aname = currentElement.attributes.item(i).name;
23688                     if (!currentElement.attributes.item(i).value.length) {
23689                         continue;
23690                     }
23691                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23692                 }
23693                 
23694                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23695             } 
23696             else {
23697                 
23698                 // eack
23699             }
23700         } else {
23701             tagName = false;
23702         }
23703         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23704             return ret;
23705         }
23706         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23707             nopadtext = true;
23708         }
23709         
23710         
23711         // Traverse the tree
23712         i = 0;
23713         var currentElementChild = currentElement.childNodes.item(i);
23714         var allText = true;
23715         var innerHTML  = '';
23716         lastnode = '';
23717         while (currentElementChild) {
23718             // Formatting code (indent the tree so it looks nice on the screen)
23719             var nopad = nopadtext;
23720             if (lastnode == 'SPAN') {
23721                 nopad  = true;
23722             }
23723             // text
23724             if  (currentElementChild.nodeName == '#text') {
23725                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23726                 toadd = nopadtext ? toadd : toadd.trim();
23727                 if (!nopad && toadd.length > 80) {
23728                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23729                 }
23730                 innerHTML  += toadd;
23731                 
23732                 i++;
23733                 currentElementChild = currentElement.childNodes.item(i);
23734                 lastNode = '';
23735                 continue;
23736             }
23737             allText = false;
23738             
23739             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23740                 
23741             // Recursively traverse the tree structure of the child node
23742             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23743             lastnode = currentElementChild.nodeName;
23744             i++;
23745             currentElementChild=currentElement.childNodes.item(i);
23746         }
23747         
23748         ret += innerHTML;
23749         
23750         if (!allText) {
23751                 // The remaining code is mostly for formatting the tree
23752             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23753         }
23754         
23755         
23756         if (tagName) {
23757             ret+= "</"+tagName+">";
23758         }
23759         return ret;
23760         
23761     },
23762         
23763     applyBlacklists : function()
23764     {
23765         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23766         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23767         
23768         this.white = [];
23769         this.black = [];
23770         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23771             if (b.indexOf(tag) > -1) {
23772                 return;
23773             }
23774             this.white.push(tag);
23775             
23776         }, this);
23777         
23778         Roo.each(w, function(tag) {
23779             if (b.indexOf(tag) > -1) {
23780                 return;
23781             }
23782             if (this.white.indexOf(tag) > -1) {
23783                 return;
23784             }
23785             this.white.push(tag);
23786             
23787         }, this);
23788         
23789         
23790         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23791             if (w.indexOf(tag) > -1) {
23792                 return;
23793             }
23794             this.black.push(tag);
23795             
23796         }, this);
23797         
23798         Roo.each(b, function(tag) {
23799             if (w.indexOf(tag) > -1) {
23800                 return;
23801             }
23802             if (this.black.indexOf(tag) > -1) {
23803                 return;
23804             }
23805             this.black.push(tag);
23806             
23807         }, this);
23808         
23809         
23810         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23811         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23812         
23813         this.cwhite = [];
23814         this.cblack = [];
23815         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23816             if (b.indexOf(tag) > -1) {
23817                 return;
23818             }
23819             this.cwhite.push(tag);
23820             
23821         }, this);
23822         
23823         Roo.each(w, function(tag) {
23824             if (b.indexOf(tag) > -1) {
23825                 return;
23826             }
23827             if (this.cwhite.indexOf(tag) > -1) {
23828                 return;
23829             }
23830             this.cwhite.push(tag);
23831             
23832         }, this);
23833         
23834         
23835         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23836             if (w.indexOf(tag) > -1) {
23837                 return;
23838             }
23839             this.cblack.push(tag);
23840             
23841         }, this);
23842         
23843         Roo.each(b, function(tag) {
23844             if (w.indexOf(tag) > -1) {
23845                 return;
23846             }
23847             if (this.cblack.indexOf(tag) > -1) {
23848                 return;
23849             }
23850             this.cblack.push(tag);
23851             
23852         }, this);
23853     },
23854     
23855     setStylesheets : function(stylesheets)
23856     {
23857         if(typeof(stylesheets) == 'string'){
23858             Roo.get(this.iframe.contentDocument.head).createChild({
23859                 tag : 'link',
23860                 rel : 'stylesheet',
23861                 type : 'text/css',
23862                 href : stylesheets
23863             });
23864             
23865             return;
23866         }
23867         var _this = this;
23868      
23869         Roo.each(stylesheets, function(s) {
23870             if(!s.length){
23871                 return;
23872             }
23873             
23874             Roo.get(_this.iframe.contentDocument.head).createChild({
23875                 tag : 'link',
23876                 rel : 'stylesheet',
23877                 type : 'text/css',
23878                 href : s
23879             });
23880         });
23881
23882         
23883     },
23884     
23885     removeStylesheets : function()
23886     {
23887         var _this = this;
23888         
23889         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23890             s.remove();
23891         });
23892     },
23893     
23894     setStyle : function(style)
23895     {
23896         Roo.get(this.iframe.contentDocument.head).createChild({
23897             tag : 'style',
23898             type : 'text/css',
23899             html : style
23900         });
23901
23902         return;
23903     }
23904     
23905     // hide stuff that is not compatible
23906     /**
23907      * @event blur
23908      * @hide
23909      */
23910     /**
23911      * @event change
23912      * @hide
23913      */
23914     /**
23915      * @event focus
23916      * @hide
23917      */
23918     /**
23919      * @event specialkey
23920      * @hide
23921      */
23922     /**
23923      * @cfg {String} fieldClass @hide
23924      */
23925     /**
23926      * @cfg {String} focusClass @hide
23927      */
23928     /**
23929      * @cfg {String} autoCreate @hide
23930      */
23931     /**
23932      * @cfg {String} inputType @hide
23933      */
23934     /**
23935      * @cfg {String} invalidClass @hide
23936      */
23937     /**
23938      * @cfg {String} invalidText @hide
23939      */
23940     /**
23941      * @cfg {String} msgFx @hide
23942      */
23943     /**
23944      * @cfg {String} validateOnBlur @hide
23945      */
23946 });
23947
23948 Roo.HtmlEditorCore.white = [
23949         'area', 'br', 'img', 'input', 'hr', 'wbr',
23950         
23951        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23952        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23953        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23954        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23955        'table',   'ul',         'xmp', 
23956        
23957        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23958       'thead',   'tr', 
23959      
23960       'dir', 'menu', 'ol', 'ul', 'dl',
23961        
23962       'embed',  'object'
23963 ];
23964
23965
23966 Roo.HtmlEditorCore.black = [
23967     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23968         'applet', // 
23969         'base',   'basefont', 'bgsound', 'blink',  'body', 
23970         'frame',  'frameset', 'head',    'html',   'ilayer', 
23971         'iframe', 'layer',  'link',     'meta',    'object',   
23972         'script', 'style' ,'title',  'xml' // clean later..
23973 ];
23974 Roo.HtmlEditorCore.clean = [
23975     'script', 'style', 'title', 'xml'
23976 ];
23977 Roo.HtmlEditorCore.remove = [
23978     'font'
23979 ];
23980 // attributes..
23981
23982 Roo.HtmlEditorCore.ablack = [
23983     'on'
23984 ];
23985     
23986 Roo.HtmlEditorCore.aclean = [ 
23987     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23988 ];
23989
23990 // protocols..
23991 Roo.HtmlEditorCore.pwhite= [
23992         'http',  'https',  'mailto'
23993 ];
23994
23995 // white listed style attributes.
23996 Roo.HtmlEditorCore.cwhite= [
23997       //  'text-align', /// default is to allow most things..
23998       
23999          
24000 //        'font-size'//??
24001 ];
24002
24003 // black listed style attributes.
24004 Roo.HtmlEditorCore.cblack= [
24005       //  'font-size' -- this can be set by the project 
24006 ];
24007
24008
24009 Roo.HtmlEditorCore.swapCodes   =[ 
24010     [    8211, "--" ], 
24011     [    8212, "--" ], 
24012     [    8216,  "'" ],  
24013     [    8217, "'" ],  
24014     [    8220, '"' ],  
24015     [    8221, '"' ],  
24016     [    8226, "*" ],  
24017     [    8230, "..." ]
24018 ]; 
24019
24020     /*
24021  * - LGPL
24022  *
24023  * HtmlEditor
24024  * 
24025  */
24026
24027 /**
24028  * @class Roo.bootstrap.HtmlEditor
24029  * @extends Roo.bootstrap.TextArea
24030  * Bootstrap HtmlEditor class
24031
24032  * @constructor
24033  * Create a new HtmlEditor
24034  * @param {Object} config The config object
24035  */
24036
24037 Roo.bootstrap.HtmlEditor = function(config){
24038     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
24039     if (!this.toolbars) {
24040         this.toolbars = [];
24041     }
24042     
24043     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
24044     this.addEvents({
24045             /**
24046              * @event initialize
24047              * Fires when the editor is fully initialized (including the iframe)
24048              * @param {HtmlEditor} this
24049              */
24050             initialize: true,
24051             /**
24052              * @event activate
24053              * Fires when the editor is first receives the focus. Any insertion must wait
24054              * until after this event.
24055              * @param {HtmlEditor} this
24056              */
24057             activate: true,
24058              /**
24059              * @event beforesync
24060              * Fires before the textarea is updated with content from the editor iframe. Return false
24061              * to cancel the sync.
24062              * @param {HtmlEditor} this
24063              * @param {String} html
24064              */
24065             beforesync: true,
24066              /**
24067              * @event beforepush
24068              * Fires before the iframe editor is updated with content from the textarea. Return false
24069              * to cancel the push.
24070              * @param {HtmlEditor} this
24071              * @param {String} html
24072              */
24073             beforepush: true,
24074              /**
24075              * @event sync
24076              * Fires when the textarea is updated with content from the editor iframe.
24077              * @param {HtmlEditor} this
24078              * @param {String} html
24079              */
24080             sync: true,
24081              /**
24082              * @event push
24083              * Fires when the iframe editor is updated with content from the textarea.
24084              * @param {HtmlEditor} this
24085              * @param {String} html
24086              */
24087             push: true,
24088              /**
24089              * @event editmodechange
24090              * Fires when the editor switches edit modes
24091              * @param {HtmlEditor} this
24092              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24093              */
24094             editmodechange: true,
24095             /**
24096              * @event editorevent
24097              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24098              * @param {HtmlEditor} this
24099              */
24100             editorevent: true,
24101             /**
24102              * @event firstfocus
24103              * Fires when on first focus - needed by toolbars..
24104              * @param {HtmlEditor} this
24105              */
24106             firstfocus: true,
24107             /**
24108              * @event autosave
24109              * Auto save the htmlEditor value as a file into Events
24110              * @param {HtmlEditor} this
24111              */
24112             autosave: true,
24113             /**
24114              * @event savedpreview
24115              * preview the saved version of htmlEditor
24116              * @param {HtmlEditor} this
24117              */
24118             savedpreview: true
24119         });
24120 };
24121
24122
24123 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
24124     
24125     
24126       /**
24127      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
24128      */
24129     toolbars : false,
24130     
24131      /**
24132     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
24133     */
24134     btns : [],
24135    
24136      /**
24137      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24138      *                        Roo.resizable.
24139      */
24140     resizable : false,
24141      /**
24142      * @cfg {Number} height (in pixels)
24143      */   
24144     height: 300,
24145    /**
24146      * @cfg {Number} width (in pixels)
24147      */   
24148     width: false,
24149     
24150     /**
24151      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24152      * 
24153      */
24154     stylesheets: false,
24155     
24156     // id of frame..
24157     frameId: false,
24158     
24159     // private properties
24160     validationEvent : false,
24161     deferHeight: true,
24162     initialized : false,
24163     activated : false,
24164     
24165     onFocus : Roo.emptyFn,
24166     iframePad:3,
24167     hideMode:'offsets',
24168     
24169     tbContainer : false,
24170     
24171     bodyCls : '',
24172     
24173     toolbarContainer :function() {
24174         return this.wrap.select('.x-html-editor-tb',true).first();
24175     },
24176
24177     /**
24178      * Protected method that will not generally be called directly. It
24179      * is called when the editor creates its toolbar. Override this method if you need to
24180      * add custom toolbar buttons.
24181      * @param {HtmlEditor} editor
24182      */
24183     createToolbar : function(){
24184         Roo.log('renewing');
24185         Roo.log("create toolbars");
24186         
24187         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
24188         this.toolbars[0].render(this.toolbarContainer());
24189         
24190         return;
24191         
24192 //        if (!editor.toolbars || !editor.toolbars.length) {
24193 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
24194 //        }
24195 //        
24196 //        for (var i =0 ; i < editor.toolbars.length;i++) {
24197 //            editor.toolbars[i] = Roo.factory(
24198 //                    typeof(editor.toolbars[i]) == 'string' ?
24199 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
24200 //                Roo.bootstrap.HtmlEditor);
24201 //            editor.toolbars[i].init(editor);
24202 //        }
24203     },
24204
24205      
24206     // private
24207     onRender : function(ct, position)
24208     {
24209        // Roo.log("Call onRender: " + this.xtype);
24210         var _t = this;
24211         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
24212       
24213         this.wrap = this.inputEl().wrap({
24214             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24215         });
24216         
24217         this.editorcore.onRender(ct, position);
24218          
24219         if (this.resizable) {
24220             this.resizeEl = new Roo.Resizable(this.wrap, {
24221                 pinned : true,
24222                 wrap: true,
24223                 dynamic : true,
24224                 minHeight : this.height,
24225                 height: this.height,
24226                 handles : this.resizable,
24227                 width: this.width,
24228                 listeners : {
24229                     resize : function(r, w, h) {
24230                         _t.onResize(w,h); // -something
24231                     }
24232                 }
24233             });
24234             
24235         }
24236         this.createToolbar(this);
24237        
24238         
24239         if(!this.width && this.resizable){
24240             this.setSize(this.wrap.getSize());
24241         }
24242         if (this.resizeEl) {
24243             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24244             // should trigger onReize..
24245         }
24246         
24247     },
24248
24249     // private
24250     onResize : function(w, h)
24251     {
24252         Roo.log('resize: ' +w + ',' + h );
24253         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
24254         var ew = false;
24255         var eh = false;
24256         
24257         if(this.inputEl() ){
24258             if(typeof w == 'number'){
24259                 var aw = w - this.wrap.getFrameWidth('lr');
24260                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
24261                 ew = aw;
24262             }
24263             if(typeof h == 'number'){
24264                  var tbh = -11;  // fixme it needs to tool bar size!
24265                 for (var i =0; i < this.toolbars.length;i++) {
24266                     // fixme - ask toolbars for heights?
24267                     tbh += this.toolbars[i].el.getHeight();
24268                     //if (this.toolbars[i].footer) {
24269                     //    tbh += this.toolbars[i].footer.el.getHeight();
24270                     //}
24271                 }
24272               
24273                 
24274                 
24275                 
24276                 
24277                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24278                 ah -= 5; // knock a few pixes off for look..
24279                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
24280                 var eh = ah;
24281             }
24282         }
24283         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
24284         this.editorcore.onResize(ew,eh);
24285         
24286     },
24287
24288     /**
24289      * Toggles the editor between standard and source edit mode.
24290      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24291      */
24292     toggleSourceEdit : function(sourceEditMode)
24293     {
24294         this.editorcore.toggleSourceEdit(sourceEditMode);
24295         
24296         if(this.editorcore.sourceEditMode){
24297             Roo.log('editor - showing textarea');
24298             
24299 //            Roo.log('in');
24300 //            Roo.log(this.syncValue());
24301             this.syncValue();
24302             this.inputEl().removeClass(['hide', 'x-hidden']);
24303             this.inputEl().dom.removeAttribute('tabIndex');
24304             this.inputEl().focus();
24305         }else{
24306             Roo.log('editor - hiding textarea');
24307 //            Roo.log('out')
24308 //            Roo.log(this.pushValue()); 
24309             this.pushValue();
24310             
24311             this.inputEl().addClass(['hide', 'x-hidden']);
24312             this.inputEl().dom.setAttribute('tabIndex', -1);
24313             //this.deferFocus();
24314         }
24315          
24316         if(this.resizable){
24317             this.setSize(this.wrap.getSize());
24318         }
24319         
24320         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
24321     },
24322  
24323     // private (for BoxComponent)
24324     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24325
24326     // private (for BoxComponent)
24327     getResizeEl : function(){
24328         return this.wrap;
24329     },
24330
24331     // private (for BoxComponent)
24332     getPositionEl : function(){
24333         return this.wrap;
24334     },
24335
24336     // private
24337     initEvents : function(){
24338         this.originalValue = this.getValue();
24339     },
24340
24341 //    /**
24342 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24343 //     * @method
24344 //     */
24345 //    markInvalid : Roo.emptyFn,
24346 //    /**
24347 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24348 //     * @method
24349 //     */
24350 //    clearInvalid : Roo.emptyFn,
24351
24352     setValue : function(v){
24353         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
24354         this.editorcore.pushValue();
24355     },
24356
24357      
24358     // private
24359     deferFocus : function(){
24360         this.focus.defer(10, this);
24361     },
24362
24363     // doc'ed in Field
24364     focus : function(){
24365         this.editorcore.focus();
24366         
24367     },
24368       
24369
24370     // private
24371     onDestroy : function(){
24372         
24373         
24374         
24375         if(this.rendered){
24376             
24377             for (var i =0; i < this.toolbars.length;i++) {
24378                 // fixme - ask toolbars for heights?
24379                 this.toolbars[i].onDestroy();
24380             }
24381             
24382             this.wrap.dom.innerHTML = '';
24383             this.wrap.remove();
24384         }
24385     },
24386
24387     // private
24388     onFirstFocus : function(){
24389         //Roo.log("onFirstFocus");
24390         this.editorcore.onFirstFocus();
24391          for (var i =0; i < this.toolbars.length;i++) {
24392             this.toolbars[i].onFirstFocus();
24393         }
24394         
24395     },
24396     
24397     // private
24398     syncValue : function()
24399     {   
24400         this.editorcore.syncValue();
24401     },
24402     
24403     pushValue : function()
24404     {   
24405         this.editorcore.pushValue();
24406     }
24407      
24408     
24409     // hide stuff that is not compatible
24410     /**
24411      * @event blur
24412      * @hide
24413      */
24414     /**
24415      * @event change
24416      * @hide
24417      */
24418     /**
24419      * @event focus
24420      * @hide
24421      */
24422     /**
24423      * @event specialkey
24424      * @hide
24425      */
24426     /**
24427      * @cfg {String} fieldClass @hide
24428      */
24429     /**
24430      * @cfg {String} focusClass @hide
24431      */
24432     /**
24433      * @cfg {String} autoCreate @hide
24434      */
24435     /**
24436      * @cfg {String} inputType @hide
24437      */
24438      
24439     /**
24440      * @cfg {String} invalidText @hide
24441      */
24442     /**
24443      * @cfg {String} msgFx @hide
24444      */
24445     /**
24446      * @cfg {String} validateOnBlur @hide
24447      */
24448 });
24449  
24450     
24451    
24452    
24453    
24454       
24455 Roo.namespace('Roo.bootstrap.htmleditor');
24456 /**
24457  * @class Roo.bootstrap.HtmlEditorToolbar1
24458  * Basic Toolbar
24459  * 
24460  * @example
24461  * Usage:
24462  *
24463  new Roo.bootstrap.HtmlEditor({
24464     ....
24465     toolbars : [
24466         new Roo.bootstrap.HtmlEditorToolbar1({
24467             disable : { fonts: 1 , format: 1, ..., ... , ...],
24468             btns : [ .... ]
24469         })
24470     }
24471      
24472  * 
24473  * @cfg {Object} disable List of elements to disable..
24474  * @cfg {Array} btns List of additional buttons.
24475  * 
24476  * 
24477  * NEEDS Extra CSS? 
24478  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24479  */
24480  
24481 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24482 {
24483     
24484     Roo.apply(this, config);
24485     
24486     // default disabled, based on 'good practice'..
24487     this.disable = this.disable || {};
24488     Roo.applyIf(this.disable, {
24489         fontSize : true,
24490         colors : true,
24491         specialElements : true
24492     });
24493     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24494     
24495     this.editor = config.editor;
24496     this.editorcore = config.editor.editorcore;
24497     
24498     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24499     
24500     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24501     // dont call parent... till later.
24502 }
24503 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
24504      
24505     bar : true,
24506     
24507     editor : false,
24508     editorcore : false,
24509     
24510     
24511     formats : [
24512         "p" ,  
24513         "h1","h2","h3","h4","h5","h6", 
24514         "pre", "code", 
24515         "abbr", "acronym", "address", "cite", "samp", "var",
24516         'div','span'
24517     ],
24518     
24519     onRender : function(ct, position)
24520     {
24521        // Roo.log("Call onRender: " + this.xtype);
24522         
24523        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24524        Roo.log(this.el);
24525        this.el.dom.style.marginBottom = '0';
24526        var _this = this;
24527        var editorcore = this.editorcore;
24528        var editor= this.editor;
24529        
24530        var children = [];
24531        var btn = function(id,cmd , toggle, handler, html){
24532        
24533             var  event = toggle ? 'toggle' : 'click';
24534        
24535             var a = {
24536                 size : 'sm',
24537                 xtype: 'Button',
24538                 xns: Roo.bootstrap,
24539                 //glyphicon : id,
24540                 fa: id,
24541                 cmd : id || cmd,
24542                 enableToggle:toggle !== false,
24543                 html : html || '',
24544                 pressed : toggle ? false : null,
24545                 listeners : {}
24546             };
24547             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24548                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24549             };
24550             children.push(a);
24551             return a;
24552        }
24553        
24554     //    var cb_box = function...
24555         
24556         var style = {
24557                 xtype: 'Button',
24558                 size : 'sm',
24559                 xns: Roo.bootstrap,
24560                 fa : 'font',
24561                 //html : 'submit'
24562                 menu : {
24563                     xtype: 'Menu',
24564                     xns: Roo.bootstrap,
24565                     items:  []
24566                 }
24567         };
24568         Roo.each(this.formats, function(f) {
24569             style.menu.items.push({
24570                 xtype :'MenuItem',
24571                 xns: Roo.bootstrap,
24572                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24573                 tagname : f,
24574                 listeners : {
24575                     click : function()
24576                     {
24577                         editorcore.insertTag(this.tagname);
24578                         editor.focus();
24579                     }
24580                 }
24581                 
24582             });
24583         });
24584         children.push(style);   
24585         
24586         btn('bold',false,true);
24587         btn('italic',false,true);
24588         btn('align-left', 'justifyleft',true);
24589         btn('align-center', 'justifycenter',true);
24590         btn('align-right' , 'justifyright',true);
24591         btn('link', false, false, function(btn) {
24592             //Roo.log("create link?");
24593             var url = prompt(this.createLinkText, this.defaultLinkValue);
24594             if(url && url != 'http:/'+'/'){
24595                 this.editorcore.relayCmd('createlink', url);
24596             }
24597         }),
24598         btn('list','insertunorderedlist',true);
24599         btn('pencil', false,true, function(btn){
24600                 Roo.log(this);
24601                 this.toggleSourceEdit(btn.pressed);
24602         });
24603         
24604         if (this.editor.btns.length > 0) {
24605             for (var i = 0; i<this.editor.btns.length; i++) {
24606                 children.push(this.editor.btns[i]);
24607             }
24608         }
24609         
24610         /*
24611         var cog = {
24612                 xtype: 'Button',
24613                 size : 'sm',
24614                 xns: Roo.bootstrap,
24615                 glyphicon : 'cog',
24616                 //html : 'submit'
24617                 menu : {
24618                     xtype: 'Menu',
24619                     xns: Roo.bootstrap,
24620                     items:  []
24621                 }
24622         };
24623         
24624         cog.menu.items.push({
24625             xtype :'MenuItem',
24626             xns: Roo.bootstrap,
24627             html : Clean styles,
24628             tagname : f,
24629             listeners : {
24630                 click : function()
24631                 {
24632                     editorcore.insertTag(this.tagname);
24633                     editor.focus();
24634                 }
24635             }
24636             
24637         });
24638        */
24639         
24640          
24641        this.xtype = 'NavSimplebar';
24642         
24643         for(var i=0;i< children.length;i++) {
24644             
24645             this.buttons.add(this.addxtypeChild(children[i]));
24646             
24647         }
24648         
24649         editor.on('editorevent', this.updateToolbar, this);
24650     },
24651     onBtnClick : function(id)
24652     {
24653        this.editorcore.relayCmd(id);
24654        this.editorcore.focus();
24655     },
24656     
24657     /**
24658      * Protected method that will not generally be called directly. It triggers
24659      * a toolbar update by reading the markup state of the current selection in the editor.
24660      */
24661     updateToolbar: function(){
24662
24663         if(!this.editorcore.activated){
24664             this.editor.onFirstFocus(); // is this neeed?
24665             return;
24666         }
24667
24668         var btns = this.buttons; 
24669         var doc = this.editorcore.doc;
24670         btns.get('bold').setActive(doc.queryCommandState('bold'));
24671         btns.get('italic').setActive(doc.queryCommandState('italic'));
24672         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24673         
24674         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24675         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24676         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24677         
24678         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24679         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24680          /*
24681         
24682         var ans = this.editorcore.getAllAncestors();
24683         if (this.formatCombo) {
24684             
24685             
24686             var store = this.formatCombo.store;
24687             this.formatCombo.setValue("");
24688             for (var i =0; i < ans.length;i++) {
24689                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24690                     // select it..
24691                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24692                     break;
24693                 }
24694             }
24695         }
24696         
24697         
24698         
24699         // hides menus... - so this cant be on a menu...
24700         Roo.bootstrap.MenuMgr.hideAll();
24701         */
24702         Roo.bootstrap.MenuMgr.hideAll();
24703         //this.editorsyncValue();
24704     },
24705     onFirstFocus: function() {
24706         this.buttons.each(function(item){
24707            item.enable();
24708         });
24709     },
24710     toggleSourceEdit : function(sourceEditMode){
24711         
24712           
24713         if(sourceEditMode){
24714             Roo.log("disabling buttons");
24715            this.buttons.each( function(item){
24716                 if(item.cmd != 'pencil'){
24717                     item.disable();
24718                 }
24719             });
24720           
24721         }else{
24722             Roo.log("enabling buttons");
24723             if(this.editorcore.initialized){
24724                 this.buttons.each( function(item){
24725                     item.enable();
24726                 });
24727             }
24728             
24729         }
24730         Roo.log("calling toggole on editor");
24731         // tell the editor that it's been pressed..
24732         this.editor.toggleSourceEdit(sourceEditMode);
24733        
24734     }
24735 });
24736
24737
24738
24739
24740
24741 /**
24742  * @class Roo.bootstrap.Table.AbstractSelectionModel
24743  * @extends Roo.util.Observable
24744  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24745  * implemented by descendant classes.  This class should not be directly instantiated.
24746  * @constructor
24747  */
24748 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24749     this.locked = false;
24750     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24751 };
24752
24753
24754 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24755     /** @ignore Called by the grid automatically. Do not call directly. */
24756     init : function(grid){
24757         this.grid = grid;
24758         this.initEvents();
24759     },
24760
24761     /**
24762      * Locks the selections.
24763      */
24764     lock : function(){
24765         this.locked = true;
24766     },
24767
24768     /**
24769      * Unlocks the selections.
24770      */
24771     unlock : function(){
24772         this.locked = false;
24773     },
24774
24775     /**
24776      * Returns true if the selections are locked.
24777      * @return {Boolean}
24778      */
24779     isLocked : function(){
24780         return this.locked;
24781     }
24782 });
24783 /**
24784  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24785  * @class Roo.bootstrap.Table.RowSelectionModel
24786  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24787  * It supports multiple selections and keyboard selection/navigation. 
24788  * @constructor
24789  * @param {Object} config
24790  */
24791
24792 Roo.bootstrap.Table.RowSelectionModel = function(config){
24793     Roo.apply(this, config);
24794     this.selections = new Roo.util.MixedCollection(false, function(o){
24795         return o.id;
24796     });
24797
24798     this.last = false;
24799     this.lastActive = false;
24800
24801     this.addEvents({
24802         /**
24803              * @event selectionchange
24804              * Fires when the selection changes
24805              * @param {SelectionModel} this
24806              */
24807             "selectionchange" : true,
24808         /**
24809              * @event afterselectionchange
24810              * Fires after the selection changes (eg. by key press or clicking)
24811              * @param {SelectionModel} this
24812              */
24813             "afterselectionchange" : true,
24814         /**
24815              * @event beforerowselect
24816              * Fires when a row is selected being selected, return false to cancel.
24817              * @param {SelectionModel} this
24818              * @param {Number} rowIndex The selected index
24819              * @param {Boolean} keepExisting False if other selections will be cleared
24820              */
24821             "beforerowselect" : true,
24822         /**
24823              * @event rowselect
24824              * Fires when a row is selected.
24825              * @param {SelectionModel} this
24826              * @param {Number} rowIndex The selected index
24827              * @param {Roo.data.Record} r The record
24828              */
24829             "rowselect" : true,
24830         /**
24831              * @event rowdeselect
24832              * Fires when a row is deselected.
24833              * @param {SelectionModel} this
24834              * @param {Number} rowIndex The selected index
24835              */
24836         "rowdeselect" : true
24837     });
24838     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24839     this.locked = false;
24840  };
24841
24842 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24843     /**
24844      * @cfg {Boolean} singleSelect
24845      * True to allow selection of only one row at a time (defaults to false)
24846      */
24847     singleSelect : false,
24848
24849     // private
24850     initEvents : function()
24851     {
24852
24853         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24854         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24855         //}else{ // allow click to work like normal
24856          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24857         //}
24858         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24859         this.grid.on("rowclick", this.handleMouseDown, this);
24860         
24861         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24862             "up" : function(e){
24863                 if(!e.shiftKey){
24864                     this.selectPrevious(e.shiftKey);
24865                 }else if(this.last !== false && this.lastActive !== false){
24866                     var last = this.last;
24867                     this.selectRange(this.last,  this.lastActive-1);
24868                     this.grid.getView().focusRow(this.lastActive);
24869                     if(last !== false){
24870                         this.last = last;
24871                     }
24872                 }else{
24873                     this.selectFirstRow();
24874                 }
24875                 this.fireEvent("afterselectionchange", this);
24876             },
24877             "down" : function(e){
24878                 if(!e.shiftKey){
24879                     this.selectNext(e.shiftKey);
24880                 }else if(this.last !== false && this.lastActive !== false){
24881                     var last = this.last;
24882                     this.selectRange(this.last,  this.lastActive+1);
24883                     this.grid.getView().focusRow(this.lastActive);
24884                     if(last !== false){
24885                         this.last = last;
24886                     }
24887                 }else{
24888                     this.selectFirstRow();
24889                 }
24890                 this.fireEvent("afterselectionchange", this);
24891             },
24892             scope: this
24893         });
24894         this.grid.store.on('load', function(){
24895             this.selections.clear();
24896         },this);
24897         /*
24898         var view = this.grid.view;
24899         view.on("refresh", this.onRefresh, this);
24900         view.on("rowupdated", this.onRowUpdated, this);
24901         view.on("rowremoved", this.onRemove, this);
24902         */
24903     },
24904
24905     // private
24906     onRefresh : function()
24907     {
24908         var ds = this.grid.store, i, v = this.grid.view;
24909         var s = this.selections;
24910         s.each(function(r){
24911             if((i = ds.indexOfId(r.id)) != -1){
24912                 v.onRowSelect(i);
24913             }else{
24914                 s.remove(r);
24915             }
24916         });
24917     },
24918
24919     // private
24920     onRemove : function(v, index, r){
24921         this.selections.remove(r);
24922     },
24923
24924     // private
24925     onRowUpdated : function(v, index, r){
24926         if(this.isSelected(r)){
24927             v.onRowSelect(index);
24928         }
24929     },
24930
24931     /**
24932      * Select records.
24933      * @param {Array} records The records to select
24934      * @param {Boolean} keepExisting (optional) True to keep existing selections
24935      */
24936     selectRecords : function(records, keepExisting)
24937     {
24938         if(!keepExisting){
24939             this.clearSelections();
24940         }
24941             var ds = this.grid.store;
24942         for(var i = 0, len = records.length; i < len; i++){
24943             this.selectRow(ds.indexOf(records[i]), true);
24944         }
24945     },
24946
24947     /**
24948      * Gets the number of selected rows.
24949      * @return {Number}
24950      */
24951     getCount : function(){
24952         return this.selections.length;
24953     },
24954
24955     /**
24956      * Selects the first row in the grid.
24957      */
24958     selectFirstRow : function(){
24959         this.selectRow(0);
24960     },
24961
24962     /**
24963      * Select the last row.
24964      * @param {Boolean} keepExisting (optional) True to keep existing selections
24965      */
24966     selectLastRow : function(keepExisting){
24967         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24968         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24969     },
24970
24971     /**
24972      * Selects the row immediately following the last selected row.
24973      * @param {Boolean} keepExisting (optional) True to keep existing selections
24974      */
24975     selectNext : function(keepExisting)
24976     {
24977             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24978             this.selectRow(this.last+1, keepExisting);
24979             this.grid.getView().focusRow(this.last);
24980         }
24981     },
24982
24983     /**
24984      * Selects the row that precedes the last selected row.
24985      * @param {Boolean} keepExisting (optional) True to keep existing selections
24986      */
24987     selectPrevious : function(keepExisting){
24988         if(this.last){
24989             this.selectRow(this.last-1, keepExisting);
24990             this.grid.getView().focusRow(this.last);
24991         }
24992     },
24993
24994     /**
24995      * Returns the selected records
24996      * @return {Array} Array of selected records
24997      */
24998     getSelections : function(){
24999         return [].concat(this.selections.items);
25000     },
25001
25002     /**
25003      * Returns the first selected record.
25004      * @return {Record}
25005      */
25006     getSelected : function(){
25007         return this.selections.itemAt(0);
25008     },
25009
25010
25011     /**
25012      * Clears all selections.
25013      */
25014     clearSelections : function(fast)
25015     {
25016         if(this.locked) {
25017             return;
25018         }
25019         if(fast !== true){
25020                 var ds = this.grid.store;
25021             var s = this.selections;
25022             s.each(function(r){
25023                 this.deselectRow(ds.indexOfId(r.id));
25024             }, this);
25025             s.clear();
25026         }else{
25027             this.selections.clear();
25028         }
25029         this.last = false;
25030     },
25031
25032
25033     /**
25034      * Selects all rows.
25035      */
25036     selectAll : function(){
25037         if(this.locked) {
25038             return;
25039         }
25040         this.selections.clear();
25041         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
25042             this.selectRow(i, true);
25043         }
25044     },
25045
25046     /**
25047      * Returns True if there is a selection.
25048      * @return {Boolean}
25049      */
25050     hasSelection : function(){
25051         return this.selections.length > 0;
25052     },
25053
25054     /**
25055      * Returns True if the specified row is selected.
25056      * @param {Number/Record} record The record or index of the record to check
25057      * @return {Boolean}
25058      */
25059     isSelected : function(index){
25060             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
25061         return (r && this.selections.key(r.id) ? true : false);
25062     },
25063
25064     /**
25065      * Returns True if the specified record id is selected.
25066      * @param {String} id The id of record to check
25067      * @return {Boolean}
25068      */
25069     isIdSelected : function(id){
25070         return (this.selections.key(id) ? true : false);
25071     },
25072
25073
25074     // private
25075     handleMouseDBClick : function(e, t){
25076         
25077     },
25078     // private
25079     handleMouseDown : function(e, t)
25080     {
25081             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
25082         if(this.isLocked() || rowIndex < 0 ){
25083             return;
25084         };
25085         if(e.shiftKey && this.last !== false){
25086             var last = this.last;
25087             this.selectRange(last, rowIndex, e.ctrlKey);
25088             this.last = last; // reset the last
25089             t.focus();
25090     
25091         }else{
25092             var isSelected = this.isSelected(rowIndex);
25093             //Roo.log("select row:" + rowIndex);
25094             if(isSelected){
25095                 this.deselectRow(rowIndex);
25096             } else {
25097                         this.selectRow(rowIndex, true);
25098             }
25099     
25100             /*
25101                 if(e.button !== 0 && isSelected){
25102                 alert('rowIndex 2: ' + rowIndex);
25103                     view.focusRow(rowIndex);
25104                 }else if(e.ctrlKey && isSelected){
25105                     this.deselectRow(rowIndex);
25106                 }else if(!isSelected){
25107                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
25108                     view.focusRow(rowIndex);
25109                 }
25110             */
25111         }
25112         this.fireEvent("afterselectionchange", this);
25113     },
25114     // private
25115     handleDragableRowClick :  function(grid, rowIndex, e) 
25116     {
25117         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
25118             this.selectRow(rowIndex, false);
25119             grid.view.focusRow(rowIndex);
25120              this.fireEvent("afterselectionchange", this);
25121         }
25122     },
25123     
25124     /**
25125      * Selects multiple rows.
25126      * @param {Array} rows Array of the indexes of the row to select
25127      * @param {Boolean} keepExisting (optional) True to keep existing selections
25128      */
25129     selectRows : function(rows, keepExisting){
25130         if(!keepExisting){
25131             this.clearSelections();
25132         }
25133         for(var i = 0, len = rows.length; i < len; i++){
25134             this.selectRow(rows[i], true);
25135         }
25136     },
25137
25138     /**
25139      * Selects a range of rows. All rows in between startRow and endRow are also selected.
25140      * @param {Number} startRow The index of the first row in the range
25141      * @param {Number} endRow The index of the last row in the range
25142      * @param {Boolean} keepExisting (optional) True to retain existing selections
25143      */
25144     selectRange : function(startRow, endRow, keepExisting){
25145         if(this.locked) {
25146             return;
25147         }
25148         if(!keepExisting){
25149             this.clearSelections();
25150         }
25151         if(startRow <= endRow){
25152             for(var i = startRow; i <= endRow; i++){
25153                 this.selectRow(i, true);
25154             }
25155         }else{
25156             for(var i = startRow; i >= endRow; i--){
25157                 this.selectRow(i, true);
25158             }
25159         }
25160     },
25161
25162     /**
25163      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
25164      * @param {Number} startRow The index of the first row in the range
25165      * @param {Number} endRow The index of the last row in the range
25166      */
25167     deselectRange : function(startRow, endRow, preventViewNotify){
25168         if(this.locked) {
25169             return;
25170         }
25171         for(var i = startRow; i <= endRow; i++){
25172             this.deselectRow(i, preventViewNotify);
25173         }
25174     },
25175
25176     /**
25177      * Selects a row.
25178      * @param {Number} row The index of the row to select
25179      * @param {Boolean} keepExisting (optional) True to keep existing selections
25180      */
25181     selectRow : function(index, keepExisting, preventViewNotify)
25182     {
25183             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
25184             return;
25185         }
25186         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
25187             if(!keepExisting || this.singleSelect){
25188                 this.clearSelections();
25189             }
25190             
25191             var r = this.grid.store.getAt(index);
25192             //console.log('selectRow - record id :' + r.id);
25193             
25194             this.selections.add(r);
25195             this.last = this.lastActive = index;
25196             if(!preventViewNotify){
25197                 var proxy = new Roo.Element(
25198                                 this.grid.getRowDom(index)
25199                 );
25200                 proxy.addClass('bg-info info');
25201             }
25202             this.fireEvent("rowselect", this, index, r);
25203             this.fireEvent("selectionchange", this);
25204         }
25205     },
25206
25207     /**
25208      * Deselects a row.
25209      * @param {Number} row The index of the row to deselect
25210      */
25211     deselectRow : function(index, preventViewNotify)
25212     {
25213         if(this.locked) {
25214             return;
25215         }
25216         if(this.last == index){
25217             this.last = false;
25218         }
25219         if(this.lastActive == index){
25220             this.lastActive = false;
25221         }
25222         
25223         var r = this.grid.store.getAt(index);
25224         if (!r) {
25225             return;
25226         }
25227         
25228         this.selections.remove(r);
25229         //.console.log('deselectRow - record id :' + r.id);
25230         if(!preventViewNotify){
25231         
25232             var proxy = new Roo.Element(
25233                 this.grid.getRowDom(index)
25234             );
25235             proxy.removeClass('bg-info info');
25236         }
25237         this.fireEvent("rowdeselect", this, index);
25238         this.fireEvent("selectionchange", this);
25239     },
25240
25241     // private
25242     restoreLast : function(){
25243         if(this._last){
25244             this.last = this._last;
25245         }
25246     },
25247
25248     // private
25249     acceptsNav : function(row, col, cm){
25250         return !cm.isHidden(col) && cm.isCellEditable(col, row);
25251     },
25252
25253     // private
25254     onEditorKey : function(field, e){
25255         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
25256         if(k == e.TAB){
25257             e.stopEvent();
25258             ed.completeEdit();
25259             if(e.shiftKey){
25260                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
25261             }else{
25262                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
25263             }
25264         }else if(k == e.ENTER && !e.ctrlKey){
25265             e.stopEvent();
25266             ed.completeEdit();
25267             if(e.shiftKey){
25268                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
25269             }else{
25270                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
25271             }
25272         }else if(k == e.ESC){
25273             ed.cancelEdit();
25274         }
25275         if(newCell){
25276             g.startEditing(newCell[0], newCell[1]);
25277         }
25278     }
25279 });
25280 /*
25281  * Based on:
25282  * Ext JS Library 1.1.1
25283  * Copyright(c) 2006-2007, Ext JS, LLC.
25284  *
25285  * Originally Released Under LGPL - original licence link has changed is not relivant.
25286  *
25287  * Fork - LGPL
25288  * <script type="text/javascript">
25289  */
25290  
25291 /**
25292  * @class Roo.bootstrap.PagingToolbar
25293  * @extends Roo.bootstrap.NavSimplebar
25294  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
25295  * @constructor
25296  * Create a new PagingToolbar
25297  * @param {Object} config The config object
25298  * @param {Roo.data.Store} store
25299  */
25300 Roo.bootstrap.PagingToolbar = function(config)
25301 {
25302     // old args format still supported... - xtype is prefered..
25303         // created from xtype...
25304     
25305     this.ds = config.dataSource;
25306     
25307     if (config.store && !this.ds) {
25308         this.store= Roo.factory(config.store, Roo.data);
25309         this.ds = this.store;
25310         this.ds.xmodule = this.xmodule || false;
25311     }
25312     
25313     this.toolbarItems = [];
25314     if (config.items) {
25315         this.toolbarItems = config.items;
25316     }
25317     
25318     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
25319     
25320     this.cursor = 0;
25321     
25322     if (this.ds) { 
25323         this.bind(this.ds);
25324     }
25325     
25326     if (Roo.bootstrap.version == 4) {
25327         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
25328     } else {
25329         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
25330     }
25331     
25332 };
25333
25334 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
25335     /**
25336      * @cfg {Roo.data.Store} dataSource
25337      * The underlying data store providing the paged data
25338      */
25339     /**
25340      * @cfg {String/HTMLElement/Element} container
25341      * container The id or element that will contain the toolbar
25342      */
25343     /**
25344      * @cfg {Boolean} displayInfo
25345      * True to display the displayMsg (defaults to false)
25346      */
25347     /**
25348      * @cfg {Number} pageSize
25349      * The number of records to display per page (defaults to 20)
25350      */
25351     pageSize: 20,
25352     /**
25353      * @cfg {String} displayMsg
25354      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
25355      */
25356     displayMsg : 'Displaying {0} - {1} of {2}',
25357     /**
25358      * @cfg {String} emptyMsg
25359      * The message to display when no records are found (defaults to "No data to display")
25360      */
25361     emptyMsg : 'No data to display',
25362     /**
25363      * Customizable piece of the default paging text (defaults to "Page")
25364      * @type String
25365      */
25366     beforePageText : "Page",
25367     /**
25368      * Customizable piece of the default paging text (defaults to "of %0")
25369      * @type String
25370      */
25371     afterPageText : "of {0}",
25372     /**
25373      * Customizable piece of the default paging text (defaults to "First Page")
25374      * @type String
25375      */
25376     firstText : "First Page",
25377     /**
25378      * Customizable piece of the default paging text (defaults to "Previous Page")
25379      * @type String
25380      */
25381     prevText : "Previous Page",
25382     /**
25383      * Customizable piece of the default paging text (defaults to "Next Page")
25384      * @type String
25385      */
25386     nextText : "Next Page",
25387     /**
25388      * Customizable piece of the default paging text (defaults to "Last Page")
25389      * @type String
25390      */
25391     lastText : "Last Page",
25392     /**
25393      * Customizable piece of the default paging text (defaults to "Refresh")
25394      * @type String
25395      */
25396     refreshText : "Refresh",
25397
25398     buttons : false,
25399     // private
25400     onRender : function(ct, position) 
25401     {
25402         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25403         this.navgroup.parentId = this.id;
25404         this.navgroup.onRender(this.el, null);
25405         // add the buttons to the navgroup
25406         
25407         if(this.displayInfo){
25408             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25409             this.displayEl = this.el.select('.x-paging-info', true).first();
25410 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25411 //            this.displayEl = navel.el.select('span',true).first();
25412         }
25413         
25414         var _this = this;
25415         
25416         if(this.buttons){
25417             Roo.each(_this.buttons, function(e){ // this might need to use render????
25418                Roo.factory(e).render(_this.el);
25419             });
25420         }
25421             
25422         Roo.each(_this.toolbarItems, function(e) {
25423             _this.navgroup.addItem(e);
25424         });
25425         
25426         
25427         this.first = this.navgroup.addItem({
25428             tooltip: this.firstText,
25429             cls: "prev btn-outline-secondary",
25430             html : ' <i class="fa fa-step-backward"></i>',
25431             disabled: true,
25432             preventDefault: true,
25433             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25434         });
25435         
25436         this.prev =  this.navgroup.addItem({
25437             tooltip: this.prevText,
25438             cls: "prev btn-outline-secondary",
25439             html : ' <i class="fa fa-backward"></i>',
25440             disabled: true,
25441             preventDefault: true,
25442             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
25443         });
25444     //this.addSeparator();
25445         
25446         
25447         var field = this.navgroup.addItem( {
25448             tagtype : 'span',
25449             cls : 'x-paging-position  btn-outline-secondary',
25450              disabled: true,
25451             html : this.beforePageText  +
25452                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25453                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
25454          } ); //?? escaped?
25455         
25456         this.field = field.el.select('input', true).first();
25457         this.field.on("keydown", this.onPagingKeydown, this);
25458         this.field.on("focus", function(){this.dom.select();});
25459     
25460     
25461         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
25462         //this.field.setHeight(18);
25463         //this.addSeparator();
25464         this.next = this.navgroup.addItem({
25465             tooltip: this.nextText,
25466             cls: "next btn-outline-secondary",
25467             html : ' <i class="fa fa-forward"></i>',
25468             disabled: true,
25469             preventDefault: true,
25470             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
25471         });
25472         this.last = this.navgroup.addItem({
25473             tooltip: this.lastText,
25474             html : ' <i class="fa fa-step-forward"></i>',
25475             cls: "next btn-outline-secondary",
25476             disabled: true,
25477             preventDefault: true,
25478             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
25479         });
25480     //this.addSeparator();
25481         this.loading = this.navgroup.addItem({
25482             tooltip: this.refreshText,
25483             cls: "btn-outline-secondary",
25484             html : ' <i class="fa fa-refresh"></i>',
25485             preventDefault: true,
25486             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25487         });
25488         
25489     },
25490
25491     // private
25492     updateInfo : function(){
25493         if(this.displayEl){
25494             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25495             var msg = count == 0 ?
25496                 this.emptyMsg :
25497                 String.format(
25498                     this.displayMsg,
25499                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
25500                 );
25501             this.displayEl.update(msg);
25502         }
25503     },
25504
25505     // private
25506     onLoad : function(ds, r, o)
25507     {
25508         this.cursor = o.params.start ? o.params.start : 0;
25509         
25510         var d = this.getPageData(),
25511             ap = d.activePage,
25512             ps = d.pages;
25513         
25514         
25515         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25516         this.field.dom.value = ap;
25517         this.first.setDisabled(ap == 1);
25518         this.prev.setDisabled(ap == 1);
25519         this.next.setDisabled(ap == ps);
25520         this.last.setDisabled(ap == ps);
25521         this.loading.enable();
25522         this.updateInfo();
25523     },
25524
25525     // private
25526     getPageData : function(){
25527         var total = this.ds.getTotalCount();
25528         return {
25529             total : total,
25530             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25531             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25532         };
25533     },
25534
25535     // private
25536     onLoadError : function(){
25537         this.loading.enable();
25538     },
25539
25540     // private
25541     onPagingKeydown : function(e){
25542         var k = e.getKey();
25543         var d = this.getPageData();
25544         if(k == e.RETURN){
25545             var v = this.field.dom.value, pageNum;
25546             if(!v || isNaN(pageNum = parseInt(v, 10))){
25547                 this.field.dom.value = d.activePage;
25548                 return;
25549             }
25550             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25551             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25552             e.stopEvent();
25553         }
25554         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))
25555         {
25556           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25557           this.field.dom.value = pageNum;
25558           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25559           e.stopEvent();
25560         }
25561         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25562         {
25563           var v = this.field.dom.value, pageNum; 
25564           var increment = (e.shiftKey) ? 10 : 1;
25565           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25566                 increment *= -1;
25567           }
25568           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25569             this.field.dom.value = d.activePage;
25570             return;
25571           }
25572           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25573           {
25574             this.field.dom.value = parseInt(v, 10) + increment;
25575             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25576             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25577           }
25578           e.stopEvent();
25579         }
25580     },
25581
25582     // private
25583     beforeLoad : function(){
25584         if(this.loading){
25585             this.loading.disable();
25586         }
25587     },
25588
25589     // private
25590     onClick : function(which){
25591         
25592         var ds = this.ds;
25593         if (!ds) {
25594             return;
25595         }
25596         
25597         switch(which){
25598             case "first":
25599                 ds.load({params:{start: 0, limit: this.pageSize}});
25600             break;
25601             case "prev":
25602                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25603             break;
25604             case "next":
25605                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25606             break;
25607             case "last":
25608                 var total = ds.getTotalCount();
25609                 var extra = total % this.pageSize;
25610                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25611                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25612             break;
25613             case "refresh":
25614                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25615             break;
25616         }
25617     },
25618
25619     /**
25620      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25621      * @param {Roo.data.Store} store The data store to unbind
25622      */
25623     unbind : function(ds){
25624         ds.un("beforeload", this.beforeLoad, this);
25625         ds.un("load", this.onLoad, this);
25626         ds.un("loadexception", this.onLoadError, this);
25627         ds.un("remove", this.updateInfo, this);
25628         ds.un("add", this.updateInfo, this);
25629         this.ds = undefined;
25630     },
25631
25632     /**
25633      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25634      * @param {Roo.data.Store} store The data store to bind
25635      */
25636     bind : function(ds){
25637         ds.on("beforeload", this.beforeLoad, this);
25638         ds.on("load", this.onLoad, this);
25639         ds.on("loadexception", this.onLoadError, this);
25640         ds.on("remove", this.updateInfo, this);
25641         ds.on("add", this.updateInfo, this);
25642         this.ds = ds;
25643     }
25644 });/*
25645  * - LGPL
25646  *
25647  * element
25648  * 
25649  */
25650
25651 /**
25652  * @class Roo.bootstrap.MessageBar
25653  * @extends Roo.bootstrap.Component
25654  * Bootstrap MessageBar class
25655  * @cfg {String} html contents of the MessageBar
25656  * @cfg {String} weight (info | success | warning | danger) default info
25657  * @cfg {String} beforeClass insert the bar before the given class
25658  * @cfg {Boolean} closable (true | false) default false
25659  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25660  * 
25661  * @constructor
25662  * Create a new Element
25663  * @param {Object} config The config object
25664  */
25665
25666 Roo.bootstrap.MessageBar = function(config){
25667     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25668 };
25669
25670 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25671     
25672     html: '',
25673     weight: 'info',
25674     closable: false,
25675     fixed: false,
25676     beforeClass: 'bootstrap-sticky-wrap',
25677     
25678     getAutoCreate : function(){
25679         
25680         var cfg = {
25681             tag: 'div',
25682             cls: 'alert alert-dismissable alert-' + this.weight,
25683             cn: [
25684                 {
25685                     tag: 'span',
25686                     cls: 'message',
25687                     html: this.html || ''
25688                 }
25689             ]
25690         };
25691         
25692         if(this.fixed){
25693             cfg.cls += ' alert-messages-fixed';
25694         }
25695         
25696         if(this.closable){
25697             cfg.cn.push({
25698                 tag: 'button',
25699                 cls: 'close',
25700                 html: 'x'
25701             });
25702         }
25703         
25704         return cfg;
25705     },
25706     
25707     onRender : function(ct, position)
25708     {
25709         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25710         
25711         if(!this.el){
25712             var cfg = Roo.apply({},  this.getAutoCreate());
25713             cfg.id = Roo.id();
25714             
25715             if (this.cls) {
25716                 cfg.cls += ' ' + this.cls;
25717             }
25718             if (this.style) {
25719                 cfg.style = this.style;
25720             }
25721             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25722             
25723             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25724         }
25725         
25726         this.el.select('>button.close').on('click', this.hide, this);
25727         
25728     },
25729     
25730     show : function()
25731     {
25732         if (!this.rendered) {
25733             this.render();
25734         }
25735         
25736         this.el.show();
25737         
25738         this.fireEvent('show', this);
25739         
25740     },
25741     
25742     hide : function()
25743     {
25744         if (!this.rendered) {
25745             this.render();
25746         }
25747         
25748         this.el.hide();
25749         
25750         this.fireEvent('hide', this);
25751     },
25752     
25753     update : function()
25754     {
25755 //        var e = this.el.dom.firstChild;
25756 //        
25757 //        if(this.closable){
25758 //            e = e.nextSibling;
25759 //        }
25760 //        
25761 //        e.data = this.html || '';
25762
25763         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25764     }
25765    
25766 });
25767
25768  
25769
25770      /*
25771  * - LGPL
25772  *
25773  * Graph
25774  * 
25775  */
25776
25777
25778 /**
25779  * @class Roo.bootstrap.Graph
25780  * @extends Roo.bootstrap.Component
25781  * Bootstrap Graph class
25782 > Prameters
25783  -sm {number} sm 4
25784  -md {number} md 5
25785  @cfg {String} graphtype  bar | vbar | pie
25786  @cfg {number} g_x coodinator | centre x (pie)
25787  @cfg {number} g_y coodinator | centre y (pie)
25788  @cfg {number} g_r radius (pie)
25789  @cfg {number} g_height height of the chart (respected by all elements in the set)
25790  @cfg {number} g_width width of the chart (respected by all elements in the set)
25791  @cfg {Object} title The title of the chart
25792     
25793  -{Array}  values
25794  -opts (object) options for the chart 
25795      o {
25796      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25797      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25798      o vgutter (number)
25799      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.
25800      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25801      o to
25802      o stretch (boolean)
25803      o }
25804  -opts (object) options for the pie
25805      o{
25806      o cut
25807      o startAngle (number)
25808      o endAngle (number)
25809      } 
25810  *
25811  * @constructor
25812  * Create a new Input
25813  * @param {Object} config The config object
25814  */
25815
25816 Roo.bootstrap.Graph = function(config){
25817     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25818     
25819     this.addEvents({
25820         // img events
25821         /**
25822          * @event click
25823          * The img click event for the img.
25824          * @param {Roo.EventObject} e
25825          */
25826         "click" : true
25827     });
25828 };
25829
25830 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25831     
25832     sm: 4,
25833     md: 5,
25834     graphtype: 'bar',
25835     g_height: 250,
25836     g_width: 400,
25837     g_x: 50,
25838     g_y: 50,
25839     g_r: 30,
25840     opts:{
25841         //g_colors: this.colors,
25842         g_type: 'soft',
25843         g_gutter: '20%'
25844
25845     },
25846     title : false,
25847
25848     getAutoCreate : function(){
25849         
25850         var cfg = {
25851             tag: 'div',
25852             html : null
25853         };
25854         
25855         
25856         return  cfg;
25857     },
25858
25859     onRender : function(ct,position){
25860         
25861         
25862         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25863         
25864         if (typeof(Raphael) == 'undefined') {
25865             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25866             return;
25867         }
25868         
25869         this.raphael = Raphael(this.el.dom);
25870         
25871                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25872                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25873                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25874                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25875                 /*
25876                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25877                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25878                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25879                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25880                 
25881                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25882                 r.barchart(330, 10, 300, 220, data1);
25883                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25884                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25885                 */
25886                 
25887                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25888                 // r.barchart(30, 30, 560, 250,  xdata, {
25889                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25890                 //     axis : "0 0 1 1",
25891                 //     axisxlabels :  xdata
25892                 //     //yvalues : cols,
25893                    
25894                 // });
25895 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25896 //        
25897 //        this.load(null,xdata,{
25898 //                axis : "0 0 1 1",
25899 //                axisxlabels :  xdata
25900 //                });
25901
25902     },
25903
25904     load : function(graphtype,xdata,opts)
25905     {
25906         this.raphael.clear();
25907         if(!graphtype) {
25908             graphtype = this.graphtype;
25909         }
25910         if(!opts){
25911             opts = this.opts;
25912         }
25913         var r = this.raphael,
25914             fin = function () {
25915                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25916             },
25917             fout = function () {
25918                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25919             },
25920             pfin = function() {
25921                 this.sector.stop();
25922                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25923
25924                 if (this.label) {
25925                     this.label[0].stop();
25926                     this.label[0].attr({ r: 7.5 });
25927                     this.label[1].attr({ "font-weight": 800 });
25928                 }
25929             },
25930             pfout = function() {
25931                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25932
25933                 if (this.label) {
25934                     this.label[0].animate({ r: 5 }, 500, "bounce");
25935                     this.label[1].attr({ "font-weight": 400 });
25936                 }
25937             };
25938
25939         switch(graphtype){
25940             case 'bar':
25941                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25942                 break;
25943             case 'hbar':
25944                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25945                 break;
25946             case 'pie':
25947 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25948 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25949 //            
25950                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25951                 
25952                 break;
25953
25954         }
25955         
25956         if(this.title){
25957             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25958         }
25959         
25960     },
25961     
25962     setTitle: function(o)
25963     {
25964         this.title = o;
25965     },
25966     
25967     initEvents: function() {
25968         
25969         if(!this.href){
25970             this.el.on('click', this.onClick, this);
25971         }
25972     },
25973     
25974     onClick : function(e)
25975     {
25976         Roo.log('img onclick');
25977         this.fireEvent('click', this, e);
25978     }
25979    
25980 });
25981
25982  
25983 /*
25984  * - LGPL
25985  *
25986  * numberBox
25987  * 
25988  */
25989 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25990
25991 /**
25992  * @class Roo.bootstrap.dash.NumberBox
25993  * @extends Roo.bootstrap.Component
25994  * Bootstrap NumberBox class
25995  * @cfg {String} headline Box headline
25996  * @cfg {String} content Box content
25997  * @cfg {String} icon Box icon
25998  * @cfg {String} footer Footer text
25999  * @cfg {String} fhref Footer href
26000  * 
26001  * @constructor
26002  * Create a new NumberBox
26003  * @param {Object} config The config object
26004  */
26005
26006
26007 Roo.bootstrap.dash.NumberBox = function(config){
26008     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
26009     
26010 };
26011
26012 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
26013     
26014     headline : '',
26015     content : '',
26016     icon : '',
26017     footer : '',
26018     fhref : '',
26019     ficon : '',
26020     
26021     getAutoCreate : function(){
26022         
26023         var cfg = {
26024             tag : 'div',
26025             cls : 'small-box ',
26026             cn : [
26027                 {
26028                     tag : 'div',
26029                     cls : 'inner',
26030                     cn :[
26031                         {
26032                             tag : 'h3',
26033                             cls : 'roo-headline',
26034                             html : this.headline
26035                         },
26036                         {
26037                             tag : 'p',
26038                             cls : 'roo-content',
26039                             html : this.content
26040                         }
26041                     ]
26042                 }
26043             ]
26044         };
26045         
26046         if(this.icon){
26047             cfg.cn.push({
26048                 tag : 'div',
26049                 cls : 'icon',
26050                 cn :[
26051                     {
26052                         tag : 'i',
26053                         cls : 'ion ' + this.icon
26054                     }
26055                 ]
26056             });
26057         }
26058         
26059         if(this.footer){
26060             var footer = {
26061                 tag : 'a',
26062                 cls : 'small-box-footer',
26063                 href : this.fhref || '#',
26064                 html : this.footer
26065             };
26066             
26067             cfg.cn.push(footer);
26068             
26069         }
26070         
26071         return  cfg;
26072     },
26073
26074     onRender : function(ct,position){
26075         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
26076
26077
26078        
26079                 
26080     },
26081
26082     setHeadline: function (value)
26083     {
26084         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
26085     },
26086     
26087     setFooter: function (value, href)
26088     {
26089         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
26090         
26091         if(href){
26092             this.el.select('a.small-box-footer',true).first().attr('href', href);
26093         }
26094         
26095     },
26096
26097     setContent: function (value)
26098     {
26099         this.el.select('.roo-content',true).first().dom.innerHTML = value;
26100     },
26101
26102     initEvents: function() 
26103     {   
26104         
26105     }
26106     
26107 });
26108
26109  
26110 /*
26111  * - LGPL
26112  *
26113  * TabBox
26114  * 
26115  */
26116 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26117
26118 /**
26119  * @class Roo.bootstrap.dash.TabBox
26120  * @extends Roo.bootstrap.Component
26121  * Bootstrap TabBox class
26122  * @cfg {String} title Title of the TabBox
26123  * @cfg {String} icon Icon of the TabBox
26124  * @cfg {Boolean} showtabs (true|false) show the tabs default true
26125  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
26126  * 
26127  * @constructor
26128  * Create a new TabBox
26129  * @param {Object} config The config object
26130  */
26131
26132
26133 Roo.bootstrap.dash.TabBox = function(config){
26134     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
26135     this.addEvents({
26136         // raw events
26137         /**
26138          * @event addpane
26139          * When a pane is added
26140          * @param {Roo.bootstrap.dash.TabPane} pane
26141          */
26142         "addpane" : true,
26143         /**
26144          * @event activatepane
26145          * When a pane is activated
26146          * @param {Roo.bootstrap.dash.TabPane} pane
26147          */
26148         "activatepane" : true
26149         
26150          
26151     });
26152     
26153     this.panes = [];
26154 };
26155
26156 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
26157
26158     title : '',
26159     icon : false,
26160     showtabs : true,
26161     tabScrollable : false,
26162     
26163     getChildContainer : function()
26164     {
26165         return this.el.select('.tab-content', true).first();
26166     },
26167     
26168     getAutoCreate : function(){
26169         
26170         var header = {
26171             tag: 'li',
26172             cls: 'pull-left header',
26173             html: this.title,
26174             cn : []
26175         };
26176         
26177         if(this.icon){
26178             header.cn.push({
26179                 tag: 'i',
26180                 cls: 'fa ' + this.icon
26181             });
26182         }
26183         
26184         var h = {
26185             tag: 'ul',
26186             cls: 'nav nav-tabs pull-right',
26187             cn: [
26188                 header
26189             ]
26190         };
26191         
26192         if(this.tabScrollable){
26193             h = {
26194                 tag: 'div',
26195                 cls: 'tab-header',
26196                 cn: [
26197                     {
26198                         tag: 'ul',
26199                         cls: 'nav nav-tabs pull-right',
26200                         cn: [
26201                             header
26202                         ]
26203                     }
26204                 ]
26205             };
26206         }
26207         
26208         var cfg = {
26209             tag: 'div',
26210             cls: 'nav-tabs-custom',
26211             cn: [
26212                 h,
26213                 {
26214                     tag: 'div',
26215                     cls: 'tab-content no-padding',
26216                     cn: []
26217                 }
26218             ]
26219         };
26220
26221         return  cfg;
26222     },
26223     initEvents : function()
26224     {
26225         //Roo.log('add add pane handler');
26226         this.on('addpane', this.onAddPane, this);
26227     },
26228      /**
26229      * Updates the box title
26230      * @param {String} html to set the title to.
26231      */
26232     setTitle : function(value)
26233     {
26234         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
26235     },
26236     onAddPane : function(pane)
26237     {
26238         this.panes.push(pane);
26239         //Roo.log('addpane');
26240         //Roo.log(pane);
26241         // tabs are rendere left to right..
26242         if(!this.showtabs){
26243             return;
26244         }
26245         
26246         var ctr = this.el.select('.nav-tabs', true).first();
26247          
26248          
26249         var existing = ctr.select('.nav-tab',true);
26250         var qty = existing.getCount();;
26251         
26252         
26253         var tab = ctr.createChild({
26254             tag : 'li',
26255             cls : 'nav-tab' + (qty ? '' : ' active'),
26256             cn : [
26257                 {
26258                     tag : 'a',
26259                     href:'#',
26260                     html : pane.title
26261                 }
26262             ]
26263         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
26264         pane.tab = tab;
26265         
26266         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
26267         if (!qty) {
26268             pane.el.addClass('active');
26269         }
26270         
26271                 
26272     },
26273     onTabClick : function(ev,un,ob,pane)
26274     {
26275         //Roo.log('tab - prev default');
26276         ev.preventDefault();
26277         
26278         
26279         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
26280         pane.tab.addClass('active');
26281         //Roo.log(pane.title);
26282         this.getChildContainer().select('.tab-pane',true).removeClass('active');
26283         // technically we should have a deactivate event.. but maybe add later.
26284         // and it should not de-activate the selected tab...
26285         this.fireEvent('activatepane', pane);
26286         pane.el.addClass('active');
26287         pane.fireEvent('activate');
26288         
26289         
26290     },
26291     
26292     getActivePane : function()
26293     {
26294         var r = false;
26295         Roo.each(this.panes, function(p) {
26296             if(p.el.hasClass('active')){
26297                 r = p;
26298                 return false;
26299             }
26300             
26301             return;
26302         });
26303         
26304         return r;
26305     }
26306     
26307     
26308 });
26309
26310  
26311 /*
26312  * - LGPL
26313  *
26314  * Tab pane
26315  * 
26316  */
26317 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26318 /**
26319  * @class Roo.bootstrap.TabPane
26320  * @extends Roo.bootstrap.Component
26321  * Bootstrap TabPane class
26322  * @cfg {Boolean} active (false | true) Default false
26323  * @cfg {String} title title of panel
26324
26325  * 
26326  * @constructor
26327  * Create a new TabPane
26328  * @param {Object} config The config object
26329  */
26330
26331 Roo.bootstrap.dash.TabPane = function(config){
26332     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
26333     
26334     this.addEvents({
26335         // raw events
26336         /**
26337          * @event activate
26338          * When a pane is activated
26339          * @param {Roo.bootstrap.dash.TabPane} pane
26340          */
26341         "activate" : true
26342          
26343     });
26344 };
26345
26346 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
26347     
26348     active : false,
26349     title : '',
26350     
26351     // the tabBox that this is attached to.
26352     tab : false,
26353      
26354     getAutoCreate : function() 
26355     {
26356         var cfg = {
26357             tag: 'div',
26358             cls: 'tab-pane'
26359         };
26360         
26361         if(this.active){
26362             cfg.cls += ' active';
26363         }
26364         
26365         return cfg;
26366     },
26367     initEvents  : function()
26368     {
26369         //Roo.log('trigger add pane handler');
26370         this.parent().fireEvent('addpane', this)
26371     },
26372     
26373      /**
26374      * Updates the tab title 
26375      * @param {String} html to set the title to.
26376      */
26377     setTitle: function(str)
26378     {
26379         if (!this.tab) {
26380             return;
26381         }
26382         this.title = str;
26383         this.tab.select('a', true).first().dom.innerHTML = str;
26384         
26385     }
26386     
26387     
26388     
26389 });
26390
26391  
26392
26393
26394  /*
26395  * - LGPL
26396  *
26397  * menu
26398  * 
26399  */
26400 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26401
26402 /**
26403  * @class Roo.bootstrap.menu.Menu
26404  * @extends Roo.bootstrap.Component
26405  * Bootstrap Menu class - container for Menu
26406  * @cfg {String} html Text of the menu
26407  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26408  * @cfg {String} icon Font awesome icon
26409  * @cfg {String} pos Menu align to (top | bottom) default bottom
26410  * 
26411  * 
26412  * @constructor
26413  * Create a new Menu
26414  * @param {Object} config The config object
26415  */
26416
26417
26418 Roo.bootstrap.menu.Menu = function(config){
26419     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26420     
26421     this.addEvents({
26422         /**
26423          * @event beforeshow
26424          * Fires before this menu is displayed
26425          * @param {Roo.bootstrap.menu.Menu} this
26426          */
26427         beforeshow : true,
26428         /**
26429          * @event beforehide
26430          * Fires before this menu is hidden
26431          * @param {Roo.bootstrap.menu.Menu} this
26432          */
26433         beforehide : true,
26434         /**
26435          * @event show
26436          * Fires after this menu is displayed
26437          * @param {Roo.bootstrap.menu.Menu} this
26438          */
26439         show : true,
26440         /**
26441          * @event hide
26442          * Fires after this menu is hidden
26443          * @param {Roo.bootstrap.menu.Menu} this
26444          */
26445         hide : true,
26446         /**
26447          * @event click
26448          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26449          * @param {Roo.bootstrap.menu.Menu} this
26450          * @param {Roo.EventObject} e
26451          */
26452         click : true
26453     });
26454     
26455 };
26456
26457 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
26458     
26459     submenu : false,
26460     html : '',
26461     weight : 'default',
26462     icon : false,
26463     pos : 'bottom',
26464     
26465     
26466     getChildContainer : function() {
26467         if(this.isSubMenu){
26468             return this.el;
26469         }
26470         
26471         return this.el.select('ul.dropdown-menu', true).first();  
26472     },
26473     
26474     getAutoCreate : function()
26475     {
26476         var text = [
26477             {
26478                 tag : 'span',
26479                 cls : 'roo-menu-text',
26480                 html : this.html
26481             }
26482         ];
26483         
26484         if(this.icon){
26485             text.unshift({
26486                 tag : 'i',
26487                 cls : 'fa ' + this.icon
26488             })
26489         }
26490         
26491         
26492         var cfg = {
26493             tag : 'div',
26494             cls : 'btn-group',
26495             cn : [
26496                 {
26497                     tag : 'button',
26498                     cls : 'dropdown-button btn btn-' + this.weight,
26499                     cn : text
26500                 },
26501                 {
26502                     tag : 'button',
26503                     cls : 'dropdown-toggle btn btn-' + this.weight,
26504                     cn : [
26505                         {
26506                             tag : 'span',
26507                             cls : 'caret'
26508                         }
26509                     ]
26510                 },
26511                 {
26512                     tag : 'ul',
26513                     cls : 'dropdown-menu'
26514                 }
26515             ]
26516             
26517         };
26518         
26519         if(this.pos == 'top'){
26520             cfg.cls += ' dropup';
26521         }
26522         
26523         if(this.isSubMenu){
26524             cfg = {
26525                 tag : 'ul',
26526                 cls : 'dropdown-menu'
26527             }
26528         }
26529         
26530         return cfg;
26531     },
26532     
26533     onRender : function(ct, position)
26534     {
26535         this.isSubMenu = ct.hasClass('dropdown-submenu');
26536         
26537         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26538     },
26539     
26540     initEvents : function() 
26541     {
26542         if(this.isSubMenu){
26543             return;
26544         }
26545         
26546         this.hidden = true;
26547         
26548         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26549         this.triggerEl.on('click', this.onTriggerPress, this);
26550         
26551         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26552         this.buttonEl.on('click', this.onClick, this);
26553         
26554     },
26555     
26556     list : function()
26557     {
26558         if(this.isSubMenu){
26559             return this.el;
26560         }
26561         
26562         return this.el.select('ul.dropdown-menu', true).first();
26563     },
26564     
26565     onClick : function(e)
26566     {
26567         this.fireEvent("click", this, e);
26568     },
26569     
26570     onTriggerPress  : function(e)
26571     {   
26572         if (this.isVisible()) {
26573             this.hide();
26574         } else {
26575             this.show();
26576         }
26577     },
26578     
26579     isVisible : function(){
26580         return !this.hidden;
26581     },
26582     
26583     show : function()
26584     {
26585         this.fireEvent("beforeshow", this);
26586         
26587         this.hidden = false;
26588         this.el.addClass('open');
26589         
26590         Roo.get(document).on("mouseup", this.onMouseUp, this);
26591         
26592         this.fireEvent("show", this);
26593         
26594         
26595     },
26596     
26597     hide : function()
26598     {
26599         this.fireEvent("beforehide", this);
26600         
26601         this.hidden = true;
26602         this.el.removeClass('open');
26603         
26604         Roo.get(document).un("mouseup", this.onMouseUp);
26605         
26606         this.fireEvent("hide", this);
26607     },
26608     
26609     onMouseUp : function()
26610     {
26611         this.hide();
26612     }
26613     
26614 });
26615
26616  
26617  /*
26618  * - LGPL
26619  *
26620  * menu item
26621  * 
26622  */
26623 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26624
26625 /**
26626  * @class Roo.bootstrap.menu.Item
26627  * @extends Roo.bootstrap.Component
26628  * Bootstrap MenuItem class
26629  * @cfg {Boolean} submenu (true | false) default false
26630  * @cfg {String} html text of the item
26631  * @cfg {String} href the link
26632  * @cfg {Boolean} disable (true | false) default false
26633  * @cfg {Boolean} preventDefault (true | false) default true
26634  * @cfg {String} icon Font awesome icon
26635  * @cfg {String} pos Submenu align to (left | right) default right 
26636  * 
26637  * 
26638  * @constructor
26639  * Create a new Item
26640  * @param {Object} config The config object
26641  */
26642
26643
26644 Roo.bootstrap.menu.Item = function(config){
26645     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26646     this.addEvents({
26647         /**
26648          * @event mouseover
26649          * Fires when the mouse is hovering over this menu
26650          * @param {Roo.bootstrap.menu.Item} this
26651          * @param {Roo.EventObject} e
26652          */
26653         mouseover : true,
26654         /**
26655          * @event mouseout
26656          * Fires when the mouse exits this menu
26657          * @param {Roo.bootstrap.menu.Item} this
26658          * @param {Roo.EventObject} e
26659          */
26660         mouseout : true,
26661         // raw events
26662         /**
26663          * @event click
26664          * The raw click event for the entire grid.
26665          * @param {Roo.EventObject} e
26666          */
26667         click : true
26668     });
26669 };
26670
26671 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26672     
26673     submenu : false,
26674     href : '',
26675     html : '',
26676     preventDefault: true,
26677     disable : false,
26678     icon : false,
26679     pos : 'right',
26680     
26681     getAutoCreate : function()
26682     {
26683         var text = [
26684             {
26685                 tag : 'span',
26686                 cls : 'roo-menu-item-text',
26687                 html : this.html
26688             }
26689         ];
26690         
26691         if(this.icon){
26692             text.unshift({
26693                 tag : 'i',
26694                 cls : 'fa ' + this.icon
26695             })
26696         }
26697         
26698         var cfg = {
26699             tag : 'li',
26700             cn : [
26701                 {
26702                     tag : 'a',
26703                     href : this.href || '#',
26704                     cn : text
26705                 }
26706             ]
26707         };
26708         
26709         if(this.disable){
26710             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26711         }
26712         
26713         if(this.submenu){
26714             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26715             
26716             if(this.pos == 'left'){
26717                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26718             }
26719         }
26720         
26721         return cfg;
26722     },
26723     
26724     initEvents : function() 
26725     {
26726         this.el.on('mouseover', this.onMouseOver, this);
26727         this.el.on('mouseout', this.onMouseOut, this);
26728         
26729         this.el.select('a', true).first().on('click', this.onClick, this);
26730         
26731     },
26732     
26733     onClick : function(e)
26734     {
26735         if(this.preventDefault){
26736             e.preventDefault();
26737         }
26738         
26739         this.fireEvent("click", this, e);
26740     },
26741     
26742     onMouseOver : function(e)
26743     {
26744         if(this.submenu && this.pos == 'left'){
26745             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26746         }
26747         
26748         this.fireEvent("mouseover", this, e);
26749     },
26750     
26751     onMouseOut : function(e)
26752     {
26753         this.fireEvent("mouseout", this, e);
26754     }
26755 });
26756
26757  
26758
26759  /*
26760  * - LGPL
26761  *
26762  * menu separator
26763  * 
26764  */
26765 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26766
26767 /**
26768  * @class Roo.bootstrap.menu.Separator
26769  * @extends Roo.bootstrap.Component
26770  * Bootstrap Separator class
26771  * 
26772  * @constructor
26773  * Create a new Separator
26774  * @param {Object} config The config object
26775  */
26776
26777
26778 Roo.bootstrap.menu.Separator = function(config){
26779     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26780 };
26781
26782 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26783     
26784     getAutoCreate : function(){
26785         var cfg = {
26786             tag : 'li',
26787             cls: 'divider'
26788         };
26789         
26790         return cfg;
26791     }
26792    
26793 });
26794
26795  
26796
26797  /*
26798  * - LGPL
26799  *
26800  * Tooltip
26801  * 
26802  */
26803
26804 /**
26805  * @class Roo.bootstrap.Tooltip
26806  * Bootstrap Tooltip class
26807  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26808  * to determine which dom element triggers the tooltip.
26809  * 
26810  * It needs to add support for additional attributes like tooltip-position
26811  * 
26812  * @constructor
26813  * Create a new Toolti
26814  * @param {Object} config The config object
26815  */
26816
26817 Roo.bootstrap.Tooltip = function(config){
26818     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26819     
26820     this.alignment = Roo.bootstrap.Tooltip.alignment;
26821     
26822     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26823         this.alignment = config.alignment;
26824     }
26825     
26826 };
26827
26828 Roo.apply(Roo.bootstrap.Tooltip, {
26829     /**
26830      * @function init initialize tooltip monitoring.
26831      * @static
26832      */
26833     currentEl : false,
26834     currentTip : false,
26835     currentRegion : false,
26836     
26837     //  init : delay?
26838     
26839     init : function()
26840     {
26841         Roo.get(document).on('mouseover', this.enter ,this);
26842         Roo.get(document).on('mouseout', this.leave, this);
26843          
26844         
26845         this.currentTip = new Roo.bootstrap.Tooltip();
26846     },
26847     
26848     enter : function(ev)
26849     {
26850         var dom = ev.getTarget();
26851         
26852         //Roo.log(['enter',dom]);
26853         var el = Roo.fly(dom);
26854         if (this.currentEl) {
26855             //Roo.log(dom);
26856             //Roo.log(this.currentEl);
26857             //Roo.log(this.currentEl.contains(dom));
26858             if (this.currentEl == el) {
26859                 return;
26860             }
26861             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26862                 return;
26863             }
26864
26865         }
26866         
26867         if (this.currentTip.el) {
26868             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26869         }    
26870         //Roo.log(ev);
26871         
26872         if(!el || el.dom == document){
26873             return;
26874         }
26875         
26876         var bindEl = el;
26877         
26878         // you can not look for children, as if el is the body.. then everythign is the child..
26879         if (!el.attr('tooltip')) { //
26880             if (!el.select("[tooltip]").elements.length) {
26881                 return;
26882             }
26883             // is the mouse over this child...?
26884             bindEl = el.select("[tooltip]").first();
26885             var xy = ev.getXY();
26886             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26887                 //Roo.log("not in region.");
26888                 return;
26889             }
26890             //Roo.log("child element over..");
26891             
26892         }
26893         this.currentEl = bindEl;
26894         this.currentTip.bind(bindEl);
26895         this.currentRegion = Roo.lib.Region.getRegion(dom);
26896         this.currentTip.enter();
26897         
26898     },
26899     leave : function(ev)
26900     {
26901         var dom = ev.getTarget();
26902         //Roo.log(['leave',dom]);
26903         if (!this.currentEl) {
26904             return;
26905         }
26906         
26907         
26908         if (dom != this.currentEl.dom) {
26909             return;
26910         }
26911         var xy = ev.getXY();
26912         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26913             return;
26914         }
26915         // only activate leave if mouse cursor is outside... bounding box..
26916         
26917         
26918         
26919         
26920         if (this.currentTip) {
26921             this.currentTip.leave();
26922         }
26923         //Roo.log('clear currentEl');
26924         this.currentEl = false;
26925         
26926         
26927     },
26928     alignment : {
26929         'left' : ['r-l', [-2,0], 'right'],
26930         'right' : ['l-r', [2,0], 'left'],
26931         'bottom' : ['t-b', [0,2], 'top'],
26932         'top' : [ 'b-t', [0,-2], 'bottom']
26933     }
26934     
26935 });
26936
26937
26938 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26939     
26940     
26941     bindEl : false,
26942     
26943     delay : null, // can be { show : 300 , hide: 500}
26944     
26945     timeout : null,
26946     
26947     hoverState : null, //???
26948     
26949     placement : 'bottom', 
26950     
26951     alignment : false,
26952     
26953     getAutoCreate : function(){
26954     
26955         var cfg = {
26956            cls : 'tooltip',
26957            role : 'tooltip',
26958            cn : [
26959                 {
26960                     cls : 'tooltip-arrow'
26961                 },
26962                 {
26963                     cls : 'tooltip-inner'
26964                 }
26965            ]
26966         };
26967         
26968         return cfg;
26969     },
26970     bind : function(el)
26971     {
26972         this.bindEl = el;
26973     },
26974       
26975     
26976     enter : function () {
26977        
26978         if (this.timeout != null) {
26979             clearTimeout(this.timeout);
26980         }
26981         
26982         this.hoverState = 'in';
26983          //Roo.log("enter - show");
26984         if (!this.delay || !this.delay.show) {
26985             this.show();
26986             return;
26987         }
26988         var _t = this;
26989         this.timeout = setTimeout(function () {
26990             if (_t.hoverState == 'in') {
26991                 _t.show();
26992             }
26993         }, this.delay.show);
26994     },
26995     leave : function()
26996     {
26997         clearTimeout(this.timeout);
26998     
26999         this.hoverState = 'out';
27000          if (!this.delay || !this.delay.hide) {
27001             this.hide();
27002             return;
27003         }
27004        
27005         var _t = this;
27006         this.timeout = setTimeout(function () {
27007             //Roo.log("leave - timeout");
27008             
27009             if (_t.hoverState == 'out') {
27010                 _t.hide();
27011                 Roo.bootstrap.Tooltip.currentEl = false;
27012             }
27013         }, delay);
27014     },
27015     
27016     show : function (msg)
27017     {
27018         if (!this.el) {
27019             this.render(document.body);
27020         }
27021         // set content.
27022         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
27023         
27024         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
27025         
27026         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
27027         
27028         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
27029         
27030         var placement = typeof this.placement == 'function' ?
27031             this.placement.call(this, this.el, on_el) :
27032             this.placement;
27033             
27034         var autoToken = /\s?auto?\s?/i;
27035         var autoPlace = autoToken.test(placement);
27036         if (autoPlace) {
27037             placement = placement.replace(autoToken, '') || 'top';
27038         }
27039         
27040         //this.el.detach()
27041         //this.el.setXY([0,0]);
27042         this.el.show();
27043         //this.el.dom.style.display='block';
27044         
27045         //this.el.appendTo(on_el);
27046         
27047         var p = this.getPosition();
27048         var box = this.el.getBox();
27049         
27050         if (autoPlace) {
27051             // fixme..
27052         }
27053         
27054         var align = this.alignment[placement];
27055         
27056         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
27057         
27058         if(placement == 'top' || placement == 'bottom'){
27059             if(xy[0] < 0){
27060                 placement = 'right';
27061             }
27062             
27063             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
27064                 placement = 'left';
27065             }
27066             
27067             var scroll = Roo.select('body', true).first().getScroll();
27068             
27069             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
27070                 placement = 'top';
27071             }
27072             
27073             align = this.alignment[placement];
27074         }
27075         
27076         this.el.alignTo(this.bindEl, align[0],align[1]);
27077         //var arrow = this.el.select('.arrow',true).first();
27078         //arrow.set(align[2], 
27079         
27080         this.el.addClass(placement);
27081         
27082         this.el.addClass('in fade');
27083         
27084         this.hoverState = null;
27085         
27086         if (this.el.hasClass('fade')) {
27087             // fade it?
27088         }
27089         
27090     },
27091     hide : function()
27092     {
27093          
27094         if (!this.el) {
27095             return;
27096         }
27097         //this.el.setXY([0,0]);
27098         this.el.removeClass('in');
27099         //this.el.hide();
27100         
27101     }
27102     
27103 });
27104  
27105
27106  /*
27107  * - LGPL
27108  *
27109  * Location Picker
27110  * 
27111  */
27112
27113 /**
27114  * @class Roo.bootstrap.LocationPicker
27115  * @extends Roo.bootstrap.Component
27116  * Bootstrap LocationPicker class
27117  * @cfg {Number} latitude Position when init default 0
27118  * @cfg {Number} longitude Position when init default 0
27119  * @cfg {Number} zoom default 15
27120  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
27121  * @cfg {Boolean} mapTypeControl default false
27122  * @cfg {Boolean} disableDoubleClickZoom default false
27123  * @cfg {Boolean} scrollwheel default true
27124  * @cfg {Boolean} streetViewControl default false
27125  * @cfg {Number} radius default 0
27126  * @cfg {String} locationName
27127  * @cfg {Boolean} draggable default true
27128  * @cfg {Boolean} enableAutocomplete default false
27129  * @cfg {Boolean} enableReverseGeocode default true
27130  * @cfg {String} markerTitle
27131  * 
27132  * @constructor
27133  * Create a new LocationPicker
27134  * @param {Object} config The config object
27135  */
27136
27137
27138 Roo.bootstrap.LocationPicker = function(config){
27139     
27140     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
27141     
27142     this.addEvents({
27143         /**
27144          * @event initial
27145          * Fires when the picker initialized.
27146          * @param {Roo.bootstrap.LocationPicker} this
27147          * @param {Google Location} location
27148          */
27149         initial : true,
27150         /**
27151          * @event positionchanged
27152          * Fires when the picker position changed.
27153          * @param {Roo.bootstrap.LocationPicker} this
27154          * @param {Google Location} location
27155          */
27156         positionchanged : true,
27157         /**
27158          * @event resize
27159          * Fires when the map resize.
27160          * @param {Roo.bootstrap.LocationPicker} this
27161          */
27162         resize : true,
27163         /**
27164          * @event show
27165          * Fires when the map show.
27166          * @param {Roo.bootstrap.LocationPicker} this
27167          */
27168         show : true,
27169         /**
27170          * @event hide
27171          * Fires when the map hide.
27172          * @param {Roo.bootstrap.LocationPicker} this
27173          */
27174         hide : true,
27175         /**
27176          * @event mapClick
27177          * Fires when click the map.
27178          * @param {Roo.bootstrap.LocationPicker} this
27179          * @param {Map event} e
27180          */
27181         mapClick : true,
27182         /**
27183          * @event mapRightClick
27184          * Fires when right click the map.
27185          * @param {Roo.bootstrap.LocationPicker} this
27186          * @param {Map event} e
27187          */
27188         mapRightClick : true,
27189         /**
27190          * @event markerClick
27191          * Fires when click the marker.
27192          * @param {Roo.bootstrap.LocationPicker} this
27193          * @param {Map event} e
27194          */
27195         markerClick : true,
27196         /**
27197          * @event markerRightClick
27198          * Fires when right click the marker.
27199          * @param {Roo.bootstrap.LocationPicker} this
27200          * @param {Map event} e
27201          */
27202         markerRightClick : true,
27203         /**
27204          * @event OverlayViewDraw
27205          * Fires when OverlayView Draw
27206          * @param {Roo.bootstrap.LocationPicker} this
27207          */
27208         OverlayViewDraw : true,
27209         /**
27210          * @event OverlayViewOnAdd
27211          * Fires when OverlayView Draw
27212          * @param {Roo.bootstrap.LocationPicker} this
27213          */
27214         OverlayViewOnAdd : true,
27215         /**
27216          * @event OverlayViewOnRemove
27217          * Fires when OverlayView Draw
27218          * @param {Roo.bootstrap.LocationPicker} this
27219          */
27220         OverlayViewOnRemove : true,
27221         /**
27222          * @event OverlayViewShow
27223          * Fires when OverlayView Draw
27224          * @param {Roo.bootstrap.LocationPicker} this
27225          * @param {Pixel} cpx
27226          */
27227         OverlayViewShow : true,
27228         /**
27229          * @event OverlayViewHide
27230          * Fires when OverlayView Draw
27231          * @param {Roo.bootstrap.LocationPicker} this
27232          */
27233         OverlayViewHide : true,
27234         /**
27235          * @event loadexception
27236          * Fires when load google lib failed.
27237          * @param {Roo.bootstrap.LocationPicker} this
27238          */
27239         loadexception : true
27240     });
27241         
27242 };
27243
27244 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
27245     
27246     gMapContext: false,
27247     
27248     latitude: 0,
27249     longitude: 0,
27250     zoom: 15,
27251     mapTypeId: false,
27252     mapTypeControl: false,
27253     disableDoubleClickZoom: false,
27254     scrollwheel: true,
27255     streetViewControl: false,
27256     radius: 0,
27257     locationName: '',
27258     draggable: true,
27259     enableAutocomplete: false,
27260     enableReverseGeocode: true,
27261     markerTitle: '',
27262     
27263     getAutoCreate: function()
27264     {
27265
27266         var cfg = {
27267             tag: 'div',
27268             cls: 'roo-location-picker'
27269         };
27270         
27271         return cfg
27272     },
27273     
27274     initEvents: function(ct, position)
27275     {       
27276         if(!this.el.getWidth() || this.isApplied()){
27277             return;
27278         }
27279         
27280         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27281         
27282         this.initial();
27283     },
27284     
27285     initial: function()
27286     {
27287         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
27288             this.fireEvent('loadexception', this);
27289             return;
27290         }
27291         
27292         if(!this.mapTypeId){
27293             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
27294         }
27295         
27296         this.gMapContext = this.GMapContext();
27297         
27298         this.initOverlayView();
27299         
27300         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
27301         
27302         var _this = this;
27303                 
27304         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
27305             _this.setPosition(_this.gMapContext.marker.position);
27306         });
27307         
27308         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
27309             _this.fireEvent('mapClick', this, event);
27310             
27311         });
27312
27313         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
27314             _this.fireEvent('mapRightClick', this, event);
27315             
27316         });
27317         
27318         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
27319             _this.fireEvent('markerClick', this, event);
27320             
27321         });
27322
27323         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
27324             _this.fireEvent('markerRightClick', this, event);
27325             
27326         });
27327         
27328         this.setPosition(this.gMapContext.location);
27329         
27330         this.fireEvent('initial', this, this.gMapContext.location);
27331     },
27332     
27333     initOverlayView: function()
27334     {
27335         var _this = this;
27336         
27337         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
27338             
27339             draw: function()
27340             {
27341                 _this.fireEvent('OverlayViewDraw', _this);
27342             },
27343             
27344             onAdd: function()
27345             {
27346                 _this.fireEvent('OverlayViewOnAdd', _this);
27347             },
27348             
27349             onRemove: function()
27350             {
27351                 _this.fireEvent('OverlayViewOnRemove', _this);
27352             },
27353             
27354             show: function(cpx)
27355             {
27356                 _this.fireEvent('OverlayViewShow', _this, cpx);
27357             },
27358             
27359             hide: function()
27360             {
27361                 _this.fireEvent('OverlayViewHide', _this);
27362             }
27363             
27364         });
27365     },
27366     
27367     fromLatLngToContainerPixel: function(event)
27368     {
27369         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27370     },
27371     
27372     isApplied: function() 
27373     {
27374         return this.getGmapContext() == false ? false : true;
27375     },
27376     
27377     getGmapContext: function() 
27378     {
27379         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27380     },
27381     
27382     GMapContext: function() 
27383     {
27384         var position = new google.maps.LatLng(this.latitude, this.longitude);
27385         
27386         var _map = new google.maps.Map(this.el.dom, {
27387             center: position,
27388             zoom: this.zoom,
27389             mapTypeId: this.mapTypeId,
27390             mapTypeControl: this.mapTypeControl,
27391             disableDoubleClickZoom: this.disableDoubleClickZoom,
27392             scrollwheel: this.scrollwheel,
27393             streetViewControl: this.streetViewControl,
27394             locationName: this.locationName,
27395             draggable: this.draggable,
27396             enableAutocomplete: this.enableAutocomplete,
27397             enableReverseGeocode: this.enableReverseGeocode
27398         });
27399         
27400         var _marker = new google.maps.Marker({
27401             position: position,
27402             map: _map,
27403             title: this.markerTitle,
27404             draggable: this.draggable
27405         });
27406         
27407         return {
27408             map: _map,
27409             marker: _marker,
27410             circle: null,
27411             location: position,
27412             radius: this.radius,
27413             locationName: this.locationName,
27414             addressComponents: {
27415                 formatted_address: null,
27416                 addressLine1: null,
27417                 addressLine2: null,
27418                 streetName: null,
27419                 streetNumber: null,
27420                 city: null,
27421                 district: null,
27422                 state: null,
27423                 stateOrProvince: null
27424             },
27425             settings: this,
27426             domContainer: this.el.dom,
27427             geodecoder: new google.maps.Geocoder()
27428         };
27429     },
27430     
27431     drawCircle: function(center, radius, options) 
27432     {
27433         if (this.gMapContext.circle != null) {
27434             this.gMapContext.circle.setMap(null);
27435         }
27436         if (radius > 0) {
27437             radius *= 1;
27438             options = Roo.apply({}, options, {
27439                 strokeColor: "#0000FF",
27440                 strokeOpacity: .35,
27441                 strokeWeight: 2,
27442                 fillColor: "#0000FF",
27443                 fillOpacity: .2
27444             });
27445             
27446             options.map = this.gMapContext.map;
27447             options.radius = radius;
27448             options.center = center;
27449             this.gMapContext.circle = new google.maps.Circle(options);
27450             return this.gMapContext.circle;
27451         }
27452         
27453         return null;
27454     },
27455     
27456     setPosition: function(location) 
27457     {
27458         this.gMapContext.location = location;
27459         this.gMapContext.marker.setPosition(location);
27460         this.gMapContext.map.panTo(location);
27461         this.drawCircle(location, this.gMapContext.radius, {});
27462         
27463         var _this = this;
27464         
27465         if (this.gMapContext.settings.enableReverseGeocode) {
27466             this.gMapContext.geodecoder.geocode({
27467                 latLng: this.gMapContext.location
27468             }, function(results, status) {
27469                 
27470                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27471                     _this.gMapContext.locationName = results[0].formatted_address;
27472                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27473                     
27474                     _this.fireEvent('positionchanged', this, location);
27475                 }
27476             });
27477             
27478             return;
27479         }
27480         
27481         this.fireEvent('positionchanged', this, location);
27482     },
27483     
27484     resize: function()
27485     {
27486         google.maps.event.trigger(this.gMapContext.map, "resize");
27487         
27488         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27489         
27490         this.fireEvent('resize', this);
27491     },
27492     
27493     setPositionByLatLng: function(latitude, longitude)
27494     {
27495         this.setPosition(new google.maps.LatLng(latitude, longitude));
27496     },
27497     
27498     getCurrentPosition: function() 
27499     {
27500         return {
27501             latitude: this.gMapContext.location.lat(),
27502             longitude: this.gMapContext.location.lng()
27503         };
27504     },
27505     
27506     getAddressName: function() 
27507     {
27508         return this.gMapContext.locationName;
27509     },
27510     
27511     getAddressComponents: function() 
27512     {
27513         return this.gMapContext.addressComponents;
27514     },
27515     
27516     address_component_from_google_geocode: function(address_components) 
27517     {
27518         var result = {};
27519         
27520         for (var i = 0; i < address_components.length; i++) {
27521             var component = address_components[i];
27522             if (component.types.indexOf("postal_code") >= 0) {
27523                 result.postalCode = component.short_name;
27524             } else if (component.types.indexOf("street_number") >= 0) {
27525                 result.streetNumber = component.short_name;
27526             } else if (component.types.indexOf("route") >= 0) {
27527                 result.streetName = component.short_name;
27528             } else if (component.types.indexOf("neighborhood") >= 0) {
27529                 result.city = component.short_name;
27530             } else if (component.types.indexOf("locality") >= 0) {
27531                 result.city = component.short_name;
27532             } else if (component.types.indexOf("sublocality") >= 0) {
27533                 result.district = component.short_name;
27534             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27535                 result.stateOrProvince = component.short_name;
27536             } else if (component.types.indexOf("country") >= 0) {
27537                 result.country = component.short_name;
27538             }
27539         }
27540         
27541         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27542         result.addressLine2 = "";
27543         return result;
27544     },
27545     
27546     setZoomLevel: function(zoom)
27547     {
27548         this.gMapContext.map.setZoom(zoom);
27549     },
27550     
27551     show: function()
27552     {
27553         if(!this.el){
27554             return;
27555         }
27556         
27557         this.el.show();
27558         
27559         this.resize();
27560         
27561         this.fireEvent('show', this);
27562     },
27563     
27564     hide: function()
27565     {
27566         if(!this.el){
27567             return;
27568         }
27569         
27570         this.el.hide();
27571         
27572         this.fireEvent('hide', this);
27573     }
27574     
27575 });
27576
27577 Roo.apply(Roo.bootstrap.LocationPicker, {
27578     
27579     OverlayView : function(map, options)
27580     {
27581         options = options || {};
27582         
27583         this.setMap(map);
27584     }
27585     
27586     
27587 });/**
27588  * @class Roo.bootstrap.Alert
27589  * @extends Roo.bootstrap.Component
27590  * Bootstrap Alert class - shows an alert area box
27591  * eg
27592  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27593   Enter a valid email address
27594 </div>
27595  * @licence LGPL
27596  * @cfg {String} title The title of alert
27597  * @cfg {String} html The content of alert
27598  * @cfg {String} weight (  success | info | warning | danger )
27599  * @cfg {String} faicon font-awesomeicon
27600  * 
27601  * @constructor
27602  * Create a new alert
27603  * @param {Object} config The config object
27604  */
27605
27606
27607 Roo.bootstrap.Alert = function(config){
27608     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27609     
27610 };
27611
27612 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27613     
27614     title: '',
27615     html: '',
27616     weight: false,
27617     faicon: false,
27618     
27619     getAutoCreate : function()
27620     {
27621         
27622         var cfg = {
27623             tag : 'div',
27624             cls : 'alert',
27625             cn : [
27626                 {
27627                     tag : 'i',
27628                     cls : 'roo-alert-icon'
27629                     
27630                 },
27631                 {
27632                     tag : 'b',
27633                     cls : 'roo-alert-title',
27634                     html : this.title
27635                 },
27636                 {
27637                     tag : 'span',
27638                     cls : 'roo-alert-text',
27639                     html : this.html
27640                 }
27641             ]
27642         };
27643         
27644         if(this.faicon){
27645             cfg.cn[0].cls += ' fa ' + this.faicon;
27646         }
27647         
27648         if(this.weight){
27649             cfg.cls += ' alert-' + this.weight;
27650         }
27651         
27652         return cfg;
27653     },
27654     
27655     initEvents: function() 
27656     {
27657         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27658     },
27659     
27660     setTitle : function(str)
27661     {
27662         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27663     },
27664     
27665     setText : function(str)
27666     {
27667         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27668     },
27669     
27670     setWeight : function(weight)
27671     {
27672         if(this.weight){
27673             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27674         }
27675         
27676         this.weight = weight;
27677         
27678         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27679     },
27680     
27681     setIcon : function(icon)
27682     {
27683         if(this.faicon){
27684             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27685         }
27686         
27687         this.faicon = icon;
27688         
27689         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27690     },
27691     
27692     hide: function() 
27693     {
27694         this.el.hide();   
27695     },
27696     
27697     show: function() 
27698     {  
27699         this.el.show();   
27700     }
27701     
27702 });
27703
27704  
27705 /*
27706 * Licence: LGPL
27707 */
27708
27709 /**
27710  * @class Roo.bootstrap.UploadCropbox
27711  * @extends Roo.bootstrap.Component
27712  * Bootstrap UploadCropbox class
27713  * @cfg {String} emptyText show when image has been loaded
27714  * @cfg {String} rotateNotify show when image too small to rotate
27715  * @cfg {Number} errorTimeout default 3000
27716  * @cfg {Number} minWidth default 300
27717  * @cfg {Number} minHeight default 300
27718  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27719  * @cfg {Boolean} isDocument (true|false) default false
27720  * @cfg {String} url action url
27721  * @cfg {String} paramName default 'imageUpload'
27722  * @cfg {String} method default POST
27723  * @cfg {Boolean} loadMask (true|false) default true
27724  * @cfg {Boolean} loadingText default 'Loading...'
27725  * 
27726  * @constructor
27727  * Create a new UploadCropbox
27728  * @param {Object} config The config object
27729  */
27730
27731 Roo.bootstrap.UploadCropbox = function(config){
27732     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27733     
27734     this.addEvents({
27735         /**
27736          * @event beforeselectfile
27737          * Fire before select file
27738          * @param {Roo.bootstrap.UploadCropbox} this
27739          */
27740         "beforeselectfile" : true,
27741         /**
27742          * @event initial
27743          * Fire after initEvent
27744          * @param {Roo.bootstrap.UploadCropbox} this
27745          */
27746         "initial" : true,
27747         /**
27748          * @event crop
27749          * Fire after initEvent
27750          * @param {Roo.bootstrap.UploadCropbox} this
27751          * @param {String} data
27752          */
27753         "crop" : true,
27754         /**
27755          * @event prepare
27756          * Fire when preparing the file data
27757          * @param {Roo.bootstrap.UploadCropbox} this
27758          * @param {Object} file
27759          */
27760         "prepare" : true,
27761         /**
27762          * @event exception
27763          * Fire when get exception
27764          * @param {Roo.bootstrap.UploadCropbox} this
27765          * @param {XMLHttpRequest} xhr
27766          */
27767         "exception" : true,
27768         /**
27769          * @event beforeloadcanvas
27770          * Fire before load the canvas
27771          * @param {Roo.bootstrap.UploadCropbox} this
27772          * @param {String} src
27773          */
27774         "beforeloadcanvas" : true,
27775         /**
27776          * @event trash
27777          * Fire when trash image
27778          * @param {Roo.bootstrap.UploadCropbox} this
27779          */
27780         "trash" : true,
27781         /**
27782          * @event download
27783          * Fire when download the image
27784          * @param {Roo.bootstrap.UploadCropbox} this
27785          */
27786         "download" : true,
27787         /**
27788          * @event footerbuttonclick
27789          * Fire when footerbuttonclick
27790          * @param {Roo.bootstrap.UploadCropbox} this
27791          * @param {String} type
27792          */
27793         "footerbuttonclick" : true,
27794         /**
27795          * @event resize
27796          * Fire when resize
27797          * @param {Roo.bootstrap.UploadCropbox} this
27798          */
27799         "resize" : true,
27800         /**
27801          * @event rotate
27802          * Fire when rotate the image
27803          * @param {Roo.bootstrap.UploadCropbox} this
27804          * @param {String} pos
27805          */
27806         "rotate" : true,
27807         /**
27808          * @event inspect
27809          * Fire when inspect the file
27810          * @param {Roo.bootstrap.UploadCropbox} this
27811          * @param {Object} file
27812          */
27813         "inspect" : true,
27814         /**
27815          * @event upload
27816          * Fire when xhr upload the file
27817          * @param {Roo.bootstrap.UploadCropbox} this
27818          * @param {Object} data
27819          */
27820         "upload" : true,
27821         /**
27822          * @event arrange
27823          * Fire when arrange the file data
27824          * @param {Roo.bootstrap.UploadCropbox} this
27825          * @param {Object} formData
27826          */
27827         "arrange" : true
27828     });
27829     
27830     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27831 };
27832
27833 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27834     
27835     emptyText : 'Click to upload image',
27836     rotateNotify : 'Image is too small to rotate',
27837     errorTimeout : 3000,
27838     scale : 0,
27839     baseScale : 1,
27840     rotate : 0,
27841     dragable : false,
27842     pinching : false,
27843     mouseX : 0,
27844     mouseY : 0,
27845     cropData : false,
27846     minWidth : 300,
27847     minHeight : 300,
27848     file : false,
27849     exif : {},
27850     baseRotate : 1,
27851     cropType : 'image/jpeg',
27852     buttons : false,
27853     canvasLoaded : false,
27854     isDocument : false,
27855     method : 'POST',
27856     paramName : 'imageUpload',
27857     loadMask : true,
27858     loadingText : 'Loading...',
27859     maskEl : false,
27860     
27861     getAutoCreate : function()
27862     {
27863         var cfg = {
27864             tag : 'div',
27865             cls : 'roo-upload-cropbox',
27866             cn : [
27867                 {
27868                     tag : 'input',
27869                     cls : 'roo-upload-cropbox-selector',
27870                     type : 'file'
27871                 },
27872                 {
27873                     tag : 'div',
27874                     cls : 'roo-upload-cropbox-body',
27875                     style : 'cursor:pointer',
27876                     cn : [
27877                         {
27878                             tag : 'div',
27879                             cls : 'roo-upload-cropbox-preview'
27880                         },
27881                         {
27882                             tag : 'div',
27883                             cls : 'roo-upload-cropbox-thumb'
27884                         },
27885                         {
27886                             tag : 'div',
27887                             cls : 'roo-upload-cropbox-empty-notify',
27888                             html : this.emptyText
27889                         },
27890                         {
27891                             tag : 'div',
27892                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27893                             html : this.rotateNotify
27894                         }
27895                     ]
27896                 },
27897                 {
27898                     tag : 'div',
27899                     cls : 'roo-upload-cropbox-footer',
27900                     cn : {
27901                         tag : 'div',
27902                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27903                         cn : []
27904                     }
27905                 }
27906             ]
27907         };
27908         
27909         return cfg;
27910     },
27911     
27912     onRender : function(ct, position)
27913     {
27914         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27915         
27916         if (this.buttons.length) {
27917             
27918             Roo.each(this.buttons, function(bb) {
27919                 
27920                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27921                 
27922                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27923                 
27924             }, this);
27925         }
27926         
27927         if(this.loadMask){
27928             this.maskEl = this.el;
27929         }
27930     },
27931     
27932     initEvents : function()
27933     {
27934         this.urlAPI = (window.createObjectURL && window) || 
27935                                 (window.URL && URL.revokeObjectURL && URL) || 
27936                                 (window.webkitURL && webkitURL);
27937                         
27938         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27939         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27940         
27941         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27942         this.selectorEl.hide();
27943         
27944         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27945         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27946         
27947         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27948         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27949         this.thumbEl.hide();
27950         
27951         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27952         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27953         
27954         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27955         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27956         this.errorEl.hide();
27957         
27958         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27959         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27960         this.footerEl.hide();
27961         
27962         this.setThumbBoxSize();
27963         
27964         this.bind();
27965         
27966         this.resize();
27967         
27968         this.fireEvent('initial', this);
27969     },
27970
27971     bind : function()
27972     {
27973         var _this = this;
27974         
27975         window.addEventListener("resize", function() { _this.resize(); } );
27976         
27977         this.bodyEl.on('click', this.beforeSelectFile, this);
27978         
27979         if(Roo.isTouch){
27980             this.bodyEl.on('touchstart', this.onTouchStart, this);
27981             this.bodyEl.on('touchmove', this.onTouchMove, this);
27982             this.bodyEl.on('touchend', this.onTouchEnd, this);
27983         }
27984         
27985         if(!Roo.isTouch){
27986             this.bodyEl.on('mousedown', this.onMouseDown, this);
27987             this.bodyEl.on('mousemove', this.onMouseMove, this);
27988             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27989             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27990             Roo.get(document).on('mouseup', this.onMouseUp, this);
27991         }
27992         
27993         this.selectorEl.on('change', this.onFileSelected, this);
27994     },
27995     
27996     reset : function()
27997     {    
27998         this.scale = 0;
27999         this.baseScale = 1;
28000         this.rotate = 0;
28001         this.baseRotate = 1;
28002         this.dragable = false;
28003         this.pinching = false;
28004         this.mouseX = 0;
28005         this.mouseY = 0;
28006         this.cropData = false;
28007         this.notifyEl.dom.innerHTML = this.emptyText;
28008         
28009         this.selectorEl.dom.value = '';
28010         
28011     },
28012     
28013     resize : function()
28014     {
28015         if(this.fireEvent('resize', this) != false){
28016             this.setThumbBoxPosition();
28017             this.setCanvasPosition();
28018         }
28019     },
28020     
28021     onFooterButtonClick : function(e, el, o, type)
28022     {
28023         switch (type) {
28024             case 'rotate-left' :
28025                 this.onRotateLeft(e);
28026                 break;
28027             case 'rotate-right' :
28028                 this.onRotateRight(e);
28029                 break;
28030             case 'picture' :
28031                 this.beforeSelectFile(e);
28032                 break;
28033             case 'trash' :
28034                 this.trash(e);
28035                 break;
28036             case 'crop' :
28037                 this.crop(e);
28038                 break;
28039             case 'download' :
28040                 this.download(e);
28041                 break;
28042             default :
28043                 break;
28044         }
28045         
28046         this.fireEvent('footerbuttonclick', this, type);
28047     },
28048     
28049     beforeSelectFile : function(e)
28050     {
28051         e.preventDefault();
28052         
28053         if(this.fireEvent('beforeselectfile', this) != false){
28054             this.selectorEl.dom.click();
28055         }
28056     },
28057     
28058     onFileSelected : function(e)
28059     {
28060         e.preventDefault();
28061         
28062         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28063             return;
28064         }
28065         
28066         var file = this.selectorEl.dom.files[0];
28067         
28068         if(this.fireEvent('inspect', this, file) != false){
28069             this.prepare(file);
28070         }
28071         
28072     },
28073     
28074     trash : function(e)
28075     {
28076         this.fireEvent('trash', this);
28077     },
28078     
28079     download : function(e)
28080     {
28081         this.fireEvent('download', this);
28082     },
28083     
28084     loadCanvas : function(src)
28085     {   
28086         if(this.fireEvent('beforeloadcanvas', this, src) != false){
28087             
28088             this.reset();
28089             
28090             this.imageEl = document.createElement('img');
28091             
28092             var _this = this;
28093             
28094             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
28095             
28096             this.imageEl.src = src;
28097         }
28098     },
28099     
28100     onLoadCanvas : function()
28101     {   
28102         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
28103         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
28104         
28105         this.bodyEl.un('click', this.beforeSelectFile, this);
28106         
28107         this.notifyEl.hide();
28108         this.thumbEl.show();
28109         this.footerEl.show();
28110         
28111         this.baseRotateLevel();
28112         
28113         if(this.isDocument){
28114             this.setThumbBoxSize();
28115         }
28116         
28117         this.setThumbBoxPosition();
28118         
28119         this.baseScaleLevel();
28120         
28121         this.draw();
28122         
28123         this.resize();
28124         
28125         this.canvasLoaded = true;
28126         
28127         if(this.loadMask){
28128             this.maskEl.unmask();
28129         }
28130         
28131     },
28132     
28133     setCanvasPosition : function()
28134     {   
28135         if(!this.canvasEl){
28136             return;
28137         }
28138         
28139         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
28140         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
28141         
28142         this.previewEl.setLeft(pw);
28143         this.previewEl.setTop(ph);
28144         
28145     },
28146     
28147     onMouseDown : function(e)
28148     {   
28149         e.stopEvent();
28150         
28151         this.dragable = true;
28152         this.pinching = false;
28153         
28154         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
28155             this.dragable = false;
28156             return;
28157         }
28158         
28159         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28160         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28161         
28162     },
28163     
28164     onMouseMove : function(e)
28165     {   
28166         e.stopEvent();
28167         
28168         if(!this.canvasLoaded){
28169             return;
28170         }
28171         
28172         if (!this.dragable){
28173             return;
28174         }
28175         
28176         var minX = Math.ceil(this.thumbEl.getLeft(true));
28177         var minY = Math.ceil(this.thumbEl.getTop(true));
28178         
28179         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
28180         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
28181         
28182         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28183         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28184         
28185         x = x - this.mouseX;
28186         y = y - this.mouseY;
28187         
28188         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
28189         var bgY = Math.ceil(y + this.previewEl.getTop(true));
28190         
28191         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
28192         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
28193         
28194         this.previewEl.setLeft(bgX);
28195         this.previewEl.setTop(bgY);
28196         
28197         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28198         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28199     },
28200     
28201     onMouseUp : function(e)
28202     {   
28203         e.stopEvent();
28204         
28205         this.dragable = false;
28206     },
28207     
28208     onMouseWheel : function(e)
28209     {   
28210         e.stopEvent();
28211         
28212         this.startScale = this.scale;
28213         
28214         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
28215         
28216         if(!this.zoomable()){
28217             this.scale = this.startScale;
28218             return;
28219         }
28220         
28221         this.draw();
28222         
28223         return;
28224     },
28225     
28226     zoomable : function()
28227     {
28228         var minScale = this.thumbEl.getWidth() / this.minWidth;
28229         
28230         if(this.minWidth < this.minHeight){
28231             minScale = this.thumbEl.getHeight() / this.minHeight;
28232         }
28233         
28234         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
28235         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
28236         
28237         if(
28238                 this.isDocument &&
28239                 (this.rotate == 0 || this.rotate == 180) && 
28240                 (
28241                     width > this.imageEl.OriginWidth || 
28242                     height > this.imageEl.OriginHeight ||
28243                     (width < this.minWidth && height < this.minHeight)
28244                 )
28245         ){
28246             return false;
28247         }
28248         
28249         if(
28250                 this.isDocument &&
28251                 (this.rotate == 90 || this.rotate == 270) && 
28252                 (
28253                     width > this.imageEl.OriginWidth || 
28254                     height > this.imageEl.OriginHeight ||
28255                     (width < this.minHeight && height < this.minWidth)
28256                 )
28257         ){
28258             return false;
28259         }
28260         
28261         if(
28262                 !this.isDocument &&
28263                 (this.rotate == 0 || this.rotate == 180) && 
28264                 (
28265                     width < this.minWidth || 
28266                     width > this.imageEl.OriginWidth || 
28267                     height < this.minHeight || 
28268                     height > this.imageEl.OriginHeight
28269                 )
28270         ){
28271             return false;
28272         }
28273         
28274         if(
28275                 !this.isDocument &&
28276                 (this.rotate == 90 || this.rotate == 270) && 
28277                 (
28278                     width < this.minHeight || 
28279                     width > this.imageEl.OriginWidth || 
28280                     height < this.minWidth || 
28281                     height > this.imageEl.OriginHeight
28282                 )
28283         ){
28284             return false;
28285         }
28286         
28287         return true;
28288         
28289     },
28290     
28291     onRotateLeft : function(e)
28292     {   
28293         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28294             
28295             var minScale = this.thumbEl.getWidth() / this.minWidth;
28296             
28297             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28298             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28299             
28300             this.startScale = this.scale;
28301             
28302             while (this.getScaleLevel() < minScale){
28303             
28304                 this.scale = this.scale + 1;
28305                 
28306                 if(!this.zoomable()){
28307                     break;
28308                 }
28309                 
28310                 if(
28311                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28312                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28313                 ){
28314                     continue;
28315                 }
28316                 
28317                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28318
28319                 this.draw();
28320                 
28321                 return;
28322             }
28323             
28324             this.scale = this.startScale;
28325             
28326             this.onRotateFail();
28327             
28328             return false;
28329         }
28330         
28331         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28332
28333         if(this.isDocument){
28334             this.setThumbBoxSize();
28335             this.setThumbBoxPosition();
28336             this.setCanvasPosition();
28337         }
28338         
28339         this.draw();
28340         
28341         this.fireEvent('rotate', this, 'left');
28342         
28343     },
28344     
28345     onRotateRight : function(e)
28346     {
28347         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28348             
28349             var minScale = this.thumbEl.getWidth() / this.minWidth;
28350         
28351             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28352             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28353             
28354             this.startScale = this.scale;
28355             
28356             while (this.getScaleLevel() < minScale){
28357             
28358                 this.scale = this.scale + 1;
28359                 
28360                 if(!this.zoomable()){
28361                     break;
28362                 }
28363                 
28364                 if(
28365                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28366                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28367                 ){
28368                     continue;
28369                 }
28370                 
28371                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28372
28373                 this.draw();
28374                 
28375                 return;
28376             }
28377             
28378             this.scale = this.startScale;
28379             
28380             this.onRotateFail();
28381             
28382             return false;
28383         }
28384         
28385         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28386
28387         if(this.isDocument){
28388             this.setThumbBoxSize();
28389             this.setThumbBoxPosition();
28390             this.setCanvasPosition();
28391         }
28392         
28393         this.draw();
28394         
28395         this.fireEvent('rotate', this, 'right');
28396     },
28397     
28398     onRotateFail : function()
28399     {
28400         this.errorEl.show(true);
28401         
28402         var _this = this;
28403         
28404         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28405     },
28406     
28407     draw : function()
28408     {
28409         this.previewEl.dom.innerHTML = '';
28410         
28411         var canvasEl = document.createElement("canvas");
28412         
28413         var contextEl = canvasEl.getContext("2d");
28414         
28415         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28416         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28417         var center = this.imageEl.OriginWidth / 2;
28418         
28419         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28420             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28421             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28422             center = this.imageEl.OriginHeight / 2;
28423         }
28424         
28425         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28426         
28427         contextEl.translate(center, center);
28428         contextEl.rotate(this.rotate * Math.PI / 180);
28429
28430         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28431         
28432         this.canvasEl = document.createElement("canvas");
28433         
28434         this.contextEl = this.canvasEl.getContext("2d");
28435         
28436         switch (this.rotate) {
28437             case 0 :
28438                 
28439                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28440                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28441                 
28442                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28443                 
28444                 break;
28445             case 90 : 
28446                 
28447                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28448                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28449                 
28450                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28451                     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);
28452                     break;
28453                 }
28454                 
28455                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28456                 
28457                 break;
28458             case 180 :
28459                 
28460                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28461                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28462                 
28463                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28464                     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);
28465                     break;
28466                 }
28467                 
28468                 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);
28469                 
28470                 break;
28471             case 270 :
28472                 
28473                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28474                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28475         
28476                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28477                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28478                     break;
28479                 }
28480                 
28481                 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);
28482                 
28483                 break;
28484             default : 
28485                 break;
28486         }
28487         
28488         this.previewEl.appendChild(this.canvasEl);
28489         
28490         this.setCanvasPosition();
28491     },
28492     
28493     crop : function()
28494     {
28495         if(!this.canvasLoaded){
28496             return;
28497         }
28498         
28499         var imageCanvas = document.createElement("canvas");
28500         
28501         var imageContext = imageCanvas.getContext("2d");
28502         
28503         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28504         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28505         
28506         var center = imageCanvas.width / 2;
28507         
28508         imageContext.translate(center, center);
28509         
28510         imageContext.rotate(this.rotate * Math.PI / 180);
28511         
28512         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28513         
28514         var canvas = document.createElement("canvas");
28515         
28516         var context = canvas.getContext("2d");
28517                 
28518         canvas.width = this.minWidth;
28519         canvas.height = this.minHeight;
28520
28521         switch (this.rotate) {
28522             case 0 :
28523                 
28524                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28525                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28526                 
28527                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28528                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28529                 
28530                 var targetWidth = this.minWidth - 2 * x;
28531                 var targetHeight = this.minHeight - 2 * y;
28532                 
28533                 var scale = 1;
28534                 
28535                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28536                     scale = targetWidth / width;
28537                 }
28538                 
28539                 if(x > 0 && y == 0){
28540                     scale = targetHeight / height;
28541                 }
28542                 
28543                 if(x > 0 && y > 0){
28544                     scale = targetWidth / width;
28545                     
28546                     if(width < height){
28547                         scale = targetHeight / height;
28548                     }
28549                 }
28550                 
28551                 context.scale(scale, scale);
28552                 
28553                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28554                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28555
28556                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28557                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28558
28559                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28560                 
28561                 break;
28562             case 90 : 
28563                 
28564                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28565                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28566                 
28567                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28568                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28569                 
28570                 var targetWidth = this.minWidth - 2 * x;
28571                 var targetHeight = this.minHeight - 2 * y;
28572                 
28573                 var scale = 1;
28574                 
28575                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28576                     scale = targetWidth / width;
28577                 }
28578                 
28579                 if(x > 0 && y == 0){
28580                     scale = targetHeight / height;
28581                 }
28582                 
28583                 if(x > 0 && y > 0){
28584                     scale = targetWidth / width;
28585                     
28586                     if(width < height){
28587                         scale = targetHeight / height;
28588                     }
28589                 }
28590                 
28591                 context.scale(scale, scale);
28592                 
28593                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28594                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28595
28596                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28597                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28598                 
28599                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28600                 
28601                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28602                 
28603                 break;
28604             case 180 :
28605                 
28606                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28607                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28608                 
28609                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28610                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28611                 
28612                 var targetWidth = this.minWidth - 2 * x;
28613                 var targetHeight = this.minHeight - 2 * y;
28614                 
28615                 var scale = 1;
28616                 
28617                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28618                     scale = targetWidth / width;
28619                 }
28620                 
28621                 if(x > 0 && y == 0){
28622                     scale = targetHeight / height;
28623                 }
28624                 
28625                 if(x > 0 && y > 0){
28626                     scale = targetWidth / width;
28627                     
28628                     if(width < height){
28629                         scale = targetHeight / height;
28630                     }
28631                 }
28632                 
28633                 context.scale(scale, scale);
28634                 
28635                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28636                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28637
28638                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28639                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28640
28641                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28642                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28643                 
28644                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28645                 
28646                 break;
28647             case 270 :
28648                 
28649                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28650                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28651                 
28652                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28653                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28654                 
28655                 var targetWidth = this.minWidth - 2 * x;
28656                 var targetHeight = this.minHeight - 2 * y;
28657                 
28658                 var scale = 1;
28659                 
28660                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28661                     scale = targetWidth / width;
28662                 }
28663                 
28664                 if(x > 0 && y == 0){
28665                     scale = targetHeight / height;
28666                 }
28667                 
28668                 if(x > 0 && y > 0){
28669                     scale = targetWidth / width;
28670                     
28671                     if(width < height){
28672                         scale = targetHeight / height;
28673                     }
28674                 }
28675                 
28676                 context.scale(scale, scale);
28677                 
28678                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28679                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28680
28681                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28682                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28683                 
28684                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28685                 
28686                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28687                 
28688                 break;
28689             default : 
28690                 break;
28691         }
28692         
28693         this.cropData = canvas.toDataURL(this.cropType);
28694         
28695         if(this.fireEvent('crop', this, this.cropData) !== false){
28696             this.process(this.file, this.cropData);
28697         }
28698         
28699         return;
28700         
28701     },
28702     
28703     setThumbBoxSize : function()
28704     {
28705         var width, height;
28706         
28707         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28708             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28709             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28710             
28711             this.minWidth = width;
28712             this.minHeight = height;
28713             
28714             if(this.rotate == 90 || this.rotate == 270){
28715                 this.minWidth = height;
28716                 this.minHeight = width;
28717             }
28718         }
28719         
28720         height = 300;
28721         width = Math.ceil(this.minWidth * height / this.minHeight);
28722         
28723         if(this.minWidth > this.minHeight){
28724             width = 300;
28725             height = Math.ceil(this.minHeight * width / this.minWidth);
28726         }
28727         
28728         this.thumbEl.setStyle({
28729             width : width + 'px',
28730             height : height + 'px'
28731         });
28732
28733         return;
28734             
28735     },
28736     
28737     setThumbBoxPosition : function()
28738     {
28739         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28740         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28741         
28742         this.thumbEl.setLeft(x);
28743         this.thumbEl.setTop(y);
28744         
28745     },
28746     
28747     baseRotateLevel : function()
28748     {
28749         this.baseRotate = 1;
28750         
28751         if(
28752                 typeof(this.exif) != 'undefined' &&
28753                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28754                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28755         ){
28756             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28757         }
28758         
28759         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28760         
28761     },
28762     
28763     baseScaleLevel : function()
28764     {
28765         var width, height;
28766         
28767         if(this.isDocument){
28768             
28769             if(this.baseRotate == 6 || this.baseRotate == 8){
28770             
28771                 height = this.thumbEl.getHeight();
28772                 this.baseScale = height / this.imageEl.OriginWidth;
28773
28774                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28775                     width = this.thumbEl.getWidth();
28776                     this.baseScale = width / this.imageEl.OriginHeight;
28777                 }
28778
28779                 return;
28780             }
28781
28782             height = this.thumbEl.getHeight();
28783             this.baseScale = height / this.imageEl.OriginHeight;
28784
28785             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28786                 width = this.thumbEl.getWidth();
28787                 this.baseScale = width / this.imageEl.OriginWidth;
28788             }
28789
28790             return;
28791         }
28792         
28793         if(this.baseRotate == 6 || this.baseRotate == 8){
28794             
28795             width = this.thumbEl.getHeight();
28796             this.baseScale = width / this.imageEl.OriginHeight;
28797             
28798             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28799                 height = this.thumbEl.getWidth();
28800                 this.baseScale = height / this.imageEl.OriginHeight;
28801             }
28802             
28803             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28804                 height = this.thumbEl.getWidth();
28805                 this.baseScale = height / this.imageEl.OriginHeight;
28806                 
28807                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28808                     width = this.thumbEl.getHeight();
28809                     this.baseScale = width / this.imageEl.OriginWidth;
28810                 }
28811             }
28812             
28813             return;
28814         }
28815         
28816         width = this.thumbEl.getWidth();
28817         this.baseScale = width / this.imageEl.OriginWidth;
28818         
28819         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28820             height = this.thumbEl.getHeight();
28821             this.baseScale = height / this.imageEl.OriginHeight;
28822         }
28823         
28824         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28825             
28826             height = this.thumbEl.getHeight();
28827             this.baseScale = height / this.imageEl.OriginHeight;
28828             
28829             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28830                 width = this.thumbEl.getWidth();
28831                 this.baseScale = width / this.imageEl.OriginWidth;
28832             }
28833             
28834         }
28835         
28836         return;
28837     },
28838     
28839     getScaleLevel : function()
28840     {
28841         return this.baseScale * Math.pow(1.1, this.scale);
28842     },
28843     
28844     onTouchStart : function(e)
28845     {
28846         if(!this.canvasLoaded){
28847             this.beforeSelectFile(e);
28848             return;
28849         }
28850         
28851         var touches = e.browserEvent.touches;
28852         
28853         if(!touches){
28854             return;
28855         }
28856         
28857         if(touches.length == 1){
28858             this.onMouseDown(e);
28859             return;
28860         }
28861         
28862         if(touches.length != 2){
28863             return;
28864         }
28865         
28866         var coords = [];
28867         
28868         for(var i = 0, finger; finger = touches[i]; i++){
28869             coords.push(finger.pageX, finger.pageY);
28870         }
28871         
28872         var x = Math.pow(coords[0] - coords[2], 2);
28873         var y = Math.pow(coords[1] - coords[3], 2);
28874         
28875         this.startDistance = Math.sqrt(x + y);
28876         
28877         this.startScale = this.scale;
28878         
28879         this.pinching = true;
28880         this.dragable = false;
28881         
28882     },
28883     
28884     onTouchMove : function(e)
28885     {
28886         if(!this.pinching && !this.dragable){
28887             return;
28888         }
28889         
28890         var touches = e.browserEvent.touches;
28891         
28892         if(!touches){
28893             return;
28894         }
28895         
28896         if(this.dragable){
28897             this.onMouseMove(e);
28898             return;
28899         }
28900         
28901         var coords = [];
28902         
28903         for(var i = 0, finger; finger = touches[i]; i++){
28904             coords.push(finger.pageX, finger.pageY);
28905         }
28906         
28907         var x = Math.pow(coords[0] - coords[2], 2);
28908         var y = Math.pow(coords[1] - coords[3], 2);
28909         
28910         this.endDistance = Math.sqrt(x + y);
28911         
28912         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28913         
28914         if(!this.zoomable()){
28915             this.scale = this.startScale;
28916             return;
28917         }
28918         
28919         this.draw();
28920         
28921     },
28922     
28923     onTouchEnd : function(e)
28924     {
28925         this.pinching = false;
28926         this.dragable = false;
28927         
28928     },
28929     
28930     process : function(file, crop)
28931     {
28932         if(this.loadMask){
28933             this.maskEl.mask(this.loadingText);
28934         }
28935         
28936         this.xhr = new XMLHttpRequest();
28937         
28938         file.xhr = this.xhr;
28939
28940         this.xhr.open(this.method, this.url, true);
28941         
28942         var headers = {
28943             "Accept": "application/json",
28944             "Cache-Control": "no-cache",
28945             "X-Requested-With": "XMLHttpRequest"
28946         };
28947         
28948         for (var headerName in headers) {
28949             var headerValue = headers[headerName];
28950             if (headerValue) {
28951                 this.xhr.setRequestHeader(headerName, headerValue);
28952             }
28953         }
28954         
28955         var _this = this;
28956         
28957         this.xhr.onload = function()
28958         {
28959             _this.xhrOnLoad(_this.xhr);
28960         }
28961         
28962         this.xhr.onerror = function()
28963         {
28964             _this.xhrOnError(_this.xhr);
28965         }
28966         
28967         var formData = new FormData();
28968
28969         formData.append('returnHTML', 'NO');
28970         
28971         if(crop){
28972             formData.append('crop', crop);
28973         }
28974         
28975         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28976             formData.append(this.paramName, file, file.name);
28977         }
28978         
28979         if(typeof(file.filename) != 'undefined'){
28980             formData.append('filename', file.filename);
28981         }
28982         
28983         if(typeof(file.mimetype) != 'undefined'){
28984             formData.append('mimetype', file.mimetype);
28985         }
28986         
28987         if(this.fireEvent('arrange', this, formData) != false){
28988             this.xhr.send(formData);
28989         };
28990     },
28991     
28992     xhrOnLoad : function(xhr)
28993     {
28994         if(this.loadMask){
28995             this.maskEl.unmask();
28996         }
28997         
28998         if (xhr.readyState !== 4) {
28999             this.fireEvent('exception', this, xhr);
29000             return;
29001         }
29002
29003         var response = Roo.decode(xhr.responseText);
29004         
29005         if(!response.success){
29006             this.fireEvent('exception', this, xhr);
29007             return;
29008         }
29009         
29010         var response = Roo.decode(xhr.responseText);
29011         
29012         this.fireEvent('upload', this, response);
29013         
29014     },
29015     
29016     xhrOnError : function()
29017     {
29018         if(this.loadMask){
29019             this.maskEl.unmask();
29020         }
29021         
29022         Roo.log('xhr on error');
29023         
29024         var response = Roo.decode(xhr.responseText);
29025           
29026         Roo.log(response);
29027         
29028     },
29029     
29030     prepare : function(file)
29031     {   
29032         if(this.loadMask){
29033             this.maskEl.mask(this.loadingText);
29034         }
29035         
29036         this.file = false;
29037         this.exif = {};
29038         
29039         if(typeof(file) === 'string'){
29040             this.loadCanvas(file);
29041             return;
29042         }
29043         
29044         if(!file || !this.urlAPI){
29045             return;
29046         }
29047         
29048         this.file = file;
29049         this.cropType = file.type;
29050         
29051         var _this = this;
29052         
29053         if(this.fireEvent('prepare', this, this.file) != false){
29054             
29055             var reader = new FileReader();
29056             
29057             reader.onload = function (e) {
29058                 if (e.target.error) {
29059                     Roo.log(e.target.error);
29060                     return;
29061                 }
29062                 
29063                 var buffer = e.target.result,
29064                     dataView = new DataView(buffer),
29065                     offset = 2,
29066                     maxOffset = dataView.byteLength - 4,
29067                     markerBytes,
29068                     markerLength;
29069                 
29070                 if (dataView.getUint16(0) === 0xffd8) {
29071                     while (offset < maxOffset) {
29072                         markerBytes = dataView.getUint16(offset);
29073                         
29074                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
29075                             markerLength = dataView.getUint16(offset + 2) + 2;
29076                             if (offset + markerLength > dataView.byteLength) {
29077                                 Roo.log('Invalid meta data: Invalid segment size.');
29078                                 break;
29079                             }
29080                             
29081                             if(markerBytes == 0xffe1){
29082                                 _this.parseExifData(
29083                                     dataView,
29084                                     offset,
29085                                     markerLength
29086                                 );
29087                             }
29088                             
29089                             offset += markerLength;
29090                             
29091                             continue;
29092                         }
29093                         
29094                         break;
29095                     }
29096                     
29097                 }
29098                 
29099                 var url = _this.urlAPI.createObjectURL(_this.file);
29100                 
29101                 _this.loadCanvas(url);
29102                 
29103                 return;
29104             }
29105             
29106             reader.readAsArrayBuffer(this.file);
29107             
29108         }
29109         
29110     },
29111     
29112     parseExifData : function(dataView, offset, length)
29113     {
29114         var tiffOffset = offset + 10,
29115             littleEndian,
29116             dirOffset;
29117     
29118         if (dataView.getUint32(offset + 4) !== 0x45786966) {
29119             // No Exif data, might be XMP data instead
29120             return;
29121         }
29122         
29123         // Check for the ASCII code for "Exif" (0x45786966):
29124         if (dataView.getUint32(offset + 4) !== 0x45786966) {
29125             // No Exif data, might be XMP data instead
29126             return;
29127         }
29128         if (tiffOffset + 8 > dataView.byteLength) {
29129             Roo.log('Invalid Exif data: Invalid segment size.');
29130             return;
29131         }
29132         // Check for the two null bytes:
29133         if (dataView.getUint16(offset + 8) !== 0x0000) {
29134             Roo.log('Invalid Exif data: Missing byte alignment offset.');
29135             return;
29136         }
29137         // Check the byte alignment:
29138         switch (dataView.getUint16(tiffOffset)) {
29139         case 0x4949:
29140             littleEndian = true;
29141             break;
29142         case 0x4D4D:
29143             littleEndian = false;
29144             break;
29145         default:
29146             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
29147             return;
29148         }
29149         // Check for the TIFF tag marker (0x002A):
29150         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
29151             Roo.log('Invalid Exif data: Missing TIFF marker.');
29152             return;
29153         }
29154         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
29155         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
29156         
29157         this.parseExifTags(
29158             dataView,
29159             tiffOffset,
29160             tiffOffset + dirOffset,
29161             littleEndian
29162         );
29163     },
29164     
29165     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
29166     {
29167         var tagsNumber,
29168             dirEndOffset,
29169             i;
29170         if (dirOffset + 6 > dataView.byteLength) {
29171             Roo.log('Invalid Exif data: Invalid directory offset.');
29172             return;
29173         }
29174         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
29175         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
29176         if (dirEndOffset + 4 > dataView.byteLength) {
29177             Roo.log('Invalid Exif data: Invalid directory size.');
29178             return;
29179         }
29180         for (i = 0; i < tagsNumber; i += 1) {
29181             this.parseExifTag(
29182                 dataView,
29183                 tiffOffset,
29184                 dirOffset + 2 + 12 * i, // tag offset
29185                 littleEndian
29186             );
29187         }
29188         // Return the offset to the next directory:
29189         return dataView.getUint32(dirEndOffset, littleEndian);
29190     },
29191     
29192     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
29193     {
29194         var tag = dataView.getUint16(offset, littleEndian);
29195         
29196         this.exif[tag] = this.getExifValue(
29197             dataView,
29198             tiffOffset,
29199             offset,
29200             dataView.getUint16(offset + 2, littleEndian), // tag type
29201             dataView.getUint32(offset + 4, littleEndian), // tag length
29202             littleEndian
29203         );
29204     },
29205     
29206     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
29207     {
29208         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
29209             tagSize,
29210             dataOffset,
29211             values,
29212             i,
29213             str,
29214             c;
29215     
29216         if (!tagType) {
29217             Roo.log('Invalid Exif data: Invalid tag type.');
29218             return;
29219         }
29220         
29221         tagSize = tagType.size * length;
29222         // Determine if the value is contained in the dataOffset bytes,
29223         // or if the value at the dataOffset is a pointer to the actual data:
29224         dataOffset = tagSize > 4 ?
29225                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
29226         if (dataOffset + tagSize > dataView.byteLength) {
29227             Roo.log('Invalid Exif data: Invalid data offset.');
29228             return;
29229         }
29230         if (length === 1) {
29231             return tagType.getValue(dataView, dataOffset, littleEndian);
29232         }
29233         values = [];
29234         for (i = 0; i < length; i += 1) {
29235             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
29236         }
29237         
29238         if (tagType.ascii) {
29239             str = '';
29240             // Concatenate the chars:
29241             for (i = 0; i < values.length; i += 1) {
29242                 c = values[i];
29243                 // Ignore the terminating NULL byte(s):
29244                 if (c === '\u0000') {
29245                     break;
29246                 }
29247                 str += c;
29248             }
29249             return str;
29250         }
29251         return values;
29252     }
29253     
29254 });
29255
29256 Roo.apply(Roo.bootstrap.UploadCropbox, {
29257     tags : {
29258         'Orientation': 0x0112
29259     },
29260     
29261     Orientation: {
29262             1: 0, //'top-left',
29263 //            2: 'top-right',
29264             3: 180, //'bottom-right',
29265 //            4: 'bottom-left',
29266 //            5: 'left-top',
29267             6: 90, //'right-top',
29268 //            7: 'right-bottom',
29269             8: 270 //'left-bottom'
29270     },
29271     
29272     exifTagTypes : {
29273         // byte, 8-bit unsigned int:
29274         1: {
29275             getValue: function (dataView, dataOffset) {
29276                 return dataView.getUint8(dataOffset);
29277             },
29278             size: 1
29279         },
29280         // ascii, 8-bit byte:
29281         2: {
29282             getValue: function (dataView, dataOffset) {
29283                 return String.fromCharCode(dataView.getUint8(dataOffset));
29284             },
29285             size: 1,
29286             ascii: true
29287         },
29288         // short, 16 bit int:
29289         3: {
29290             getValue: function (dataView, dataOffset, littleEndian) {
29291                 return dataView.getUint16(dataOffset, littleEndian);
29292             },
29293             size: 2
29294         },
29295         // long, 32 bit int:
29296         4: {
29297             getValue: function (dataView, dataOffset, littleEndian) {
29298                 return dataView.getUint32(dataOffset, littleEndian);
29299             },
29300             size: 4
29301         },
29302         // rational = two long values, first is numerator, second is denominator:
29303         5: {
29304             getValue: function (dataView, dataOffset, littleEndian) {
29305                 return dataView.getUint32(dataOffset, littleEndian) /
29306                     dataView.getUint32(dataOffset + 4, littleEndian);
29307             },
29308             size: 8
29309         },
29310         // slong, 32 bit signed int:
29311         9: {
29312             getValue: function (dataView, dataOffset, littleEndian) {
29313                 return dataView.getInt32(dataOffset, littleEndian);
29314             },
29315             size: 4
29316         },
29317         // srational, two slongs, first is numerator, second is denominator:
29318         10: {
29319             getValue: function (dataView, dataOffset, littleEndian) {
29320                 return dataView.getInt32(dataOffset, littleEndian) /
29321                     dataView.getInt32(dataOffset + 4, littleEndian);
29322             },
29323             size: 8
29324         }
29325     },
29326     
29327     footer : {
29328         STANDARD : [
29329             {
29330                 tag : 'div',
29331                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29332                 action : 'rotate-left',
29333                 cn : [
29334                     {
29335                         tag : 'button',
29336                         cls : 'btn btn-default',
29337                         html : '<i class="fa fa-undo"></i>'
29338                     }
29339                 ]
29340             },
29341             {
29342                 tag : 'div',
29343                 cls : 'btn-group roo-upload-cropbox-picture',
29344                 action : 'picture',
29345                 cn : [
29346                     {
29347                         tag : 'button',
29348                         cls : 'btn btn-default',
29349                         html : '<i class="fa fa-picture-o"></i>'
29350                     }
29351                 ]
29352             },
29353             {
29354                 tag : 'div',
29355                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29356                 action : 'rotate-right',
29357                 cn : [
29358                     {
29359                         tag : 'button',
29360                         cls : 'btn btn-default',
29361                         html : '<i class="fa fa-repeat"></i>'
29362                     }
29363                 ]
29364             }
29365         ],
29366         DOCUMENT : [
29367             {
29368                 tag : 'div',
29369                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29370                 action : 'rotate-left',
29371                 cn : [
29372                     {
29373                         tag : 'button',
29374                         cls : 'btn btn-default',
29375                         html : '<i class="fa fa-undo"></i>'
29376                     }
29377                 ]
29378             },
29379             {
29380                 tag : 'div',
29381                 cls : 'btn-group roo-upload-cropbox-download',
29382                 action : 'download',
29383                 cn : [
29384                     {
29385                         tag : 'button',
29386                         cls : 'btn btn-default',
29387                         html : '<i class="fa fa-download"></i>'
29388                     }
29389                 ]
29390             },
29391             {
29392                 tag : 'div',
29393                 cls : 'btn-group roo-upload-cropbox-crop',
29394                 action : 'crop',
29395                 cn : [
29396                     {
29397                         tag : 'button',
29398                         cls : 'btn btn-default',
29399                         html : '<i class="fa fa-crop"></i>'
29400                     }
29401                 ]
29402             },
29403             {
29404                 tag : 'div',
29405                 cls : 'btn-group roo-upload-cropbox-trash',
29406                 action : 'trash',
29407                 cn : [
29408                     {
29409                         tag : 'button',
29410                         cls : 'btn btn-default',
29411                         html : '<i class="fa fa-trash"></i>'
29412                     }
29413                 ]
29414             },
29415             {
29416                 tag : 'div',
29417                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29418                 action : 'rotate-right',
29419                 cn : [
29420                     {
29421                         tag : 'button',
29422                         cls : 'btn btn-default',
29423                         html : '<i class="fa fa-repeat"></i>'
29424                     }
29425                 ]
29426             }
29427         ],
29428         ROTATOR : [
29429             {
29430                 tag : 'div',
29431                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29432                 action : 'rotate-left',
29433                 cn : [
29434                     {
29435                         tag : 'button',
29436                         cls : 'btn btn-default',
29437                         html : '<i class="fa fa-undo"></i>'
29438                     }
29439                 ]
29440             },
29441             {
29442                 tag : 'div',
29443                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29444                 action : 'rotate-right',
29445                 cn : [
29446                     {
29447                         tag : 'button',
29448                         cls : 'btn btn-default',
29449                         html : '<i class="fa fa-repeat"></i>'
29450                     }
29451                 ]
29452             }
29453         ]
29454     }
29455 });
29456
29457 /*
29458 * Licence: LGPL
29459 */
29460
29461 /**
29462  * @class Roo.bootstrap.DocumentManager
29463  * @extends Roo.bootstrap.Component
29464  * Bootstrap DocumentManager class
29465  * @cfg {String} paramName default 'imageUpload'
29466  * @cfg {String} toolTipName default 'filename'
29467  * @cfg {String} method default POST
29468  * @cfg {String} url action url
29469  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29470  * @cfg {Boolean} multiple multiple upload default true
29471  * @cfg {Number} thumbSize default 300
29472  * @cfg {String} fieldLabel
29473  * @cfg {Number} labelWidth default 4
29474  * @cfg {String} labelAlign (left|top) default left
29475  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29476 * @cfg {Number} labellg set the width of label (1-12)
29477  * @cfg {Number} labelmd set the width of label (1-12)
29478  * @cfg {Number} labelsm set the width of label (1-12)
29479  * @cfg {Number} labelxs set the width of label (1-12)
29480  * 
29481  * @constructor
29482  * Create a new DocumentManager
29483  * @param {Object} config The config object
29484  */
29485
29486 Roo.bootstrap.DocumentManager = function(config){
29487     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29488     
29489     this.files = [];
29490     this.delegates = [];
29491     
29492     this.addEvents({
29493         /**
29494          * @event initial
29495          * Fire when initial the DocumentManager
29496          * @param {Roo.bootstrap.DocumentManager} this
29497          */
29498         "initial" : true,
29499         /**
29500          * @event inspect
29501          * inspect selected file
29502          * @param {Roo.bootstrap.DocumentManager} this
29503          * @param {File} file
29504          */
29505         "inspect" : true,
29506         /**
29507          * @event exception
29508          * Fire when xhr load exception
29509          * @param {Roo.bootstrap.DocumentManager} this
29510          * @param {XMLHttpRequest} xhr
29511          */
29512         "exception" : true,
29513         /**
29514          * @event afterupload
29515          * Fire when xhr load exception
29516          * @param {Roo.bootstrap.DocumentManager} this
29517          * @param {XMLHttpRequest} xhr
29518          */
29519         "afterupload" : true,
29520         /**
29521          * @event prepare
29522          * prepare the form data
29523          * @param {Roo.bootstrap.DocumentManager} this
29524          * @param {Object} formData
29525          */
29526         "prepare" : true,
29527         /**
29528          * @event remove
29529          * Fire when remove the file
29530          * @param {Roo.bootstrap.DocumentManager} this
29531          * @param {Object} file
29532          */
29533         "remove" : true,
29534         /**
29535          * @event refresh
29536          * Fire after refresh the file
29537          * @param {Roo.bootstrap.DocumentManager} this
29538          */
29539         "refresh" : true,
29540         /**
29541          * @event click
29542          * Fire after click the image
29543          * @param {Roo.bootstrap.DocumentManager} this
29544          * @param {Object} file
29545          */
29546         "click" : true,
29547         /**
29548          * @event edit
29549          * Fire when upload a image and editable set to true
29550          * @param {Roo.bootstrap.DocumentManager} this
29551          * @param {Object} file
29552          */
29553         "edit" : true,
29554         /**
29555          * @event beforeselectfile
29556          * Fire before select file
29557          * @param {Roo.bootstrap.DocumentManager} this
29558          */
29559         "beforeselectfile" : true,
29560         /**
29561          * @event process
29562          * Fire before process file
29563          * @param {Roo.bootstrap.DocumentManager} this
29564          * @param {Object} file
29565          */
29566         "process" : true,
29567         /**
29568          * @event previewrendered
29569          * Fire when preview rendered
29570          * @param {Roo.bootstrap.DocumentManager} this
29571          * @param {Object} file
29572          */
29573         "previewrendered" : true,
29574         /**
29575          */
29576         "previewResize" : true
29577         
29578     });
29579 };
29580
29581 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29582     
29583     boxes : 0,
29584     inputName : '',
29585     thumbSize : 300,
29586     multiple : true,
29587     files : false,
29588     method : 'POST',
29589     url : '',
29590     paramName : 'imageUpload',
29591     toolTipName : 'filename',
29592     fieldLabel : '',
29593     labelWidth : 4,
29594     labelAlign : 'left',
29595     editable : true,
29596     delegates : false,
29597     xhr : false, 
29598     
29599     labellg : 0,
29600     labelmd : 0,
29601     labelsm : 0,
29602     labelxs : 0,
29603     
29604     getAutoCreate : function()
29605     {   
29606         var managerWidget = {
29607             tag : 'div',
29608             cls : 'roo-document-manager',
29609             cn : [
29610                 {
29611                     tag : 'input',
29612                     cls : 'roo-document-manager-selector',
29613                     type : 'file'
29614                 },
29615                 {
29616                     tag : 'div',
29617                     cls : 'roo-document-manager-uploader',
29618                     cn : [
29619                         {
29620                             tag : 'div',
29621                             cls : 'roo-document-manager-upload-btn',
29622                             html : '<i class="fa fa-plus"></i>'
29623                         }
29624                     ]
29625                     
29626                 }
29627             ]
29628         };
29629         
29630         var content = [
29631             {
29632                 tag : 'div',
29633                 cls : 'column col-md-12',
29634                 cn : managerWidget
29635             }
29636         ];
29637         
29638         if(this.fieldLabel.length){
29639             
29640             content = [
29641                 {
29642                     tag : 'div',
29643                     cls : 'column col-md-12',
29644                     html : this.fieldLabel
29645                 },
29646                 {
29647                     tag : 'div',
29648                     cls : 'column col-md-12',
29649                     cn : managerWidget
29650                 }
29651             ];
29652
29653             if(this.labelAlign == 'left'){
29654                 content = [
29655                     {
29656                         tag : 'div',
29657                         cls : 'column',
29658                         html : this.fieldLabel
29659                     },
29660                     {
29661                         tag : 'div',
29662                         cls : 'column',
29663                         cn : managerWidget
29664                     }
29665                 ];
29666                 
29667                 if(this.labelWidth > 12){
29668                     content[0].style = "width: " + this.labelWidth + 'px';
29669                 }
29670
29671                 if(this.labelWidth < 13 && this.labelmd == 0){
29672                     this.labelmd = this.labelWidth;
29673                 }
29674
29675                 if(this.labellg > 0){
29676                     content[0].cls += ' col-lg-' + this.labellg;
29677                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29678                 }
29679
29680                 if(this.labelmd > 0){
29681                     content[0].cls += ' col-md-' + this.labelmd;
29682                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29683                 }
29684
29685                 if(this.labelsm > 0){
29686                     content[0].cls += ' col-sm-' + this.labelsm;
29687                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29688                 }
29689
29690                 if(this.labelxs > 0){
29691                     content[0].cls += ' col-xs-' + this.labelxs;
29692                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29693                 }
29694                 
29695             }
29696         }
29697         
29698         var cfg = {
29699             tag : 'div',
29700             cls : 'row clearfix',
29701             cn : content
29702         };
29703         
29704         return cfg;
29705         
29706     },
29707     
29708     initEvents : function()
29709     {
29710         this.managerEl = this.el.select('.roo-document-manager', true).first();
29711         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29712         
29713         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29714         this.selectorEl.hide();
29715         
29716         if(this.multiple){
29717             this.selectorEl.attr('multiple', 'multiple');
29718         }
29719         
29720         this.selectorEl.on('change', this.onFileSelected, this);
29721         
29722         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29723         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29724         
29725         this.uploader.on('click', this.onUploaderClick, this);
29726         
29727         this.renderProgressDialog();
29728         
29729         var _this = this;
29730         
29731         window.addEventListener("resize", function() { _this.refresh(); } );
29732         
29733         this.fireEvent('initial', this);
29734     },
29735     
29736     renderProgressDialog : function()
29737     {
29738         var _this = this;
29739         
29740         this.progressDialog = new Roo.bootstrap.Modal({
29741             cls : 'roo-document-manager-progress-dialog',
29742             allow_close : false,
29743             animate : false,
29744             title : '',
29745             buttons : [
29746                 {
29747                     name  :'cancel',
29748                     weight : 'danger',
29749                     html : 'Cancel'
29750                 }
29751             ], 
29752             listeners : { 
29753                 btnclick : function() {
29754                     _this.uploadCancel();
29755                     this.hide();
29756                 }
29757             }
29758         });
29759          
29760         this.progressDialog.render(Roo.get(document.body));
29761          
29762         this.progress = new Roo.bootstrap.Progress({
29763             cls : 'roo-document-manager-progress',
29764             active : true,
29765             striped : true
29766         });
29767         
29768         this.progress.render(this.progressDialog.getChildContainer());
29769         
29770         this.progressBar = new Roo.bootstrap.ProgressBar({
29771             cls : 'roo-document-manager-progress-bar',
29772             aria_valuenow : 0,
29773             aria_valuemin : 0,
29774             aria_valuemax : 12,
29775             panel : 'success'
29776         });
29777         
29778         this.progressBar.render(this.progress.getChildContainer());
29779     },
29780     
29781     onUploaderClick : function(e)
29782     {
29783         e.preventDefault();
29784      
29785         if(this.fireEvent('beforeselectfile', this) != false){
29786             this.selectorEl.dom.click();
29787         }
29788         
29789     },
29790     
29791     onFileSelected : function(e)
29792     {
29793         e.preventDefault();
29794         
29795         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29796             return;
29797         }
29798         
29799         Roo.each(this.selectorEl.dom.files, function(file){
29800             if(this.fireEvent('inspect', this, file) != false){
29801                 this.files.push(file);
29802             }
29803         }, this);
29804         
29805         this.queue();
29806         
29807     },
29808     
29809     queue : function()
29810     {
29811         this.selectorEl.dom.value = '';
29812         
29813         if(!this.files || !this.files.length){
29814             return;
29815         }
29816         
29817         if(this.boxes > 0 && this.files.length > this.boxes){
29818             this.files = this.files.slice(0, this.boxes);
29819         }
29820         
29821         this.uploader.show();
29822         
29823         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29824             this.uploader.hide();
29825         }
29826         
29827         var _this = this;
29828         
29829         var files = [];
29830         
29831         var docs = [];
29832         
29833         Roo.each(this.files, function(file){
29834             
29835             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29836                 var f = this.renderPreview(file);
29837                 files.push(f);
29838                 return;
29839             }
29840             
29841             if(file.type.indexOf('image') != -1){
29842                 this.delegates.push(
29843                     (function(){
29844                         _this.process(file);
29845                     }).createDelegate(this)
29846                 );
29847         
29848                 return;
29849             }
29850             
29851             docs.push(
29852                 (function(){
29853                     _this.process(file);
29854                 }).createDelegate(this)
29855             );
29856             
29857         }, this);
29858         
29859         this.files = files;
29860         
29861         this.delegates = this.delegates.concat(docs);
29862         
29863         if(!this.delegates.length){
29864             this.refresh();
29865             return;
29866         }
29867         
29868         this.progressBar.aria_valuemax = this.delegates.length;
29869         
29870         this.arrange();
29871         
29872         return;
29873     },
29874     
29875     arrange : function()
29876     {
29877         if(!this.delegates.length){
29878             this.progressDialog.hide();
29879             this.refresh();
29880             return;
29881         }
29882         
29883         var delegate = this.delegates.shift();
29884         
29885         this.progressDialog.show();
29886         
29887         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29888         
29889         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29890         
29891         delegate();
29892     },
29893     
29894     refresh : function()
29895     {
29896         this.uploader.show();
29897         
29898         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29899             this.uploader.hide();
29900         }
29901         
29902         Roo.isTouch ? this.closable(false) : this.closable(true);
29903         
29904         this.fireEvent('refresh', this);
29905     },
29906     
29907     onRemove : function(e, el, o)
29908     {
29909         e.preventDefault();
29910         
29911         this.fireEvent('remove', this, o);
29912         
29913     },
29914     
29915     remove : function(o)
29916     {
29917         var files = [];
29918         
29919         Roo.each(this.files, function(file){
29920             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29921                 files.push(file);
29922                 return;
29923             }
29924
29925             o.target.remove();
29926
29927         }, this);
29928         
29929         this.files = files;
29930         
29931         this.refresh();
29932     },
29933     
29934     clear : function()
29935     {
29936         Roo.each(this.files, function(file){
29937             if(!file.target){
29938                 return;
29939             }
29940             
29941             file.target.remove();
29942
29943         }, this);
29944         
29945         this.files = [];
29946         
29947         this.refresh();
29948     },
29949     
29950     onClick : function(e, el, o)
29951     {
29952         e.preventDefault();
29953         
29954         this.fireEvent('click', this, o);
29955         
29956     },
29957     
29958     closable : function(closable)
29959     {
29960         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29961             
29962             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29963             
29964             if(closable){
29965                 el.show();
29966                 return;
29967             }
29968             
29969             el.hide();
29970             
29971         }, this);
29972     },
29973     
29974     xhrOnLoad : function(xhr)
29975     {
29976         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29977             el.remove();
29978         }, this);
29979         
29980         if (xhr.readyState !== 4) {
29981             this.arrange();
29982             this.fireEvent('exception', this, xhr);
29983             return;
29984         }
29985
29986         var response = Roo.decode(xhr.responseText);
29987         
29988         if(!response.success){
29989             this.arrange();
29990             this.fireEvent('exception', this, xhr);
29991             return;
29992         }
29993         
29994         var file = this.renderPreview(response.data);
29995         
29996         this.files.push(file);
29997         
29998         this.arrange();
29999         
30000         this.fireEvent('afterupload', this, xhr);
30001         
30002     },
30003     
30004     xhrOnError : function(xhr)
30005     {
30006         Roo.log('xhr on error');
30007         
30008         var response = Roo.decode(xhr.responseText);
30009           
30010         Roo.log(response);
30011         
30012         this.arrange();
30013     },
30014     
30015     process : function(file)
30016     {
30017         if(this.fireEvent('process', this, file) !== false){
30018             if(this.editable && file.type.indexOf('image') != -1){
30019                 this.fireEvent('edit', this, file);
30020                 return;
30021             }
30022
30023             this.uploadStart(file, false);
30024
30025             return;
30026         }
30027         
30028     },
30029     
30030     uploadStart : function(file, crop)
30031     {
30032         this.xhr = new XMLHttpRequest();
30033         
30034         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
30035             this.arrange();
30036             return;
30037         }
30038         
30039         file.xhr = this.xhr;
30040             
30041         this.managerEl.createChild({
30042             tag : 'div',
30043             cls : 'roo-document-manager-loading',
30044             cn : [
30045                 {
30046                     tag : 'div',
30047                     tooltip : file.name,
30048                     cls : 'roo-document-manager-thumb',
30049                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30050                 }
30051             ]
30052
30053         });
30054
30055         this.xhr.open(this.method, this.url, true);
30056         
30057         var headers = {
30058             "Accept": "application/json",
30059             "Cache-Control": "no-cache",
30060             "X-Requested-With": "XMLHttpRequest"
30061         };
30062         
30063         for (var headerName in headers) {
30064             var headerValue = headers[headerName];
30065             if (headerValue) {
30066                 this.xhr.setRequestHeader(headerName, headerValue);
30067             }
30068         }
30069         
30070         var _this = this;
30071         
30072         this.xhr.onload = function()
30073         {
30074             _this.xhrOnLoad(_this.xhr);
30075         }
30076         
30077         this.xhr.onerror = function()
30078         {
30079             _this.xhrOnError(_this.xhr);
30080         }
30081         
30082         var formData = new FormData();
30083
30084         formData.append('returnHTML', 'NO');
30085         
30086         if(crop){
30087             formData.append('crop', crop);
30088         }
30089         
30090         formData.append(this.paramName, file, file.name);
30091         
30092         var options = {
30093             file : file, 
30094             manually : false
30095         };
30096         
30097         if(this.fireEvent('prepare', this, formData, options) != false){
30098             
30099             if(options.manually){
30100                 return;
30101             }
30102             
30103             this.xhr.send(formData);
30104             return;
30105         };
30106         
30107         this.uploadCancel();
30108     },
30109     
30110     uploadCancel : function()
30111     {
30112         if (this.xhr) {
30113             this.xhr.abort();
30114         }
30115         
30116         this.delegates = [];
30117         
30118         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30119             el.remove();
30120         }, this);
30121         
30122         this.arrange();
30123     },
30124     
30125     renderPreview : function(file)
30126     {
30127         if(typeof(file.target) != 'undefined' && file.target){
30128             return file;
30129         }
30130         
30131         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
30132         
30133         var previewEl = this.managerEl.createChild({
30134             tag : 'div',
30135             cls : 'roo-document-manager-preview',
30136             cn : [
30137                 {
30138                     tag : 'div',
30139                     tooltip : file[this.toolTipName],
30140                     cls : 'roo-document-manager-thumb',
30141                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
30142                 },
30143                 {
30144                     tag : 'button',
30145                     cls : 'close',
30146                     html : '<i class="fa fa-times-circle"></i>'
30147                 }
30148             ]
30149         });
30150
30151         var close = previewEl.select('button.close', true).first();
30152
30153         close.on('click', this.onRemove, this, file);
30154
30155         file.target = previewEl;
30156
30157         var image = previewEl.select('img', true).first();
30158         
30159         var _this = this;
30160         
30161         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
30162         
30163         image.on('click', this.onClick, this, file);
30164         
30165         this.fireEvent('previewrendered', this, file);
30166         
30167         return file;
30168         
30169     },
30170     
30171     onPreviewLoad : function(file, image)
30172     {
30173         if(typeof(file.target) == 'undefined' || !file.target){
30174             return;
30175         }
30176         
30177         var width = image.dom.naturalWidth || image.dom.width;
30178         var height = image.dom.naturalHeight || image.dom.height;
30179         
30180         if(!this.previewResize) {
30181             return;
30182         }
30183         
30184         if(width > height){
30185             file.target.addClass('wide');
30186             return;
30187         }
30188         
30189         file.target.addClass('tall');
30190         return;
30191         
30192     },
30193     
30194     uploadFromSource : function(file, crop)
30195     {
30196         this.xhr = new XMLHttpRequest();
30197         
30198         this.managerEl.createChild({
30199             tag : 'div',
30200             cls : 'roo-document-manager-loading',
30201             cn : [
30202                 {
30203                     tag : 'div',
30204                     tooltip : file.name,
30205                     cls : 'roo-document-manager-thumb',
30206                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30207                 }
30208             ]
30209
30210         });
30211
30212         this.xhr.open(this.method, this.url, true);
30213         
30214         var headers = {
30215             "Accept": "application/json",
30216             "Cache-Control": "no-cache",
30217             "X-Requested-With": "XMLHttpRequest"
30218         };
30219         
30220         for (var headerName in headers) {
30221             var headerValue = headers[headerName];
30222             if (headerValue) {
30223                 this.xhr.setRequestHeader(headerName, headerValue);
30224             }
30225         }
30226         
30227         var _this = this;
30228         
30229         this.xhr.onload = function()
30230         {
30231             _this.xhrOnLoad(_this.xhr);
30232         }
30233         
30234         this.xhr.onerror = function()
30235         {
30236             _this.xhrOnError(_this.xhr);
30237         }
30238         
30239         var formData = new FormData();
30240
30241         formData.append('returnHTML', 'NO');
30242         
30243         formData.append('crop', crop);
30244         
30245         if(typeof(file.filename) != 'undefined'){
30246             formData.append('filename', file.filename);
30247         }
30248         
30249         if(typeof(file.mimetype) != 'undefined'){
30250             formData.append('mimetype', file.mimetype);
30251         }
30252         
30253         Roo.log(formData);
30254         
30255         if(this.fireEvent('prepare', this, formData) != false){
30256             this.xhr.send(formData);
30257         };
30258     }
30259 });
30260
30261 /*
30262 * Licence: LGPL
30263 */
30264
30265 /**
30266  * @class Roo.bootstrap.DocumentViewer
30267  * @extends Roo.bootstrap.Component
30268  * Bootstrap DocumentViewer class
30269  * @cfg {Boolean} showDownload (true|false) show download button (default true)
30270  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
30271  * 
30272  * @constructor
30273  * Create a new DocumentViewer
30274  * @param {Object} config The config object
30275  */
30276
30277 Roo.bootstrap.DocumentViewer = function(config){
30278     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
30279     
30280     this.addEvents({
30281         /**
30282          * @event initial
30283          * Fire after initEvent
30284          * @param {Roo.bootstrap.DocumentViewer} this
30285          */
30286         "initial" : true,
30287         /**
30288          * @event click
30289          * Fire after click
30290          * @param {Roo.bootstrap.DocumentViewer} this
30291          */
30292         "click" : true,
30293         /**
30294          * @event download
30295          * Fire after download button
30296          * @param {Roo.bootstrap.DocumentViewer} this
30297          */
30298         "download" : true,
30299         /**
30300          * @event trash
30301          * Fire after trash button
30302          * @param {Roo.bootstrap.DocumentViewer} this
30303          */
30304         "trash" : true
30305         
30306     });
30307 };
30308
30309 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
30310     
30311     showDownload : true,
30312     
30313     showTrash : true,
30314     
30315     getAutoCreate : function()
30316     {
30317         var cfg = {
30318             tag : 'div',
30319             cls : 'roo-document-viewer',
30320             cn : [
30321                 {
30322                     tag : 'div',
30323                     cls : 'roo-document-viewer-body',
30324                     cn : [
30325                         {
30326                             tag : 'div',
30327                             cls : 'roo-document-viewer-thumb',
30328                             cn : [
30329                                 {
30330                                     tag : 'img',
30331                                     cls : 'roo-document-viewer-image'
30332                                 }
30333                             ]
30334                         }
30335                     ]
30336                 },
30337                 {
30338                     tag : 'div',
30339                     cls : 'roo-document-viewer-footer',
30340                     cn : {
30341                         tag : 'div',
30342                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
30343                         cn : [
30344                             {
30345                                 tag : 'div',
30346                                 cls : 'btn-group roo-document-viewer-download',
30347                                 cn : [
30348                                     {
30349                                         tag : 'button',
30350                                         cls : 'btn btn-default',
30351                                         html : '<i class="fa fa-download"></i>'
30352                                     }
30353                                 ]
30354                             },
30355                             {
30356                                 tag : 'div',
30357                                 cls : 'btn-group roo-document-viewer-trash',
30358                                 cn : [
30359                                     {
30360                                         tag : 'button',
30361                                         cls : 'btn btn-default',
30362                                         html : '<i class="fa fa-trash"></i>'
30363                                     }
30364                                 ]
30365                             }
30366                         ]
30367                     }
30368                 }
30369             ]
30370         };
30371         
30372         return cfg;
30373     },
30374     
30375     initEvents : function()
30376     {
30377         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30378         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30379         
30380         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30381         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30382         
30383         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30384         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30385         
30386         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30387         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30388         
30389         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30390         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30391         
30392         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30393         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30394         
30395         this.bodyEl.on('click', this.onClick, this);
30396         this.downloadBtn.on('click', this.onDownload, this);
30397         this.trashBtn.on('click', this.onTrash, this);
30398         
30399         this.downloadBtn.hide();
30400         this.trashBtn.hide();
30401         
30402         if(this.showDownload){
30403             this.downloadBtn.show();
30404         }
30405         
30406         if(this.showTrash){
30407             this.trashBtn.show();
30408         }
30409         
30410         if(!this.showDownload && !this.showTrash) {
30411             this.footerEl.hide();
30412         }
30413         
30414     },
30415     
30416     initial : function()
30417     {
30418         this.fireEvent('initial', this);
30419         
30420     },
30421     
30422     onClick : function(e)
30423     {
30424         e.preventDefault();
30425         
30426         this.fireEvent('click', this);
30427     },
30428     
30429     onDownload : function(e)
30430     {
30431         e.preventDefault();
30432         
30433         this.fireEvent('download', this);
30434     },
30435     
30436     onTrash : function(e)
30437     {
30438         e.preventDefault();
30439         
30440         this.fireEvent('trash', this);
30441     }
30442     
30443 });
30444 /*
30445  * - LGPL
30446  *
30447  * nav progress bar
30448  * 
30449  */
30450
30451 /**
30452  * @class Roo.bootstrap.NavProgressBar
30453  * @extends Roo.bootstrap.Component
30454  * Bootstrap NavProgressBar class
30455  * 
30456  * @constructor
30457  * Create a new nav progress bar
30458  * @param {Object} config The config object
30459  */
30460
30461 Roo.bootstrap.NavProgressBar = function(config){
30462     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30463
30464     this.bullets = this.bullets || [];
30465    
30466 //    Roo.bootstrap.NavProgressBar.register(this);
30467      this.addEvents({
30468         /**
30469              * @event changed
30470              * Fires when the active item changes
30471              * @param {Roo.bootstrap.NavProgressBar} this
30472              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30473              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
30474          */
30475         'changed': true
30476      });
30477     
30478 };
30479
30480 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
30481     
30482     bullets : [],
30483     barItems : [],
30484     
30485     getAutoCreate : function()
30486     {
30487         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30488         
30489         cfg = {
30490             tag : 'div',
30491             cls : 'roo-navigation-bar-group',
30492             cn : [
30493                 {
30494                     tag : 'div',
30495                     cls : 'roo-navigation-top-bar'
30496                 },
30497                 {
30498                     tag : 'div',
30499                     cls : 'roo-navigation-bullets-bar',
30500                     cn : [
30501                         {
30502                             tag : 'ul',
30503                             cls : 'roo-navigation-bar'
30504                         }
30505                     ]
30506                 },
30507                 
30508                 {
30509                     tag : 'div',
30510                     cls : 'roo-navigation-bottom-bar'
30511                 }
30512             ]
30513             
30514         };
30515         
30516         return cfg;
30517         
30518     },
30519     
30520     initEvents: function() 
30521     {
30522         
30523     },
30524     
30525     onRender : function(ct, position) 
30526     {
30527         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30528         
30529         if(this.bullets.length){
30530             Roo.each(this.bullets, function(b){
30531                this.addItem(b);
30532             }, this);
30533         }
30534         
30535         this.format();
30536         
30537     },
30538     
30539     addItem : function(cfg)
30540     {
30541         var item = new Roo.bootstrap.NavProgressItem(cfg);
30542         
30543         item.parentId = this.id;
30544         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30545         
30546         if(cfg.html){
30547             var top = new Roo.bootstrap.Element({
30548                 tag : 'div',
30549                 cls : 'roo-navigation-bar-text'
30550             });
30551             
30552             var bottom = new Roo.bootstrap.Element({
30553                 tag : 'div',
30554                 cls : 'roo-navigation-bar-text'
30555             });
30556             
30557             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30558             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30559             
30560             var topText = new Roo.bootstrap.Element({
30561                 tag : 'span',
30562                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30563             });
30564             
30565             var bottomText = new Roo.bootstrap.Element({
30566                 tag : 'span',
30567                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30568             });
30569             
30570             topText.onRender(top.el, null);
30571             bottomText.onRender(bottom.el, null);
30572             
30573             item.topEl = top;
30574             item.bottomEl = bottom;
30575         }
30576         
30577         this.barItems.push(item);
30578         
30579         return item;
30580     },
30581     
30582     getActive : function()
30583     {
30584         var active = false;
30585         
30586         Roo.each(this.barItems, function(v){
30587             
30588             if (!v.isActive()) {
30589                 return;
30590             }
30591             
30592             active = v;
30593             return false;
30594             
30595         });
30596         
30597         return active;
30598     },
30599     
30600     setActiveItem : function(item)
30601     {
30602         var prev = false;
30603         
30604         Roo.each(this.barItems, function(v){
30605             if (v.rid == item.rid) {
30606                 return ;
30607             }
30608             
30609             if (v.isActive()) {
30610                 v.setActive(false);
30611                 prev = v;
30612             }
30613         });
30614
30615         item.setActive(true);
30616         
30617         this.fireEvent('changed', this, item, prev);
30618     },
30619     
30620     getBarItem: function(rid)
30621     {
30622         var ret = false;
30623         
30624         Roo.each(this.barItems, function(e) {
30625             if (e.rid != rid) {
30626                 return;
30627             }
30628             
30629             ret =  e;
30630             return false;
30631         });
30632         
30633         return ret;
30634     },
30635     
30636     indexOfItem : function(item)
30637     {
30638         var index = false;
30639         
30640         Roo.each(this.barItems, function(v, i){
30641             
30642             if (v.rid != item.rid) {
30643                 return;
30644             }
30645             
30646             index = i;
30647             return false
30648         });
30649         
30650         return index;
30651     },
30652     
30653     setActiveNext : function()
30654     {
30655         var i = this.indexOfItem(this.getActive());
30656         
30657         if (i > this.barItems.length) {
30658             return;
30659         }
30660         
30661         this.setActiveItem(this.barItems[i+1]);
30662     },
30663     
30664     setActivePrev : function()
30665     {
30666         var i = this.indexOfItem(this.getActive());
30667         
30668         if (i  < 1) {
30669             return;
30670         }
30671         
30672         this.setActiveItem(this.barItems[i-1]);
30673     },
30674     
30675     format : function()
30676     {
30677         if(!this.barItems.length){
30678             return;
30679         }
30680      
30681         var width = 100 / this.barItems.length;
30682         
30683         Roo.each(this.barItems, function(i){
30684             i.el.setStyle('width', width + '%');
30685             i.topEl.el.setStyle('width', width + '%');
30686             i.bottomEl.el.setStyle('width', width + '%');
30687         }, this);
30688         
30689     }
30690     
30691 });
30692 /*
30693  * - LGPL
30694  *
30695  * Nav Progress Item
30696  * 
30697  */
30698
30699 /**
30700  * @class Roo.bootstrap.NavProgressItem
30701  * @extends Roo.bootstrap.Component
30702  * Bootstrap NavProgressItem class
30703  * @cfg {String} rid the reference id
30704  * @cfg {Boolean} active (true|false) Is item active default false
30705  * @cfg {Boolean} disabled (true|false) Is item active default false
30706  * @cfg {String} html
30707  * @cfg {String} position (top|bottom) text position default bottom
30708  * @cfg {String} icon show icon instead of number
30709  * 
30710  * @constructor
30711  * Create a new NavProgressItem
30712  * @param {Object} config The config object
30713  */
30714 Roo.bootstrap.NavProgressItem = function(config){
30715     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30716     this.addEvents({
30717         // raw events
30718         /**
30719          * @event click
30720          * The raw click event for the entire grid.
30721          * @param {Roo.bootstrap.NavProgressItem} this
30722          * @param {Roo.EventObject} e
30723          */
30724         "click" : true
30725     });
30726    
30727 };
30728
30729 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30730     
30731     rid : '',
30732     active : false,
30733     disabled : false,
30734     html : '',
30735     position : 'bottom',
30736     icon : false,
30737     
30738     getAutoCreate : function()
30739     {
30740         var iconCls = 'roo-navigation-bar-item-icon';
30741         
30742         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30743         
30744         var cfg = {
30745             tag: 'li',
30746             cls: 'roo-navigation-bar-item',
30747             cn : [
30748                 {
30749                     tag : 'i',
30750                     cls : iconCls
30751                 }
30752             ]
30753         };
30754         
30755         if(this.active){
30756             cfg.cls += ' active';
30757         }
30758         if(this.disabled){
30759             cfg.cls += ' disabled';
30760         }
30761         
30762         return cfg;
30763     },
30764     
30765     disable : function()
30766     {
30767         this.setDisabled(true);
30768     },
30769     
30770     enable : function()
30771     {
30772         this.setDisabled(false);
30773     },
30774     
30775     initEvents: function() 
30776     {
30777         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30778         
30779         this.iconEl.on('click', this.onClick, this);
30780     },
30781     
30782     onClick : function(e)
30783     {
30784         e.preventDefault();
30785         
30786         if(this.disabled){
30787             return;
30788         }
30789         
30790         if(this.fireEvent('click', this, e) === false){
30791             return;
30792         };
30793         
30794         this.parent().setActiveItem(this);
30795     },
30796     
30797     isActive: function () 
30798     {
30799         return this.active;
30800     },
30801     
30802     setActive : function(state)
30803     {
30804         if(this.active == state){
30805             return;
30806         }
30807         
30808         this.active = state;
30809         
30810         if (state) {
30811             this.el.addClass('active');
30812             return;
30813         }
30814         
30815         this.el.removeClass('active');
30816         
30817         return;
30818     },
30819     
30820     setDisabled : function(state)
30821     {
30822         if(this.disabled == state){
30823             return;
30824         }
30825         
30826         this.disabled = state;
30827         
30828         if (state) {
30829             this.el.addClass('disabled');
30830             return;
30831         }
30832         
30833         this.el.removeClass('disabled');
30834     },
30835     
30836     tooltipEl : function()
30837     {
30838         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30839     }
30840 });
30841  
30842
30843  /*
30844  * - LGPL
30845  *
30846  * FieldLabel
30847  * 
30848  */
30849
30850 /**
30851  * @class Roo.bootstrap.FieldLabel
30852  * @extends Roo.bootstrap.Component
30853  * Bootstrap FieldLabel class
30854  * @cfg {String} html contents of the element
30855  * @cfg {String} tag tag of the element default label
30856  * @cfg {String} cls class of the element
30857  * @cfg {String} target label target 
30858  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30859  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30860  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30861  * @cfg {String} iconTooltip default "This field is required"
30862  * @cfg {String} indicatorpos (left|right) default left
30863  * 
30864  * @constructor
30865  * Create a new FieldLabel
30866  * @param {Object} config The config object
30867  */
30868
30869 Roo.bootstrap.FieldLabel = function(config){
30870     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30871     
30872     this.addEvents({
30873             /**
30874              * @event invalid
30875              * Fires after the field has been marked as invalid.
30876              * @param {Roo.form.FieldLabel} this
30877              * @param {String} msg The validation message
30878              */
30879             invalid : true,
30880             /**
30881              * @event valid
30882              * Fires after the field has been validated with no errors.
30883              * @param {Roo.form.FieldLabel} this
30884              */
30885             valid : true
30886         });
30887 };
30888
30889 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30890     
30891     tag: 'label',
30892     cls: '',
30893     html: '',
30894     target: '',
30895     allowBlank : true,
30896     invalidClass : 'has-warning',
30897     validClass : 'has-success',
30898     iconTooltip : 'This field is required',
30899     indicatorpos : 'left',
30900     
30901     getAutoCreate : function(){
30902         
30903         var cls = "";
30904         if (!this.allowBlank) {
30905             cls  = "visible";
30906         }
30907         
30908         var cfg = {
30909             tag : this.tag,
30910             cls : 'roo-bootstrap-field-label ' + this.cls,
30911             for : this.target,
30912             cn : [
30913                 {
30914                     tag : 'i',
30915                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30916                     tooltip : this.iconTooltip
30917                 },
30918                 {
30919                     tag : 'span',
30920                     html : this.html
30921                 }
30922             ] 
30923         };
30924         
30925         if(this.indicatorpos == 'right'){
30926             var cfg = {
30927                 tag : this.tag,
30928                 cls : 'roo-bootstrap-field-label ' + this.cls,
30929                 for : this.target,
30930                 cn : [
30931                     {
30932                         tag : 'span',
30933                         html : this.html
30934                     },
30935                     {
30936                         tag : 'i',
30937                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30938                         tooltip : this.iconTooltip
30939                     }
30940                 ] 
30941             };
30942         }
30943         
30944         return cfg;
30945     },
30946     
30947     initEvents: function() 
30948     {
30949         Roo.bootstrap.Element.superclass.initEvents.call(this);
30950         
30951         this.indicator = this.indicatorEl();
30952         
30953         if(this.indicator){
30954             this.indicator.removeClass('visible');
30955             this.indicator.addClass('invisible');
30956         }
30957         
30958         Roo.bootstrap.FieldLabel.register(this);
30959     },
30960     
30961     indicatorEl : function()
30962     {
30963         var indicator = this.el.select('i.roo-required-indicator',true).first();
30964         
30965         if(!indicator){
30966             return false;
30967         }
30968         
30969         return indicator;
30970         
30971     },
30972     
30973     /**
30974      * Mark this field as valid
30975      */
30976     markValid : function()
30977     {
30978         if(this.indicator){
30979             this.indicator.removeClass('visible');
30980             this.indicator.addClass('invisible');
30981         }
30982         if (Roo.bootstrap.version == 3) {
30983             this.el.removeClass(this.invalidClass);
30984             this.el.addClass(this.validClass);
30985         } else {
30986             this.el.removeClass('is-invalid');
30987             this.el.addClass('is-valid');
30988         }
30989         
30990         
30991         this.fireEvent('valid', this);
30992     },
30993     
30994     /**
30995      * Mark this field as invalid
30996      * @param {String} msg The validation message
30997      */
30998     markInvalid : function(msg)
30999     {
31000         if(this.indicator){
31001             this.indicator.removeClass('invisible');
31002             this.indicator.addClass('visible');
31003         }
31004           if (Roo.bootstrap.version == 3) {
31005             this.el.removeClass(this.validClass);
31006             this.el.addClass(this.invalidClass);
31007         } else {
31008             this.el.removeClass('is-valid');
31009             this.el.addClass('is-invalid');
31010         }
31011         
31012         
31013         this.fireEvent('invalid', this, msg);
31014     }
31015     
31016    
31017 });
31018
31019 Roo.apply(Roo.bootstrap.FieldLabel, {
31020     
31021     groups: {},
31022     
31023      /**
31024     * register a FieldLabel Group
31025     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
31026     */
31027     register : function(label)
31028     {
31029         if(this.groups.hasOwnProperty(label.target)){
31030             return;
31031         }
31032      
31033         this.groups[label.target] = label;
31034         
31035     },
31036     /**
31037     * fetch a FieldLabel Group based on the target
31038     * @param {string} target
31039     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
31040     */
31041     get: function(target) {
31042         if (typeof(this.groups[target]) == 'undefined') {
31043             return false;
31044         }
31045         
31046         return this.groups[target] ;
31047     }
31048 });
31049
31050  
31051
31052  /*
31053  * - LGPL
31054  *
31055  * page DateSplitField.
31056  * 
31057  */
31058
31059
31060 /**
31061  * @class Roo.bootstrap.DateSplitField
31062  * @extends Roo.bootstrap.Component
31063  * Bootstrap DateSplitField class
31064  * @cfg {string} fieldLabel - the label associated
31065  * @cfg {Number} labelWidth set the width of label (0-12)
31066  * @cfg {String} labelAlign (top|left)
31067  * @cfg {Boolean} dayAllowBlank (true|false) default false
31068  * @cfg {Boolean} monthAllowBlank (true|false) default false
31069  * @cfg {Boolean} yearAllowBlank (true|false) default false
31070  * @cfg {string} dayPlaceholder 
31071  * @cfg {string} monthPlaceholder
31072  * @cfg {string} yearPlaceholder
31073  * @cfg {string} dayFormat default 'd'
31074  * @cfg {string} monthFormat default 'm'
31075  * @cfg {string} yearFormat default 'Y'
31076  * @cfg {Number} labellg set the width of label (1-12)
31077  * @cfg {Number} labelmd set the width of label (1-12)
31078  * @cfg {Number} labelsm set the width of label (1-12)
31079  * @cfg {Number} labelxs set the width of label (1-12)
31080
31081  *     
31082  * @constructor
31083  * Create a new DateSplitField
31084  * @param {Object} config The config object
31085  */
31086
31087 Roo.bootstrap.DateSplitField = function(config){
31088     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
31089     
31090     this.addEvents({
31091         // raw events
31092          /**
31093          * @event years
31094          * getting the data of years
31095          * @param {Roo.bootstrap.DateSplitField} this
31096          * @param {Object} years
31097          */
31098         "years" : true,
31099         /**
31100          * @event days
31101          * getting the data of days
31102          * @param {Roo.bootstrap.DateSplitField} this
31103          * @param {Object} days
31104          */
31105         "days" : true,
31106         /**
31107          * @event invalid
31108          * Fires after the field has been marked as invalid.
31109          * @param {Roo.form.Field} this
31110          * @param {String} msg The validation message
31111          */
31112         invalid : true,
31113        /**
31114          * @event valid
31115          * Fires after the field has been validated with no errors.
31116          * @param {Roo.form.Field} this
31117          */
31118         valid : true
31119     });
31120 };
31121
31122 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
31123     
31124     fieldLabel : '',
31125     labelAlign : 'top',
31126     labelWidth : 3,
31127     dayAllowBlank : false,
31128     monthAllowBlank : false,
31129     yearAllowBlank : false,
31130     dayPlaceholder : '',
31131     monthPlaceholder : '',
31132     yearPlaceholder : '',
31133     dayFormat : 'd',
31134     monthFormat : 'm',
31135     yearFormat : 'Y',
31136     isFormField : true,
31137     labellg : 0,
31138     labelmd : 0,
31139     labelsm : 0,
31140     labelxs : 0,
31141     
31142     getAutoCreate : function()
31143     {
31144         var cfg = {
31145             tag : 'div',
31146             cls : 'row roo-date-split-field-group',
31147             cn : [
31148                 {
31149                     tag : 'input',
31150                     type : 'hidden',
31151                     cls : 'form-hidden-field roo-date-split-field-group-value',
31152                     name : this.name
31153                 }
31154             ]
31155         };
31156         
31157         var labelCls = 'col-md-12';
31158         var contentCls = 'col-md-4';
31159         
31160         if(this.fieldLabel){
31161             
31162             var label = {
31163                 tag : 'div',
31164                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
31165                 cn : [
31166                     {
31167                         tag : 'label',
31168                         html : this.fieldLabel
31169                     }
31170                 ]
31171             };
31172             
31173             if(this.labelAlign == 'left'){
31174             
31175                 if(this.labelWidth > 12){
31176                     label.style = "width: " + this.labelWidth + 'px';
31177                 }
31178
31179                 if(this.labelWidth < 13 && this.labelmd == 0){
31180                     this.labelmd = this.labelWidth;
31181                 }
31182
31183                 if(this.labellg > 0){
31184                     labelCls = ' col-lg-' + this.labellg;
31185                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
31186                 }
31187
31188                 if(this.labelmd > 0){
31189                     labelCls = ' col-md-' + this.labelmd;
31190                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
31191                 }
31192
31193                 if(this.labelsm > 0){
31194                     labelCls = ' col-sm-' + this.labelsm;
31195                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
31196                 }
31197
31198                 if(this.labelxs > 0){
31199                     labelCls = ' col-xs-' + this.labelxs;
31200                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
31201                 }
31202             }
31203             
31204             label.cls += ' ' + labelCls;
31205             
31206             cfg.cn.push(label);
31207         }
31208         
31209         Roo.each(['day', 'month', 'year'], function(t){
31210             cfg.cn.push({
31211                 tag : 'div',
31212                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
31213             });
31214         }, this);
31215         
31216         return cfg;
31217     },
31218     
31219     inputEl: function ()
31220     {
31221         return this.el.select('.roo-date-split-field-group-value', true).first();
31222     },
31223     
31224     onRender : function(ct, position) 
31225     {
31226         var _this = this;
31227         
31228         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31229         
31230         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
31231         
31232         this.dayField = new Roo.bootstrap.ComboBox({
31233             allowBlank : this.dayAllowBlank,
31234             alwaysQuery : true,
31235             displayField : 'value',
31236             editable : false,
31237             fieldLabel : '',
31238             forceSelection : true,
31239             mode : 'local',
31240             placeholder : this.dayPlaceholder,
31241             selectOnFocus : true,
31242             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31243             triggerAction : 'all',
31244             typeAhead : true,
31245             valueField : 'value',
31246             store : new Roo.data.SimpleStore({
31247                 data : (function() {    
31248                     var days = [];
31249                     _this.fireEvent('days', _this, days);
31250                     return days;
31251                 })(),
31252                 fields : [ 'value' ]
31253             }),
31254             listeners : {
31255                 select : function (_self, record, index)
31256                 {
31257                     _this.setValue(_this.getValue());
31258                 }
31259             }
31260         });
31261
31262         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
31263         
31264         this.monthField = new Roo.bootstrap.MonthField({
31265             after : '<i class=\"fa fa-calendar\"></i>',
31266             allowBlank : this.monthAllowBlank,
31267             placeholder : this.monthPlaceholder,
31268             readOnly : true,
31269             listeners : {
31270                 render : function (_self)
31271                 {
31272                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
31273                         e.preventDefault();
31274                         _self.focus();
31275                     });
31276                 },
31277                 select : function (_self, oldvalue, newvalue)
31278                 {
31279                     _this.setValue(_this.getValue());
31280                 }
31281             }
31282         });
31283         
31284         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
31285         
31286         this.yearField = new Roo.bootstrap.ComboBox({
31287             allowBlank : this.yearAllowBlank,
31288             alwaysQuery : true,
31289             displayField : 'value',
31290             editable : false,
31291             fieldLabel : '',
31292             forceSelection : true,
31293             mode : 'local',
31294             placeholder : this.yearPlaceholder,
31295             selectOnFocus : true,
31296             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31297             triggerAction : 'all',
31298             typeAhead : true,
31299             valueField : 'value',
31300             store : new Roo.data.SimpleStore({
31301                 data : (function() {
31302                     var years = [];
31303                     _this.fireEvent('years', _this, years);
31304                     return years;
31305                 })(),
31306                 fields : [ 'value' ]
31307             }),
31308             listeners : {
31309                 select : function (_self, record, index)
31310                 {
31311                     _this.setValue(_this.getValue());
31312                 }
31313             }
31314         });
31315
31316         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
31317     },
31318     
31319     setValue : function(v, format)
31320     {
31321         this.inputEl.dom.value = v;
31322         
31323         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
31324         
31325         var d = Date.parseDate(v, f);
31326         
31327         if(!d){
31328             this.validate();
31329             return;
31330         }
31331         
31332         this.setDay(d.format(this.dayFormat));
31333         this.setMonth(d.format(this.monthFormat));
31334         this.setYear(d.format(this.yearFormat));
31335         
31336         this.validate();
31337         
31338         return;
31339     },
31340     
31341     setDay : function(v)
31342     {
31343         this.dayField.setValue(v);
31344         this.inputEl.dom.value = this.getValue();
31345         this.validate();
31346         return;
31347     },
31348     
31349     setMonth : function(v)
31350     {
31351         this.monthField.setValue(v, true);
31352         this.inputEl.dom.value = this.getValue();
31353         this.validate();
31354         return;
31355     },
31356     
31357     setYear : function(v)
31358     {
31359         this.yearField.setValue(v);
31360         this.inputEl.dom.value = this.getValue();
31361         this.validate();
31362         return;
31363     },
31364     
31365     getDay : function()
31366     {
31367         return this.dayField.getValue();
31368     },
31369     
31370     getMonth : function()
31371     {
31372         return this.monthField.getValue();
31373     },
31374     
31375     getYear : function()
31376     {
31377         return this.yearField.getValue();
31378     },
31379     
31380     getValue : function()
31381     {
31382         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31383         
31384         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31385         
31386         return date;
31387     },
31388     
31389     reset : function()
31390     {
31391         this.setDay('');
31392         this.setMonth('');
31393         this.setYear('');
31394         this.inputEl.dom.value = '';
31395         this.validate();
31396         return;
31397     },
31398     
31399     validate : function()
31400     {
31401         var d = this.dayField.validate();
31402         var m = this.monthField.validate();
31403         var y = this.yearField.validate();
31404         
31405         var valid = true;
31406         
31407         if(
31408                 (!this.dayAllowBlank && !d) ||
31409                 (!this.monthAllowBlank && !m) ||
31410                 (!this.yearAllowBlank && !y)
31411         ){
31412             valid = false;
31413         }
31414         
31415         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31416             return valid;
31417         }
31418         
31419         if(valid){
31420             this.markValid();
31421             return valid;
31422         }
31423         
31424         this.markInvalid();
31425         
31426         return valid;
31427     },
31428     
31429     markValid : function()
31430     {
31431         
31432         var label = this.el.select('label', true).first();
31433         var icon = this.el.select('i.fa-star', true).first();
31434
31435         if(label && icon){
31436             icon.remove();
31437         }
31438         
31439         this.fireEvent('valid', this);
31440     },
31441     
31442      /**
31443      * Mark this field as invalid
31444      * @param {String} msg The validation message
31445      */
31446     markInvalid : function(msg)
31447     {
31448         
31449         var label = this.el.select('label', true).first();
31450         var icon = this.el.select('i.fa-star', true).first();
31451
31452         if(label && !icon){
31453             this.el.select('.roo-date-split-field-label', true).createChild({
31454                 tag : 'i',
31455                 cls : 'text-danger fa fa-lg fa-star',
31456                 tooltip : 'This field is required',
31457                 style : 'margin-right:5px;'
31458             }, label, true);
31459         }
31460         
31461         this.fireEvent('invalid', this, msg);
31462     },
31463     
31464     clearInvalid : function()
31465     {
31466         var label = this.el.select('label', true).first();
31467         var icon = this.el.select('i.fa-star', true).first();
31468
31469         if(label && icon){
31470             icon.remove();
31471         }
31472         
31473         this.fireEvent('valid', this);
31474     },
31475     
31476     getName: function()
31477     {
31478         return this.name;
31479     }
31480     
31481 });
31482
31483  /**
31484  *
31485  * This is based on 
31486  * http://masonry.desandro.com
31487  *
31488  * The idea is to render all the bricks based on vertical width...
31489  *
31490  * The original code extends 'outlayer' - we might need to use that....
31491  * 
31492  */
31493
31494
31495 /**
31496  * @class Roo.bootstrap.LayoutMasonry
31497  * @extends Roo.bootstrap.Component
31498  * Bootstrap Layout Masonry class
31499  * 
31500  * @constructor
31501  * Create a new Element
31502  * @param {Object} config The config object
31503  */
31504
31505 Roo.bootstrap.LayoutMasonry = function(config){
31506     
31507     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31508     
31509     this.bricks = [];
31510     
31511     Roo.bootstrap.LayoutMasonry.register(this);
31512     
31513     this.addEvents({
31514         // raw events
31515         /**
31516          * @event layout
31517          * Fire after layout the items
31518          * @param {Roo.bootstrap.LayoutMasonry} this
31519          * @param {Roo.EventObject} e
31520          */
31521         "layout" : true
31522     });
31523     
31524 };
31525
31526 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31527     
31528     /**
31529      * @cfg {Boolean} isLayoutInstant = no animation?
31530      */   
31531     isLayoutInstant : false, // needed?
31532    
31533     /**
31534      * @cfg {Number} boxWidth  width of the columns
31535      */   
31536     boxWidth : 450,
31537     
31538       /**
31539      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31540      */   
31541     boxHeight : 0,
31542     
31543     /**
31544      * @cfg {Number} padWidth padding below box..
31545      */   
31546     padWidth : 10, 
31547     
31548     /**
31549      * @cfg {Number} gutter gutter width..
31550      */   
31551     gutter : 10,
31552     
31553      /**
31554      * @cfg {Number} maxCols maximum number of columns
31555      */   
31556     
31557     maxCols: 0,
31558     
31559     /**
31560      * @cfg {Boolean} isAutoInitial defalut true
31561      */   
31562     isAutoInitial : true, 
31563     
31564     containerWidth: 0,
31565     
31566     /**
31567      * @cfg {Boolean} isHorizontal defalut false
31568      */   
31569     isHorizontal : false, 
31570
31571     currentSize : null,
31572     
31573     tag: 'div',
31574     
31575     cls: '',
31576     
31577     bricks: null, //CompositeElement
31578     
31579     cols : 1,
31580     
31581     _isLayoutInited : false,
31582     
31583 //    isAlternative : false, // only use for vertical layout...
31584     
31585     /**
31586      * @cfg {Number} alternativePadWidth padding below box..
31587      */   
31588     alternativePadWidth : 50,
31589     
31590     selectedBrick : [],
31591     
31592     getAutoCreate : function(){
31593         
31594         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31595         
31596         var cfg = {
31597             tag: this.tag,
31598             cls: 'blog-masonary-wrapper ' + this.cls,
31599             cn : {
31600                 cls : 'mas-boxes masonary'
31601             }
31602         };
31603         
31604         return cfg;
31605     },
31606     
31607     getChildContainer: function( )
31608     {
31609         if (this.boxesEl) {
31610             return this.boxesEl;
31611         }
31612         
31613         this.boxesEl = this.el.select('.mas-boxes').first();
31614         
31615         return this.boxesEl;
31616     },
31617     
31618     
31619     initEvents : function()
31620     {
31621         var _this = this;
31622         
31623         if(this.isAutoInitial){
31624             Roo.log('hook children rendered');
31625             this.on('childrenrendered', function() {
31626                 Roo.log('children rendered');
31627                 _this.initial();
31628             } ,this);
31629         }
31630     },
31631     
31632     initial : function()
31633     {
31634         this.selectedBrick = [];
31635         
31636         this.currentSize = this.el.getBox(true);
31637         
31638         Roo.EventManager.onWindowResize(this.resize, this); 
31639
31640         if(!this.isAutoInitial){
31641             this.layout();
31642             return;
31643         }
31644         
31645         this.layout();
31646         
31647         return;
31648         //this.layout.defer(500,this);
31649         
31650     },
31651     
31652     resize : function()
31653     {
31654         var cs = this.el.getBox(true);
31655         
31656         if (
31657                 this.currentSize.width == cs.width && 
31658                 this.currentSize.x == cs.x && 
31659                 this.currentSize.height == cs.height && 
31660                 this.currentSize.y == cs.y 
31661         ) {
31662             Roo.log("no change in with or X or Y");
31663             return;
31664         }
31665         
31666         this.currentSize = cs;
31667         
31668         this.layout();
31669         
31670     },
31671     
31672     layout : function()
31673     {   
31674         this._resetLayout();
31675         
31676         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31677         
31678         this.layoutItems( isInstant );
31679       
31680         this._isLayoutInited = true;
31681         
31682         this.fireEvent('layout', this);
31683         
31684     },
31685     
31686     _resetLayout : function()
31687     {
31688         if(this.isHorizontal){
31689             this.horizontalMeasureColumns();
31690             return;
31691         }
31692         
31693         this.verticalMeasureColumns();
31694         
31695     },
31696     
31697     verticalMeasureColumns : function()
31698     {
31699         this.getContainerWidth();
31700         
31701 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31702 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31703 //            return;
31704 //        }
31705         
31706         var boxWidth = this.boxWidth + this.padWidth;
31707         
31708         if(this.containerWidth < this.boxWidth){
31709             boxWidth = this.containerWidth
31710         }
31711         
31712         var containerWidth = this.containerWidth;
31713         
31714         var cols = Math.floor(containerWidth / boxWidth);
31715         
31716         this.cols = Math.max( cols, 1 );
31717         
31718         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31719         
31720         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31721         
31722         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31723         
31724         this.colWidth = boxWidth + avail - this.padWidth;
31725         
31726         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31727         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31728     },
31729     
31730     horizontalMeasureColumns : function()
31731     {
31732         this.getContainerWidth();
31733         
31734         var boxWidth = this.boxWidth;
31735         
31736         if(this.containerWidth < boxWidth){
31737             boxWidth = this.containerWidth;
31738         }
31739         
31740         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31741         
31742         this.el.setHeight(boxWidth);
31743         
31744     },
31745     
31746     getContainerWidth : function()
31747     {
31748         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31749     },
31750     
31751     layoutItems : function( isInstant )
31752     {
31753         Roo.log(this.bricks);
31754         
31755         var items = Roo.apply([], this.bricks);
31756         
31757         if(this.isHorizontal){
31758             this._horizontalLayoutItems( items , isInstant );
31759             return;
31760         }
31761         
31762 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31763 //            this._verticalAlternativeLayoutItems( items , isInstant );
31764 //            return;
31765 //        }
31766         
31767         this._verticalLayoutItems( items , isInstant );
31768         
31769     },
31770     
31771     _verticalLayoutItems : function ( items , isInstant)
31772     {
31773         if ( !items || !items.length ) {
31774             return;
31775         }
31776         
31777         var standard = [
31778             ['xs', 'xs', 'xs', 'tall'],
31779             ['xs', 'xs', 'tall'],
31780             ['xs', 'xs', 'sm'],
31781             ['xs', 'xs', 'xs'],
31782             ['xs', 'tall'],
31783             ['xs', 'sm'],
31784             ['xs', 'xs'],
31785             ['xs'],
31786             
31787             ['sm', 'xs', 'xs'],
31788             ['sm', 'xs'],
31789             ['sm'],
31790             
31791             ['tall', 'xs', 'xs', 'xs'],
31792             ['tall', 'xs', 'xs'],
31793             ['tall', 'xs'],
31794             ['tall']
31795             
31796         ];
31797         
31798         var queue = [];
31799         
31800         var boxes = [];
31801         
31802         var box = [];
31803         
31804         Roo.each(items, function(item, k){
31805             
31806             switch (item.size) {
31807                 // these layouts take up a full box,
31808                 case 'md' :
31809                 case 'md-left' :
31810                 case 'md-right' :
31811                 case 'wide' :
31812                     
31813                     if(box.length){
31814                         boxes.push(box);
31815                         box = [];
31816                     }
31817                     
31818                     boxes.push([item]);
31819                     
31820                     break;
31821                     
31822                 case 'xs' :
31823                 case 'sm' :
31824                 case 'tall' :
31825                     
31826                     box.push(item);
31827                     
31828                     break;
31829                 default :
31830                     break;
31831                     
31832             }
31833             
31834         }, this);
31835         
31836         if(box.length){
31837             boxes.push(box);
31838             box = [];
31839         }
31840         
31841         var filterPattern = function(box, length)
31842         {
31843             if(!box.length){
31844                 return;
31845             }
31846             
31847             var match = false;
31848             
31849             var pattern = box.slice(0, length);
31850             
31851             var format = [];
31852             
31853             Roo.each(pattern, function(i){
31854                 format.push(i.size);
31855             }, this);
31856             
31857             Roo.each(standard, function(s){
31858                 
31859                 if(String(s) != String(format)){
31860                     return;
31861                 }
31862                 
31863                 match = true;
31864                 return false;
31865                 
31866             }, this);
31867             
31868             if(!match && length == 1){
31869                 return;
31870             }
31871             
31872             if(!match){
31873                 filterPattern(box, length - 1);
31874                 return;
31875             }
31876                 
31877             queue.push(pattern);
31878
31879             box = box.slice(length, box.length);
31880
31881             filterPattern(box, 4);
31882
31883             return;
31884             
31885         }
31886         
31887         Roo.each(boxes, function(box, k){
31888             
31889             if(!box.length){
31890                 return;
31891             }
31892             
31893             if(box.length == 1){
31894                 queue.push(box);
31895                 return;
31896             }
31897             
31898             filterPattern(box, 4);
31899             
31900         }, this);
31901         
31902         this._processVerticalLayoutQueue( queue, isInstant );
31903         
31904     },
31905     
31906 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31907 //    {
31908 //        if ( !items || !items.length ) {
31909 //            return;
31910 //        }
31911 //
31912 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31913 //        
31914 //    },
31915     
31916     _horizontalLayoutItems : function ( items , isInstant)
31917     {
31918         if ( !items || !items.length || items.length < 3) {
31919             return;
31920         }
31921         
31922         items.reverse();
31923         
31924         var eItems = items.slice(0, 3);
31925         
31926         items = items.slice(3, items.length);
31927         
31928         var standard = [
31929             ['xs', 'xs', 'xs', 'wide'],
31930             ['xs', 'xs', 'wide'],
31931             ['xs', 'xs', 'sm'],
31932             ['xs', 'xs', 'xs'],
31933             ['xs', 'wide'],
31934             ['xs', 'sm'],
31935             ['xs', 'xs'],
31936             ['xs'],
31937             
31938             ['sm', 'xs', 'xs'],
31939             ['sm', 'xs'],
31940             ['sm'],
31941             
31942             ['wide', 'xs', 'xs', 'xs'],
31943             ['wide', 'xs', 'xs'],
31944             ['wide', 'xs'],
31945             ['wide'],
31946             
31947             ['wide-thin']
31948         ];
31949         
31950         var queue = [];
31951         
31952         var boxes = [];
31953         
31954         var box = [];
31955         
31956         Roo.each(items, function(item, k){
31957             
31958             switch (item.size) {
31959                 case 'md' :
31960                 case 'md-left' :
31961                 case 'md-right' :
31962                 case 'tall' :
31963                     
31964                     if(box.length){
31965                         boxes.push(box);
31966                         box = [];
31967                     }
31968                     
31969                     boxes.push([item]);
31970                     
31971                     break;
31972                     
31973                 case 'xs' :
31974                 case 'sm' :
31975                 case 'wide' :
31976                 case 'wide-thin' :
31977                     
31978                     box.push(item);
31979                     
31980                     break;
31981                 default :
31982                     break;
31983                     
31984             }
31985             
31986         }, this);
31987         
31988         if(box.length){
31989             boxes.push(box);
31990             box = [];
31991         }
31992         
31993         var filterPattern = function(box, length)
31994         {
31995             if(!box.length){
31996                 return;
31997             }
31998             
31999             var match = false;
32000             
32001             var pattern = box.slice(0, length);
32002             
32003             var format = [];
32004             
32005             Roo.each(pattern, function(i){
32006                 format.push(i.size);
32007             }, this);
32008             
32009             Roo.each(standard, function(s){
32010                 
32011                 if(String(s) != String(format)){
32012                     return;
32013                 }
32014                 
32015                 match = true;
32016                 return false;
32017                 
32018             }, this);
32019             
32020             if(!match && length == 1){
32021                 return;
32022             }
32023             
32024             if(!match){
32025                 filterPattern(box, length - 1);
32026                 return;
32027             }
32028                 
32029             queue.push(pattern);
32030
32031             box = box.slice(length, box.length);
32032
32033             filterPattern(box, 4);
32034
32035             return;
32036             
32037         }
32038         
32039         Roo.each(boxes, function(box, k){
32040             
32041             if(!box.length){
32042                 return;
32043             }
32044             
32045             if(box.length == 1){
32046                 queue.push(box);
32047                 return;
32048             }
32049             
32050             filterPattern(box, 4);
32051             
32052         }, this);
32053         
32054         
32055         var prune = [];
32056         
32057         var pos = this.el.getBox(true);
32058         
32059         var minX = pos.x;
32060         
32061         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32062         
32063         var hit_end = false;
32064         
32065         Roo.each(queue, function(box){
32066             
32067             if(hit_end){
32068                 
32069                 Roo.each(box, function(b){
32070                 
32071                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
32072                     b.el.hide();
32073
32074                 }, this);
32075
32076                 return;
32077             }
32078             
32079             var mx = 0;
32080             
32081             Roo.each(box, function(b){
32082                 
32083                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32084                 b.el.show();
32085
32086                 mx = Math.max(mx, b.x);
32087                 
32088             }, this);
32089             
32090             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
32091             
32092             if(maxX < minX){
32093                 
32094                 Roo.each(box, function(b){
32095                 
32096                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
32097                     b.el.hide();
32098                     
32099                 }, this);
32100                 
32101                 hit_end = true;
32102                 
32103                 return;
32104             }
32105             
32106             prune.push(box);
32107             
32108         }, this);
32109         
32110         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
32111     },
32112     
32113     /** Sets position of item in DOM
32114     * @param {Element} item
32115     * @param {Number} x - horizontal position
32116     * @param {Number} y - vertical position
32117     * @param {Boolean} isInstant - disables transitions
32118     */
32119     _processVerticalLayoutQueue : function( queue, isInstant )
32120     {
32121         var pos = this.el.getBox(true);
32122         var x = pos.x;
32123         var y = pos.y;
32124         var maxY = [];
32125         
32126         for (var i = 0; i < this.cols; i++){
32127             maxY[i] = pos.y;
32128         }
32129         
32130         Roo.each(queue, function(box, k){
32131             
32132             var col = k % this.cols;
32133             
32134             Roo.each(box, function(b,kk){
32135                 
32136                 b.el.position('absolute');
32137                 
32138                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32139                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32140                 
32141                 if(b.size == 'md-left' || b.size == 'md-right'){
32142                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32143                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32144                 }
32145                 
32146                 b.el.setWidth(width);
32147                 b.el.setHeight(height);
32148                 // iframe?
32149                 b.el.select('iframe',true).setSize(width,height);
32150                 
32151             }, this);
32152             
32153             for (var i = 0; i < this.cols; i++){
32154                 
32155                 if(maxY[i] < maxY[col]){
32156                     col = i;
32157                     continue;
32158                 }
32159                 
32160                 col = Math.min(col, i);
32161                 
32162             }
32163             
32164             x = pos.x + col * (this.colWidth + this.padWidth);
32165             
32166             y = maxY[col];
32167             
32168             var positions = [];
32169             
32170             switch (box.length){
32171                 case 1 :
32172                     positions = this.getVerticalOneBoxColPositions(x, y, box);
32173                     break;
32174                 case 2 :
32175                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
32176                     break;
32177                 case 3 :
32178                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
32179                     break;
32180                 case 4 :
32181                     positions = this.getVerticalFourBoxColPositions(x, y, box);
32182                     break;
32183                 default :
32184                     break;
32185             }
32186             
32187             Roo.each(box, function(b,kk){
32188                 
32189                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32190                 
32191                 var sz = b.el.getSize();
32192                 
32193                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
32194                 
32195             }, this);
32196             
32197         }, this);
32198         
32199         var mY = 0;
32200         
32201         for (var i = 0; i < this.cols; i++){
32202             mY = Math.max(mY, maxY[i]);
32203         }
32204         
32205         this.el.setHeight(mY - pos.y);
32206         
32207     },
32208     
32209 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
32210 //    {
32211 //        var pos = this.el.getBox(true);
32212 //        var x = pos.x;
32213 //        var y = pos.y;
32214 //        var maxX = pos.right;
32215 //        
32216 //        var maxHeight = 0;
32217 //        
32218 //        Roo.each(items, function(item, k){
32219 //            
32220 //            var c = k % 2;
32221 //            
32222 //            item.el.position('absolute');
32223 //                
32224 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
32225 //
32226 //            item.el.setWidth(width);
32227 //
32228 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
32229 //
32230 //            item.el.setHeight(height);
32231 //            
32232 //            if(c == 0){
32233 //                item.el.setXY([x, y], isInstant ? false : true);
32234 //            } else {
32235 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
32236 //            }
32237 //            
32238 //            y = y + height + this.alternativePadWidth;
32239 //            
32240 //            maxHeight = maxHeight + height + this.alternativePadWidth;
32241 //            
32242 //        }, this);
32243 //        
32244 //        this.el.setHeight(maxHeight);
32245 //        
32246 //    },
32247     
32248     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
32249     {
32250         var pos = this.el.getBox(true);
32251         
32252         var minX = pos.x;
32253         var minY = pos.y;
32254         
32255         var maxX = pos.right;
32256         
32257         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
32258         
32259         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32260         
32261         Roo.each(queue, function(box, k){
32262             
32263             Roo.each(box, function(b, kk){
32264                 
32265                 b.el.position('absolute');
32266                 
32267                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32268                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32269                 
32270                 if(b.size == 'md-left' || b.size == 'md-right'){
32271                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32272                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32273                 }
32274                 
32275                 b.el.setWidth(width);
32276                 b.el.setHeight(height);
32277                 
32278             }, this);
32279             
32280             if(!box.length){
32281                 return;
32282             }
32283             
32284             var positions = [];
32285             
32286             switch (box.length){
32287                 case 1 :
32288                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
32289                     break;
32290                 case 2 :
32291                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
32292                     break;
32293                 case 3 :
32294                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
32295                     break;
32296                 case 4 :
32297                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
32298                     break;
32299                 default :
32300                     break;
32301             }
32302             
32303             Roo.each(box, function(b,kk){
32304                 
32305                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32306                 
32307                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
32308                 
32309             }, this);
32310             
32311         }, this);
32312         
32313     },
32314     
32315     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
32316     {
32317         Roo.each(eItems, function(b,k){
32318             
32319             b.size = (k == 0) ? 'sm' : 'xs';
32320             b.x = (k == 0) ? 2 : 1;
32321             b.y = (k == 0) ? 2 : 1;
32322             
32323             b.el.position('absolute');
32324             
32325             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32326                 
32327             b.el.setWidth(width);
32328             
32329             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32330             
32331             b.el.setHeight(height);
32332             
32333         }, this);
32334
32335         var positions = [];
32336         
32337         positions.push({
32338             x : maxX - this.unitWidth * 2 - this.gutter,
32339             y : minY
32340         });
32341         
32342         positions.push({
32343             x : maxX - this.unitWidth,
32344             y : minY + (this.unitWidth + this.gutter) * 2
32345         });
32346         
32347         positions.push({
32348             x : maxX - this.unitWidth * 3 - this.gutter * 2,
32349             y : minY
32350         });
32351         
32352         Roo.each(eItems, function(b,k){
32353             
32354             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
32355
32356         }, this);
32357         
32358     },
32359     
32360     getVerticalOneBoxColPositions : function(x, y, box)
32361     {
32362         var pos = [];
32363         
32364         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32365         
32366         if(box[0].size == 'md-left'){
32367             rand = 0;
32368         }
32369         
32370         if(box[0].size == 'md-right'){
32371             rand = 1;
32372         }
32373         
32374         pos.push({
32375             x : x + (this.unitWidth + this.gutter) * rand,
32376             y : y
32377         });
32378         
32379         return pos;
32380     },
32381     
32382     getVerticalTwoBoxColPositions : function(x, y, box)
32383     {
32384         var pos = [];
32385         
32386         if(box[0].size == 'xs'){
32387             
32388             pos.push({
32389                 x : x,
32390                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32391             });
32392
32393             pos.push({
32394                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32395                 y : y
32396             });
32397             
32398             return pos;
32399             
32400         }
32401         
32402         pos.push({
32403             x : x,
32404             y : y
32405         });
32406
32407         pos.push({
32408             x : x + (this.unitWidth + this.gutter) * 2,
32409             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32410         });
32411         
32412         return pos;
32413         
32414     },
32415     
32416     getVerticalThreeBoxColPositions : function(x, y, box)
32417     {
32418         var pos = [];
32419         
32420         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32421             
32422             pos.push({
32423                 x : x,
32424                 y : y
32425             });
32426
32427             pos.push({
32428                 x : x + (this.unitWidth + this.gutter) * 1,
32429                 y : y
32430             });
32431             
32432             pos.push({
32433                 x : x + (this.unitWidth + this.gutter) * 2,
32434                 y : y
32435             });
32436             
32437             return pos;
32438             
32439         }
32440         
32441         if(box[0].size == 'xs' && box[1].size == 'xs'){
32442             
32443             pos.push({
32444                 x : x,
32445                 y : y
32446             });
32447
32448             pos.push({
32449                 x : x,
32450                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32451             });
32452             
32453             pos.push({
32454                 x : x + (this.unitWidth + this.gutter) * 1,
32455                 y : y
32456             });
32457             
32458             return pos;
32459             
32460         }
32461         
32462         pos.push({
32463             x : x,
32464             y : y
32465         });
32466
32467         pos.push({
32468             x : x + (this.unitWidth + this.gutter) * 2,
32469             y : y
32470         });
32471
32472         pos.push({
32473             x : x + (this.unitWidth + this.gutter) * 2,
32474             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32475         });
32476             
32477         return pos;
32478         
32479     },
32480     
32481     getVerticalFourBoxColPositions : function(x, y, box)
32482     {
32483         var pos = [];
32484         
32485         if(box[0].size == 'xs'){
32486             
32487             pos.push({
32488                 x : x,
32489                 y : y
32490             });
32491
32492             pos.push({
32493                 x : x,
32494                 y : y + (this.unitHeight + this.gutter) * 1
32495             });
32496             
32497             pos.push({
32498                 x : x,
32499                 y : y + (this.unitHeight + this.gutter) * 2
32500             });
32501             
32502             pos.push({
32503                 x : x + (this.unitWidth + this.gutter) * 1,
32504                 y : y
32505             });
32506             
32507             return pos;
32508             
32509         }
32510         
32511         pos.push({
32512             x : x,
32513             y : y
32514         });
32515
32516         pos.push({
32517             x : x + (this.unitWidth + this.gutter) * 2,
32518             y : y
32519         });
32520
32521         pos.push({
32522             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32523             y : y + (this.unitHeight + this.gutter) * 1
32524         });
32525
32526         pos.push({
32527             x : x + (this.unitWidth + this.gutter) * 2,
32528             y : y + (this.unitWidth + this.gutter) * 2
32529         });
32530
32531         return pos;
32532         
32533     },
32534     
32535     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32536     {
32537         var pos = [];
32538         
32539         if(box[0].size == 'md-left'){
32540             pos.push({
32541                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32542                 y : minY
32543             });
32544             
32545             return pos;
32546         }
32547         
32548         if(box[0].size == 'md-right'){
32549             pos.push({
32550                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32551                 y : minY + (this.unitWidth + this.gutter) * 1
32552             });
32553             
32554             return pos;
32555         }
32556         
32557         var rand = Math.floor(Math.random() * (4 - box[0].y));
32558         
32559         pos.push({
32560             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32561             y : minY + (this.unitWidth + this.gutter) * rand
32562         });
32563         
32564         return pos;
32565         
32566     },
32567     
32568     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32569     {
32570         var pos = [];
32571         
32572         if(box[0].size == 'xs'){
32573             
32574             pos.push({
32575                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32576                 y : minY
32577             });
32578
32579             pos.push({
32580                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32581                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32582             });
32583             
32584             return pos;
32585             
32586         }
32587         
32588         pos.push({
32589             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32590             y : minY
32591         });
32592
32593         pos.push({
32594             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32595             y : minY + (this.unitWidth + this.gutter) * 2
32596         });
32597         
32598         return pos;
32599         
32600     },
32601     
32602     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32603     {
32604         var pos = [];
32605         
32606         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32607             
32608             pos.push({
32609                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32610                 y : minY
32611             });
32612
32613             pos.push({
32614                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32615                 y : minY + (this.unitWidth + this.gutter) * 1
32616             });
32617             
32618             pos.push({
32619                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32620                 y : minY + (this.unitWidth + this.gutter) * 2
32621             });
32622             
32623             return pos;
32624             
32625         }
32626         
32627         if(box[0].size == 'xs' && box[1].size == 'xs'){
32628             
32629             pos.push({
32630                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32631                 y : minY
32632             });
32633
32634             pos.push({
32635                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32636                 y : minY
32637             });
32638             
32639             pos.push({
32640                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32641                 y : minY + (this.unitWidth + this.gutter) * 1
32642             });
32643             
32644             return pos;
32645             
32646         }
32647         
32648         pos.push({
32649             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32650             y : minY
32651         });
32652
32653         pos.push({
32654             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32655             y : minY + (this.unitWidth + this.gutter) * 2
32656         });
32657
32658         pos.push({
32659             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32660             y : minY + (this.unitWidth + this.gutter) * 2
32661         });
32662             
32663         return pos;
32664         
32665     },
32666     
32667     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32668     {
32669         var pos = [];
32670         
32671         if(box[0].size == 'xs'){
32672             
32673             pos.push({
32674                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32675                 y : minY
32676             });
32677
32678             pos.push({
32679                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32680                 y : minY
32681             });
32682             
32683             pos.push({
32684                 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),
32685                 y : minY
32686             });
32687             
32688             pos.push({
32689                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32690                 y : minY + (this.unitWidth + this.gutter) * 1
32691             });
32692             
32693             return pos;
32694             
32695         }
32696         
32697         pos.push({
32698             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32699             y : minY
32700         });
32701         
32702         pos.push({
32703             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32704             y : minY + (this.unitWidth + this.gutter) * 2
32705         });
32706         
32707         pos.push({
32708             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32709             y : minY + (this.unitWidth + this.gutter) * 2
32710         });
32711         
32712         pos.push({
32713             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),
32714             y : minY + (this.unitWidth + this.gutter) * 2
32715         });
32716
32717         return pos;
32718         
32719     },
32720     
32721     /**
32722     * remove a Masonry Brick
32723     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32724     */
32725     removeBrick : function(brick_id)
32726     {
32727         if (!brick_id) {
32728             return;
32729         }
32730         
32731         for (var i = 0; i<this.bricks.length; i++) {
32732             if (this.bricks[i].id == brick_id) {
32733                 this.bricks.splice(i,1);
32734                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32735                 this.initial();
32736             }
32737         }
32738     },
32739     
32740     /**
32741     * adds a Masonry Brick
32742     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32743     */
32744     addBrick : function(cfg)
32745     {
32746         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32747         //this.register(cn);
32748         cn.parentId = this.id;
32749         cn.render(this.el);
32750         return cn;
32751     },
32752     
32753     /**
32754     * register a Masonry Brick
32755     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32756     */
32757     
32758     register : function(brick)
32759     {
32760         this.bricks.push(brick);
32761         brick.masonryId = this.id;
32762     },
32763     
32764     /**
32765     * clear all the Masonry Brick
32766     */
32767     clearAll : function()
32768     {
32769         this.bricks = [];
32770         //this.getChildContainer().dom.innerHTML = "";
32771         this.el.dom.innerHTML = '';
32772     },
32773     
32774     getSelected : function()
32775     {
32776         if (!this.selectedBrick) {
32777             return false;
32778         }
32779         
32780         return this.selectedBrick;
32781     }
32782 });
32783
32784 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32785     
32786     groups: {},
32787      /**
32788     * register a Masonry Layout
32789     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32790     */
32791     
32792     register : function(layout)
32793     {
32794         this.groups[layout.id] = layout;
32795     },
32796     /**
32797     * fetch a  Masonry Layout based on the masonry layout ID
32798     * @param {string} the masonry layout to add
32799     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32800     */
32801     
32802     get: function(layout_id) {
32803         if (typeof(this.groups[layout_id]) == 'undefined') {
32804             return false;
32805         }
32806         return this.groups[layout_id] ;
32807     }
32808     
32809     
32810     
32811 });
32812
32813  
32814
32815  /**
32816  *
32817  * This is based on 
32818  * http://masonry.desandro.com
32819  *
32820  * The idea is to render all the bricks based on vertical width...
32821  *
32822  * The original code extends 'outlayer' - we might need to use that....
32823  * 
32824  */
32825
32826
32827 /**
32828  * @class Roo.bootstrap.LayoutMasonryAuto
32829  * @extends Roo.bootstrap.Component
32830  * Bootstrap Layout Masonry class
32831  * 
32832  * @constructor
32833  * Create a new Element
32834  * @param {Object} config The config object
32835  */
32836
32837 Roo.bootstrap.LayoutMasonryAuto = function(config){
32838     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32839 };
32840
32841 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32842     
32843       /**
32844      * @cfg {Boolean} isFitWidth  - resize the width..
32845      */   
32846     isFitWidth : false,  // options..
32847     /**
32848      * @cfg {Boolean} isOriginLeft = left align?
32849      */   
32850     isOriginLeft : true,
32851     /**
32852      * @cfg {Boolean} isOriginTop = top align?
32853      */   
32854     isOriginTop : false,
32855     /**
32856      * @cfg {Boolean} isLayoutInstant = no animation?
32857      */   
32858     isLayoutInstant : false, // needed?
32859     /**
32860      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32861      */   
32862     isResizingContainer : true,
32863     /**
32864      * @cfg {Number} columnWidth  width of the columns 
32865      */   
32866     
32867     columnWidth : 0,
32868     
32869     /**
32870      * @cfg {Number} maxCols maximum number of columns
32871      */   
32872     
32873     maxCols: 0,
32874     /**
32875      * @cfg {Number} padHeight padding below box..
32876      */   
32877     
32878     padHeight : 10, 
32879     
32880     /**
32881      * @cfg {Boolean} isAutoInitial defalut true
32882      */   
32883     
32884     isAutoInitial : true, 
32885     
32886     // private?
32887     gutter : 0,
32888     
32889     containerWidth: 0,
32890     initialColumnWidth : 0,
32891     currentSize : null,
32892     
32893     colYs : null, // array.
32894     maxY : 0,
32895     padWidth: 10,
32896     
32897     
32898     tag: 'div',
32899     cls: '',
32900     bricks: null, //CompositeElement
32901     cols : 0, // array?
32902     // element : null, // wrapped now this.el
32903     _isLayoutInited : null, 
32904     
32905     
32906     getAutoCreate : function(){
32907         
32908         var cfg = {
32909             tag: this.tag,
32910             cls: 'blog-masonary-wrapper ' + this.cls,
32911             cn : {
32912                 cls : 'mas-boxes masonary'
32913             }
32914         };
32915         
32916         return cfg;
32917     },
32918     
32919     getChildContainer: function( )
32920     {
32921         if (this.boxesEl) {
32922             return this.boxesEl;
32923         }
32924         
32925         this.boxesEl = this.el.select('.mas-boxes').first();
32926         
32927         return this.boxesEl;
32928     },
32929     
32930     
32931     initEvents : function()
32932     {
32933         var _this = this;
32934         
32935         if(this.isAutoInitial){
32936             Roo.log('hook children rendered');
32937             this.on('childrenrendered', function() {
32938                 Roo.log('children rendered');
32939                 _this.initial();
32940             } ,this);
32941         }
32942         
32943     },
32944     
32945     initial : function()
32946     {
32947         this.reloadItems();
32948
32949         this.currentSize = this.el.getBox(true);
32950
32951         /// was window resize... - let's see if this works..
32952         Roo.EventManager.onWindowResize(this.resize, this); 
32953
32954         if(!this.isAutoInitial){
32955             this.layout();
32956             return;
32957         }
32958         
32959         this.layout.defer(500,this);
32960     },
32961     
32962     reloadItems: function()
32963     {
32964         this.bricks = this.el.select('.masonry-brick', true);
32965         
32966         this.bricks.each(function(b) {
32967             //Roo.log(b.getSize());
32968             if (!b.attr('originalwidth')) {
32969                 b.attr('originalwidth',  b.getSize().width);
32970             }
32971             
32972         });
32973         
32974         Roo.log(this.bricks.elements.length);
32975     },
32976     
32977     resize : function()
32978     {
32979         Roo.log('resize');
32980         var cs = this.el.getBox(true);
32981         
32982         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32983             Roo.log("no change in with or X");
32984             return;
32985         }
32986         this.currentSize = cs;
32987         this.layout();
32988     },
32989     
32990     layout : function()
32991     {
32992          Roo.log('layout');
32993         this._resetLayout();
32994         //this._manageStamps();
32995       
32996         // don't animate first layout
32997         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32998         this.layoutItems( isInstant );
32999       
33000         // flag for initalized
33001         this._isLayoutInited = true;
33002     },
33003     
33004     layoutItems : function( isInstant )
33005     {
33006         //var items = this._getItemsForLayout( this.items );
33007         // original code supports filtering layout items.. we just ignore it..
33008         
33009         this._layoutItems( this.bricks , isInstant );
33010       
33011         this._postLayout();
33012     },
33013     _layoutItems : function ( items , isInstant)
33014     {
33015        //this.fireEvent( 'layout', this, items );
33016     
33017
33018         if ( !items || !items.elements.length ) {
33019           // no items, emit event with empty array
33020             return;
33021         }
33022
33023         var queue = [];
33024         items.each(function(item) {
33025             Roo.log("layout item");
33026             Roo.log(item);
33027             // get x/y object from method
33028             var position = this._getItemLayoutPosition( item );
33029             // enqueue
33030             position.item = item;
33031             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
33032             queue.push( position );
33033         }, this);
33034       
33035         this._processLayoutQueue( queue );
33036     },
33037     /** Sets position of item in DOM
33038     * @param {Element} item
33039     * @param {Number} x - horizontal position
33040     * @param {Number} y - vertical position
33041     * @param {Boolean} isInstant - disables transitions
33042     */
33043     _processLayoutQueue : function( queue )
33044     {
33045         for ( var i=0, len = queue.length; i < len; i++ ) {
33046             var obj = queue[i];
33047             obj.item.position('absolute');
33048             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
33049         }
33050     },
33051       
33052     
33053     /**
33054     * Any logic you want to do after each layout,
33055     * i.e. size the container
33056     */
33057     _postLayout : function()
33058     {
33059         this.resizeContainer();
33060     },
33061     
33062     resizeContainer : function()
33063     {
33064         if ( !this.isResizingContainer ) {
33065             return;
33066         }
33067         var size = this._getContainerSize();
33068         if ( size ) {
33069             this.el.setSize(size.width,size.height);
33070             this.boxesEl.setSize(size.width,size.height);
33071         }
33072     },
33073     
33074     
33075     
33076     _resetLayout : function()
33077     {
33078         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
33079         this.colWidth = this.el.getWidth();
33080         //this.gutter = this.el.getWidth(); 
33081         
33082         this.measureColumns();
33083
33084         // reset column Y
33085         var i = this.cols;
33086         this.colYs = [];
33087         while (i--) {
33088             this.colYs.push( 0 );
33089         }
33090     
33091         this.maxY = 0;
33092     },
33093
33094     measureColumns : function()
33095     {
33096         this.getContainerWidth();
33097       // if columnWidth is 0, default to outerWidth of first item
33098         if ( !this.columnWidth ) {
33099             var firstItem = this.bricks.first();
33100             Roo.log(firstItem);
33101             this.columnWidth  = this.containerWidth;
33102             if (firstItem && firstItem.attr('originalwidth') ) {
33103                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
33104             }
33105             // columnWidth fall back to item of first element
33106             Roo.log("set column width?");
33107                         this.initialColumnWidth = this.columnWidth  ;
33108
33109             // if first elem has no width, default to size of container
33110             
33111         }
33112         
33113         
33114         if (this.initialColumnWidth) {
33115             this.columnWidth = this.initialColumnWidth;
33116         }
33117         
33118         
33119             
33120         // column width is fixed at the top - however if container width get's smaller we should
33121         // reduce it...
33122         
33123         // this bit calcs how man columns..
33124             
33125         var columnWidth = this.columnWidth += this.gutter;
33126       
33127         // calculate columns
33128         var containerWidth = this.containerWidth + this.gutter;
33129         
33130         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
33131         // fix rounding errors, typically with gutters
33132         var excess = columnWidth - containerWidth % columnWidth;
33133         
33134         
33135         // if overshoot is less than a pixel, round up, otherwise floor it
33136         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
33137         cols = Math[ mathMethod ]( cols );
33138         this.cols = Math.max( cols, 1 );
33139         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33140         
33141          // padding positioning..
33142         var totalColWidth = this.cols * this.columnWidth;
33143         var padavail = this.containerWidth - totalColWidth;
33144         // so for 2 columns - we need 3 'pads'
33145         
33146         var padNeeded = (1+this.cols) * this.padWidth;
33147         
33148         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
33149         
33150         this.columnWidth += padExtra
33151         //this.padWidth = Math.floor(padavail /  ( this.cols));
33152         
33153         // adjust colum width so that padding is fixed??
33154         
33155         // we have 3 columns ... total = width * 3
33156         // we have X left over... that should be used by 
33157         
33158         //if (this.expandC) {
33159             
33160         //}
33161         
33162         
33163         
33164     },
33165     
33166     getContainerWidth : function()
33167     {
33168        /* // container is parent if fit width
33169         var container = this.isFitWidth ? this.element.parentNode : this.element;
33170         // check that this.size and size are there
33171         // IE8 triggers resize on body size change, so they might not be
33172         
33173         var size = getSize( container );  //FIXME
33174         this.containerWidth = size && size.innerWidth; //FIXME
33175         */
33176          
33177         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33178         
33179     },
33180     
33181     _getItemLayoutPosition : function( item )  // what is item?
33182     {
33183         // we resize the item to our columnWidth..
33184       
33185         item.setWidth(this.columnWidth);
33186         item.autoBoxAdjust  = false;
33187         
33188         var sz = item.getSize();
33189  
33190         // how many columns does this brick span
33191         var remainder = this.containerWidth % this.columnWidth;
33192         
33193         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
33194         // round if off by 1 pixel, otherwise use ceil
33195         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
33196         colSpan = Math.min( colSpan, this.cols );
33197         
33198         // normally this should be '1' as we dont' currently allow multi width columns..
33199         
33200         var colGroup = this._getColGroup( colSpan );
33201         // get the minimum Y value from the columns
33202         var minimumY = Math.min.apply( Math, colGroup );
33203         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
33204         
33205         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
33206          
33207         // position the brick
33208         var position = {
33209             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
33210             y: this.currentSize.y + minimumY + this.padHeight
33211         };
33212         
33213         Roo.log(position);
33214         // apply setHeight to necessary columns
33215         var setHeight = minimumY + sz.height + this.padHeight;
33216         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
33217         
33218         var setSpan = this.cols + 1 - colGroup.length;
33219         for ( var i = 0; i < setSpan; i++ ) {
33220           this.colYs[ shortColIndex + i ] = setHeight ;
33221         }
33222       
33223         return position;
33224     },
33225     
33226     /**
33227      * @param {Number} colSpan - number of columns the element spans
33228      * @returns {Array} colGroup
33229      */
33230     _getColGroup : function( colSpan )
33231     {
33232         if ( colSpan < 2 ) {
33233           // if brick spans only one column, use all the column Ys
33234           return this.colYs;
33235         }
33236       
33237         var colGroup = [];
33238         // how many different places could this brick fit horizontally
33239         var groupCount = this.cols + 1 - colSpan;
33240         // for each group potential horizontal position
33241         for ( var i = 0; i < groupCount; i++ ) {
33242           // make an array of colY values for that one group
33243           var groupColYs = this.colYs.slice( i, i + colSpan );
33244           // and get the max value of the array
33245           colGroup[i] = Math.max.apply( Math, groupColYs );
33246         }
33247         return colGroup;
33248     },
33249     /*
33250     _manageStamp : function( stamp )
33251     {
33252         var stampSize =  stamp.getSize();
33253         var offset = stamp.getBox();
33254         // get the columns that this stamp affects
33255         var firstX = this.isOriginLeft ? offset.x : offset.right;
33256         var lastX = firstX + stampSize.width;
33257         var firstCol = Math.floor( firstX / this.columnWidth );
33258         firstCol = Math.max( 0, firstCol );
33259         
33260         var lastCol = Math.floor( lastX / this.columnWidth );
33261         // lastCol should not go over if multiple of columnWidth #425
33262         lastCol -= lastX % this.columnWidth ? 0 : 1;
33263         lastCol = Math.min( this.cols - 1, lastCol );
33264         
33265         // set colYs to bottom of the stamp
33266         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
33267             stampSize.height;
33268             
33269         for ( var i = firstCol; i <= lastCol; i++ ) {
33270           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
33271         }
33272     },
33273     */
33274     
33275     _getContainerSize : function()
33276     {
33277         this.maxY = Math.max.apply( Math, this.colYs );
33278         var size = {
33279             height: this.maxY
33280         };
33281       
33282         if ( this.isFitWidth ) {
33283             size.width = this._getContainerFitWidth();
33284         }
33285       
33286         return size;
33287     },
33288     
33289     _getContainerFitWidth : function()
33290     {
33291         var unusedCols = 0;
33292         // count unused columns
33293         var i = this.cols;
33294         while ( --i ) {
33295           if ( this.colYs[i] !== 0 ) {
33296             break;
33297           }
33298           unusedCols++;
33299         }
33300         // fit container to columns that have been used
33301         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
33302     },
33303     
33304     needsResizeLayout : function()
33305     {
33306         var previousWidth = this.containerWidth;
33307         this.getContainerWidth();
33308         return previousWidth !== this.containerWidth;
33309     }
33310  
33311 });
33312
33313  
33314
33315  /*
33316  * - LGPL
33317  *
33318  * element
33319  * 
33320  */
33321
33322 /**
33323  * @class Roo.bootstrap.MasonryBrick
33324  * @extends Roo.bootstrap.Component
33325  * Bootstrap MasonryBrick class
33326  * 
33327  * @constructor
33328  * Create a new MasonryBrick
33329  * @param {Object} config The config object
33330  */
33331
33332 Roo.bootstrap.MasonryBrick = function(config){
33333     
33334     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
33335     
33336     Roo.bootstrap.MasonryBrick.register(this);
33337     
33338     this.addEvents({
33339         // raw events
33340         /**
33341          * @event click
33342          * When a MasonryBrick is clcik
33343          * @param {Roo.bootstrap.MasonryBrick} this
33344          * @param {Roo.EventObject} e
33345          */
33346         "click" : true
33347     });
33348 };
33349
33350 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
33351     
33352     /**
33353      * @cfg {String} title
33354      */   
33355     title : '',
33356     /**
33357      * @cfg {String} html
33358      */   
33359     html : '',
33360     /**
33361      * @cfg {String} bgimage
33362      */   
33363     bgimage : '',
33364     /**
33365      * @cfg {String} videourl
33366      */   
33367     videourl : '',
33368     /**
33369      * @cfg {String} cls
33370      */   
33371     cls : '',
33372     /**
33373      * @cfg {String} href
33374      */   
33375     href : '',
33376     /**
33377      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33378      */   
33379     size : 'xs',
33380     
33381     /**
33382      * @cfg {String} placetitle (center|bottom)
33383      */   
33384     placetitle : '',
33385     
33386     /**
33387      * @cfg {Boolean} isFitContainer defalut true
33388      */   
33389     isFitContainer : true, 
33390     
33391     /**
33392      * @cfg {Boolean} preventDefault defalut false
33393      */   
33394     preventDefault : false, 
33395     
33396     /**
33397      * @cfg {Boolean} inverse defalut false
33398      */   
33399     maskInverse : false, 
33400     
33401     getAutoCreate : function()
33402     {
33403         if(!this.isFitContainer){
33404             return this.getSplitAutoCreate();
33405         }
33406         
33407         var cls = 'masonry-brick masonry-brick-full';
33408         
33409         if(this.href.length){
33410             cls += ' masonry-brick-link';
33411         }
33412         
33413         if(this.bgimage.length){
33414             cls += ' masonry-brick-image';
33415         }
33416         
33417         if(this.maskInverse){
33418             cls += ' mask-inverse';
33419         }
33420         
33421         if(!this.html.length && !this.maskInverse && !this.videourl.length){
33422             cls += ' enable-mask';
33423         }
33424         
33425         if(this.size){
33426             cls += ' masonry-' + this.size + '-brick';
33427         }
33428         
33429         if(this.placetitle.length){
33430             
33431             switch (this.placetitle) {
33432                 case 'center' :
33433                     cls += ' masonry-center-title';
33434                     break;
33435                 case 'bottom' :
33436                     cls += ' masonry-bottom-title';
33437                     break;
33438                 default:
33439                     break;
33440             }
33441             
33442         } else {
33443             if(!this.html.length && !this.bgimage.length){
33444                 cls += ' masonry-center-title';
33445             }
33446
33447             if(!this.html.length && this.bgimage.length){
33448                 cls += ' masonry-bottom-title';
33449             }
33450         }
33451         
33452         if(this.cls){
33453             cls += ' ' + this.cls;
33454         }
33455         
33456         var cfg = {
33457             tag: (this.href.length) ? 'a' : 'div',
33458             cls: cls,
33459             cn: [
33460                 {
33461                     tag: 'div',
33462                     cls: 'masonry-brick-mask'
33463                 },
33464                 {
33465                     tag: 'div',
33466                     cls: 'masonry-brick-paragraph',
33467                     cn: []
33468                 }
33469             ]
33470         };
33471         
33472         if(this.href.length){
33473             cfg.href = this.href;
33474         }
33475         
33476         var cn = cfg.cn[1].cn;
33477         
33478         if(this.title.length){
33479             cn.push({
33480                 tag: 'h4',
33481                 cls: 'masonry-brick-title',
33482                 html: this.title
33483             });
33484         }
33485         
33486         if(this.html.length){
33487             cn.push({
33488                 tag: 'p',
33489                 cls: 'masonry-brick-text',
33490                 html: this.html
33491             });
33492         }
33493         
33494         if (!this.title.length && !this.html.length) {
33495             cfg.cn[1].cls += ' hide';
33496         }
33497         
33498         if(this.bgimage.length){
33499             cfg.cn.push({
33500                 tag: 'img',
33501                 cls: 'masonry-brick-image-view',
33502                 src: this.bgimage
33503             });
33504         }
33505         
33506         if(this.videourl.length){
33507             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33508             // youtube support only?
33509             cfg.cn.push({
33510                 tag: 'iframe',
33511                 cls: 'masonry-brick-image-view',
33512                 src: vurl,
33513                 frameborder : 0,
33514                 allowfullscreen : true
33515             });
33516         }
33517         
33518         return cfg;
33519         
33520     },
33521     
33522     getSplitAutoCreate : function()
33523     {
33524         var cls = 'masonry-brick masonry-brick-split';
33525         
33526         if(this.href.length){
33527             cls += ' masonry-brick-link';
33528         }
33529         
33530         if(this.bgimage.length){
33531             cls += ' masonry-brick-image';
33532         }
33533         
33534         if(this.size){
33535             cls += ' masonry-' + this.size + '-brick';
33536         }
33537         
33538         switch (this.placetitle) {
33539             case 'center' :
33540                 cls += ' masonry-center-title';
33541                 break;
33542             case 'bottom' :
33543                 cls += ' masonry-bottom-title';
33544                 break;
33545             default:
33546                 if(!this.bgimage.length){
33547                     cls += ' masonry-center-title';
33548                 }
33549
33550                 if(this.bgimage.length){
33551                     cls += ' masonry-bottom-title';
33552                 }
33553                 break;
33554         }
33555         
33556         if(this.cls){
33557             cls += ' ' + this.cls;
33558         }
33559         
33560         var cfg = {
33561             tag: (this.href.length) ? 'a' : 'div',
33562             cls: cls,
33563             cn: [
33564                 {
33565                     tag: 'div',
33566                     cls: 'masonry-brick-split-head',
33567                     cn: [
33568                         {
33569                             tag: 'div',
33570                             cls: 'masonry-brick-paragraph',
33571                             cn: []
33572                         }
33573                     ]
33574                 },
33575                 {
33576                     tag: 'div',
33577                     cls: 'masonry-brick-split-body',
33578                     cn: []
33579                 }
33580             ]
33581         };
33582         
33583         if(this.href.length){
33584             cfg.href = this.href;
33585         }
33586         
33587         if(this.title.length){
33588             cfg.cn[0].cn[0].cn.push({
33589                 tag: 'h4',
33590                 cls: 'masonry-brick-title',
33591                 html: this.title
33592             });
33593         }
33594         
33595         if(this.html.length){
33596             cfg.cn[1].cn.push({
33597                 tag: 'p',
33598                 cls: 'masonry-brick-text',
33599                 html: this.html
33600             });
33601         }
33602
33603         if(this.bgimage.length){
33604             cfg.cn[0].cn.push({
33605                 tag: 'img',
33606                 cls: 'masonry-brick-image-view',
33607                 src: this.bgimage
33608             });
33609         }
33610         
33611         if(this.videourl.length){
33612             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33613             // youtube support only?
33614             cfg.cn[0].cn.cn.push({
33615                 tag: 'iframe',
33616                 cls: 'masonry-brick-image-view',
33617                 src: vurl,
33618                 frameborder : 0,
33619                 allowfullscreen : true
33620             });
33621         }
33622         
33623         return cfg;
33624     },
33625     
33626     initEvents: function() 
33627     {
33628         switch (this.size) {
33629             case 'xs' :
33630                 this.x = 1;
33631                 this.y = 1;
33632                 break;
33633             case 'sm' :
33634                 this.x = 2;
33635                 this.y = 2;
33636                 break;
33637             case 'md' :
33638             case 'md-left' :
33639             case 'md-right' :
33640                 this.x = 3;
33641                 this.y = 3;
33642                 break;
33643             case 'tall' :
33644                 this.x = 2;
33645                 this.y = 3;
33646                 break;
33647             case 'wide' :
33648                 this.x = 3;
33649                 this.y = 2;
33650                 break;
33651             case 'wide-thin' :
33652                 this.x = 3;
33653                 this.y = 1;
33654                 break;
33655                         
33656             default :
33657                 break;
33658         }
33659         
33660         if(Roo.isTouch){
33661             this.el.on('touchstart', this.onTouchStart, this);
33662             this.el.on('touchmove', this.onTouchMove, this);
33663             this.el.on('touchend', this.onTouchEnd, this);
33664             this.el.on('contextmenu', this.onContextMenu, this);
33665         } else {
33666             this.el.on('mouseenter'  ,this.enter, this);
33667             this.el.on('mouseleave', this.leave, this);
33668             this.el.on('click', this.onClick, this);
33669         }
33670         
33671         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33672             this.parent().bricks.push(this);   
33673         }
33674         
33675     },
33676     
33677     onClick: function(e, el)
33678     {
33679         var time = this.endTimer - this.startTimer;
33680         // Roo.log(e.preventDefault());
33681         if(Roo.isTouch){
33682             if(time > 1000){
33683                 e.preventDefault();
33684                 return;
33685             }
33686         }
33687         
33688         if(!this.preventDefault){
33689             return;
33690         }
33691         
33692         e.preventDefault();
33693         
33694         if (this.activeClass != '') {
33695             this.selectBrick();
33696         }
33697         
33698         this.fireEvent('click', this, e);
33699     },
33700     
33701     enter: function(e, el)
33702     {
33703         e.preventDefault();
33704         
33705         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33706             return;
33707         }
33708         
33709         if(this.bgimage.length && this.html.length){
33710             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33711         }
33712     },
33713     
33714     leave: function(e, el)
33715     {
33716         e.preventDefault();
33717         
33718         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33719             return;
33720         }
33721         
33722         if(this.bgimage.length && this.html.length){
33723             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33724         }
33725     },
33726     
33727     onTouchStart: function(e, el)
33728     {
33729 //        e.preventDefault();
33730         
33731         this.touchmoved = false;
33732         
33733         if(!this.isFitContainer){
33734             return;
33735         }
33736         
33737         if(!this.bgimage.length || !this.html.length){
33738             return;
33739         }
33740         
33741         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33742         
33743         this.timer = new Date().getTime();
33744         
33745     },
33746     
33747     onTouchMove: function(e, el)
33748     {
33749         this.touchmoved = true;
33750     },
33751     
33752     onContextMenu : function(e,el)
33753     {
33754         e.preventDefault();
33755         e.stopPropagation();
33756         return false;
33757     },
33758     
33759     onTouchEnd: function(e, el)
33760     {
33761 //        e.preventDefault();
33762         
33763         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33764         
33765             this.leave(e,el);
33766             
33767             return;
33768         }
33769         
33770         if(!this.bgimage.length || !this.html.length){
33771             
33772             if(this.href.length){
33773                 window.location.href = this.href;
33774             }
33775             
33776             return;
33777         }
33778         
33779         if(!this.isFitContainer){
33780             return;
33781         }
33782         
33783         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33784         
33785         window.location.href = this.href;
33786     },
33787     
33788     //selection on single brick only
33789     selectBrick : function() {
33790         
33791         if (!this.parentId) {
33792             return;
33793         }
33794         
33795         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33796         var index = m.selectedBrick.indexOf(this.id);
33797         
33798         if ( index > -1) {
33799             m.selectedBrick.splice(index,1);
33800             this.el.removeClass(this.activeClass);
33801             return;
33802         }
33803         
33804         for(var i = 0; i < m.selectedBrick.length; i++) {
33805             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33806             b.el.removeClass(b.activeClass);
33807         }
33808         
33809         m.selectedBrick = [];
33810         
33811         m.selectedBrick.push(this.id);
33812         this.el.addClass(this.activeClass);
33813         return;
33814     },
33815     
33816     isSelected : function(){
33817         return this.el.hasClass(this.activeClass);
33818         
33819     }
33820 });
33821
33822 Roo.apply(Roo.bootstrap.MasonryBrick, {
33823     
33824     //groups: {},
33825     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33826      /**
33827     * register a Masonry Brick
33828     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33829     */
33830     
33831     register : function(brick)
33832     {
33833         //this.groups[brick.id] = brick;
33834         this.groups.add(brick.id, brick);
33835     },
33836     /**
33837     * fetch a  masonry brick based on the masonry brick ID
33838     * @param {string} the masonry brick to add
33839     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33840     */
33841     
33842     get: function(brick_id) 
33843     {
33844         // if (typeof(this.groups[brick_id]) == 'undefined') {
33845         //     return false;
33846         // }
33847         // return this.groups[brick_id] ;
33848         
33849         if(this.groups.key(brick_id)) {
33850             return this.groups.key(brick_id);
33851         }
33852         
33853         return false;
33854     }
33855     
33856     
33857     
33858 });
33859
33860  /*
33861  * - LGPL
33862  *
33863  * element
33864  * 
33865  */
33866
33867 /**
33868  * @class Roo.bootstrap.Brick
33869  * @extends Roo.bootstrap.Component
33870  * Bootstrap Brick class
33871  * 
33872  * @constructor
33873  * Create a new Brick
33874  * @param {Object} config The config object
33875  */
33876
33877 Roo.bootstrap.Brick = function(config){
33878     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33879     
33880     this.addEvents({
33881         // raw events
33882         /**
33883          * @event click
33884          * When a Brick is click
33885          * @param {Roo.bootstrap.Brick} this
33886          * @param {Roo.EventObject} e
33887          */
33888         "click" : true
33889     });
33890 };
33891
33892 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33893     
33894     /**
33895      * @cfg {String} title
33896      */   
33897     title : '',
33898     /**
33899      * @cfg {String} html
33900      */   
33901     html : '',
33902     /**
33903      * @cfg {String} bgimage
33904      */   
33905     bgimage : '',
33906     /**
33907      * @cfg {String} cls
33908      */   
33909     cls : '',
33910     /**
33911      * @cfg {String} href
33912      */   
33913     href : '',
33914     /**
33915      * @cfg {String} video
33916      */   
33917     video : '',
33918     /**
33919      * @cfg {Boolean} square
33920      */   
33921     square : true,
33922     
33923     getAutoCreate : function()
33924     {
33925         var cls = 'roo-brick';
33926         
33927         if(this.href.length){
33928             cls += ' roo-brick-link';
33929         }
33930         
33931         if(this.bgimage.length){
33932             cls += ' roo-brick-image';
33933         }
33934         
33935         if(!this.html.length && !this.bgimage.length){
33936             cls += ' roo-brick-center-title';
33937         }
33938         
33939         if(!this.html.length && this.bgimage.length){
33940             cls += ' roo-brick-bottom-title';
33941         }
33942         
33943         if(this.cls){
33944             cls += ' ' + this.cls;
33945         }
33946         
33947         var cfg = {
33948             tag: (this.href.length) ? 'a' : 'div',
33949             cls: cls,
33950             cn: [
33951                 {
33952                     tag: 'div',
33953                     cls: 'roo-brick-paragraph',
33954                     cn: []
33955                 }
33956             ]
33957         };
33958         
33959         if(this.href.length){
33960             cfg.href = this.href;
33961         }
33962         
33963         var cn = cfg.cn[0].cn;
33964         
33965         if(this.title.length){
33966             cn.push({
33967                 tag: 'h4',
33968                 cls: 'roo-brick-title',
33969                 html: this.title
33970             });
33971         }
33972         
33973         if(this.html.length){
33974             cn.push({
33975                 tag: 'p',
33976                 cls: 'roo-brick-text',
33977                 html: this.html
33978             });
33979         } else {
33980             cn.cls += ' hide';
33981         }
33982         
33983         if(this.bgimage.length){
33984             cfg.cn.push({
33985                 tag: 'img',
33986                 cls: 'roo-brick-image-view',
33987                 src: this.bgimage
33988             });
33989         }
33990         
33991         return cfg;
33992     },
33993     
33994     initEvents: function() 
33995     {
33996         if(this.title.length || this.html.length){
33997             this.el.on('mouseenter'  ,this.enter, this);
33998             this.el.on('mouseleave', this.leave, this);
33999         }
34000         
34001         Roo.EventManager.onWindowResize(this.resize, this); 
34002         
34003         if(this.bgimage.length){
34004             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
34005             this.imageEl.on('load', this.onImageLoad, this);
34006             return;
34007         }
34008         
34009         this.resize();
34010     },
34011     
34012     onImageLoad : function()
34013     {
34014         this.resize();
34015     },
34016     
34017     resize : function()
34018     {
34019         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
34020         
34021         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
34022         
34023         if(this.bgimage.length){
34024             var image = this.el.select('.roo-brick-image-view', true).first();
34025             
34026             image.setWidth(paragraph.getWidth());
34027             
34028             if(this.square){
34029                 image.setHeight(paragraph.getWidth());
34030             }
34031             
34032             this.el.setHeight(image.getHeight());
34033             paragraph.setHeight(image.getHeight());
34034             
34035         }
34036         
34037     },
34038     
34039     enter: function(e, el)
34040     {
34041         e.preventDefault();
34042         
34043         if(this.bgimage.length){
34044             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
34045             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
34046         }
34047     },
34048     
34049     leave: function(e, el)
34050     {
34051         e.preventDefault();
34052         
34053         if(this.bgimage.length){
34054             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
34055             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
34056         }
34057     }
34058     
34059 });
34060
34061  
34062
34063  /*
34064  * - LGPL
34065  *
34066  * Number field 
34067  */
34068
34069 /**
34070  * @class Roo.bootstrap.NumberField
34071  * @extends Roo.bootstrap.Input
34072  * Bootstrap NumberField class
34073  * 
34074  * 
34075  * 
34076  * 
34077  * @constructor
34078  * Create a new NumberField
34079  * @param {Object} config The config object
34080  */
34081
34082 Roo.bootstrap.NumberField = function(config){
34083     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
34084 };
34085
34086 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
34087     
34088     /**
34089      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
34090      */
34091     allowDecimals : true,
34092     /**
34093      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
34094      */
34095     decimalSeparator : ".",
34096     /**
34097      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
34098      */
34099     decimalPrecision : 2,
34100     /**
34101      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
34102      */
34103     allowNegative : true,
34104     
34105     /**
34106      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
34107      */
34108     allowZero: true,
34109     /**
34110      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
34111      */
34112     minValue : Number.NEGATIVE_INFINITY,
34113     /**
34114      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
34115      */
34116     maxValue : Number.MAX_VALUE,
34117     /**
34118      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
34119      */
34120     minText : "The minimum value for this field is {0}",
34121     /**
34122      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
34123      */
34124     maxText : "The maximum value for this field is {0}",
34125     /**
34126      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
34127      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
34128      */
34129     nanText : "{0} is not a valid number",
34130     /**
34131      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
34132      */
34133     thousandsDelimiter : false,
34134     /**
34135      * @cfg {String} valueAlign alignment of value
34136      */
34137     valueAlign : "left",
34138
34139     getAutoCreate : function()
34140     {
34141         var hiddenInput = {
34142             tag: 'input',
34143             type: 'hidden',
34144             id: Roo.id(),
34145             cls: 'hidden-number-input'
34146         };
34147         
34148         if (this.name) {
34149             hiddenInput.name = this.name;
34150         }
34151         
34152         this.name = '';
34153         
34154         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
34155         
34156         this.name = hiddenInput.name;
34157         
34158         if(cfg.cn.length > 0) {
34159             cfg.cn.push(hiddenInput);
34160         }
34161         
34162         return cfg;
34163     },
34164
34165     // private
34166     initEvents : function()
34167     {   
34168         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
34169         
34170         var allowed = "0123456789";
34171         
34172         if(this.allowDecimals){
34173             allowed += this.decimalSeparator;
34174         }
34175         
34176         if(this.allowNegative){
34177             allowed += "-";
34178         }
34179         
34180         if(this.thousandsDelimiter) {
34181             allowed += ",";
34182         }
34183         
34184         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
34185         
34186         var keyPress = function(e){
34187             
34188             var k = e.getKey();
34189             
34190             var c = e.getCharCode();
34191             
34192             if(
34193                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
34194                     allowed.indexOf(String.fromCharCode(c)) === -1
34195             ){
34196                 e.stopEvent();
34197                 return;
34198             }
34199             
34200             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
34201                 return;
34202             }
34203             
34204             if(allowed.indexOf(String.fromCharCode(c)) === -1){
34205                 e.stopEvent();
34206             }
34207         };
34208         
34209         this.el.on("keypress", keyPress, this);
34210     },
34211     
34212     validateValue : function(value)
34213     {
34214         
34215         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
34216             return false;
34217         }
34218         
34219         var num = this.parseValue(value);
34220         
34221         if(isNaN(num)){
34222             this.markInvalid(String.format(this.nanText, value));
34223             return false;
34224         }
34225         
34226         if(num < this.minValue){
34227             this.markInvalid(String.format(this.minText, this.minValue));
34228             return false;
34229         }
34230         
34231         if(num > this.maxValue){
34232             this.markInvalid(String.format(this.maxText, this.maxValue));
34233             return false;
34234         }
34235         
34236         return true;
34237     },
34238
34239     getValue : function()
34240     {
34241         var v = this.hiddenEl().getValue();
34242         
34243         return this.fixPrecision(this.parseValue(v));
34244     },
34245
34246     parseValue : function(value)
34247     {
34248         if(this.thousandsDelimiter) {
34249             value += "";
34250             r = new RegExp(",", "g");
34251             value = value.replace(r, "");
34252         }
34253         
34254         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
34255         return isNaN(value) ? '' : value;
34256     },
34257
34258     fixPrecision : function(value)
34259     {
34260         if(this.thousandsDelimiter) {
34261             value += "";
34262             r = new RegExp(",", "g");
34263             value = value.replace(r, "");
34264         }
34265         
34266         var nan = isNaN(value);
34267         
34268         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
34269             return nan ? '' : value;
34270         }
34271         return parseFloat(value).toFixed(this.decimalPrecision);
34272     },
34273
34274     setValue : function(v)
34275     {
34276         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
34277         
34278         this.value = v;
34279         
34280         if(this.rendered){
34281             
34282             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
34283             
34284             this.inputEl().dom.value = (v == '') ? '' :
34285                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
34286             
34287             if(!this.allowZero && v === '0') {
34288                 this.hiddenEl().dom.value = '';
34289                 this.inputEl().dom.value = '';
34290             }
34291             
34292             this.validate();
34293         }
34294     },
34295
34296     decimalPrecisionFcn : function(v)
34297     {
34298         return Math.floor(v);
34299     },
34300
34301     beforeBlur : function()
34302     {
34303         var v = this.parseValue(this.getRawValue());
34304         
34305         if(v || v === 0 || v === ''){
34306             this.setValue(v);
34307         }
34308     },
34309     
34310     hiddenEl : function()
34311     {
34312         return this.el.select('input.hidden-number-input',true).first();
34313     }
34314     
34315 });
34316
34317  
34318
34319 /*
34320 * Licence: LGPL
34321 */
34322
34323 /**
34324  * @class Roo.bootstrap.DocumentSlider
34325  * @extends Roo.bootstrap.Component
34326  * Bootstrap DocumentSlider class
34327  * 
34328  * @constructor
34329  * Create a new DocumentViewer
34330  * @param {Object} config The config object
34331  */
34332
34333 Roo.bootstrap.DocumentSlider = function(config){
34334     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
34335     
34336     this.files = [];
34337     
34338     this.addEvents({
34339         /**
34340          * @event initial
34341          * Fire after initEvent
34342          * @param {Roo.bootstrap.DocumentSlider} this
34343          */
34344         "initial" : true,
34345         /**
34346          * @event update
34347          * Fire after update
34348          * @param {Roo.bootstrap.DocumentSlider} this
34349          */
34350         "update" : true,
34351         /**
34352          * @event click
34353          * Fire after click
34354          * @param {Roo.bootstrap.DocumentSlider} this
34355          */
34356         "click" : true
34357     });
34358 };
34359
34360 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
34361     
34362     files : false,
34363     
34364     indicator : 0,
34365     
34366     getAutoCreate : function()
34367     {
34368         var cfg = {
34369             tag : 'div',
34370             cls : 'roo-document-slider',
34371             cn : [
34372                 {
34373                     tag : 'div',
34374                     cls : 'roo-document-slider-header',
34375                     cn : [
34376                         {
34377                             tag : 'div',
34378                             cls : 'roo-document-slider-header-title'
34379                         }
34380                     ]
34381                 },
34382                 {
34383                     tag : 'div',
34384                     cls : 'roo-document-slider-body',
34385                     cn : [
34386                         {
34387                             tag : 'div',
34388                             cls : 'roo-document-slider-prev',
34389                             cn : [
34390                                 {
34391                                     tag : 'i',
34392                                     cls : 'fa fa-chevron-left'
34393                                 }
34394                             ]
34395                         },
34396                         {
34397                             tag : 'div',
34398                             cls : 'roo-document-slider-thumb',
34399                             cn : [
34400                                 {
34401                                     tag : 'img',
34402                                     cls : 'roo-document-slider-image'
34403                                 }
34404                             ]
34405                         },
34406                         {
34407                             tag : 'div',
34408                             cls : 'roo-document-slider-next',
34409                             cn : [
34410                                 {
34411                                     tag : 'i',
34412                                     cls : 'fa fa-chevron-right'
34413                                 }
34414                             ]
34415                         }
34416                     ]
34417                 }
34418             ]
34419         };
34420         
34421         return cfg;
34422     },
34423     
34424     initEvents : function()
34425     {
34426         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34427         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34428         
34429         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34430         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34431         
34432         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34433         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34434         
34435         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34436         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34437         
34438         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34439         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34440         
34441         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34442         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34443         
34444         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34445         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34446         
34447         this.thumbEl.on('click', this.onClick, this);
34448         
34449         this.prevIndicator.on('click', this.prev, this);
34450         
34451         this.nextIndicator.on('click', this.next, this);
34452         
34453     },
34454     
34455     initial : function()
34456     {
34457         if(this.files.length){
34458             this.indicator = 1;
34459             this.update()
34460         }
34461         
34462         this.fireEvent('initial', this);
34463     },
34464     
34465     update : function()
34466     {
34467         this.imageEl.attr('src', this.files[this.indicator - 1]);
34468         
34469         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34470         
34471         this.prevIndicator.show();
34472         
34473         if(this.indicator == 1){
34474             this.prevIndicator.hide();
34475         }
34476         
34477         this.nextIndicator.show();
34478         
34479         if(this.indicator == this.files.length){
34480             this.nextIndicator.hide();
34481         }
34482         
34483         this.thumbEl.scrollTo('top');
34484         
34485         this.fireEvent('update', this);
34486     },
34487     
34488     onClick : function(e)
34489     {
34490         e.preventDefault();
34491         
34492         this.fireEvent('click', this);
34493     },
34494     
34495     prev : function(e)
34496     {
34497         e.preventDefault();
34498         
34499         this.indicator = Math.max(1, this.indicator - 1);
34500         
34501         this.update();
34502     },
34503     
34504     next : function(e)
34505     {
34506         e.preventDefault();
34507         
34508         this.indicator = Math.min(this.files.length, this.indicator + 1);
34509         
34510         this.update();
34511     }
34512 });
34513 /*
34514  * - LGPL
34515  *
34516  * RadioSet
34517  *
34518  *
34519  */
34520
34521 /**
34522  * @class Roo.bootstrap.RadioSet
34523  * @extends Roo.bootstrap.Input
34524  * Bootstrap RadioSet class
34525  * @cfg {String} indicatorpos (left|right) default left
34526  * @cfg {Boolean} inline (true|false) inline the element (default true)
34527  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34528  * @constructor
34529  * Create a new RadioSet
34530  * @param {Object} config The config object
34531  */
34532
34533 Roo.bootstrap.RadioSet = function(config){
34534     
34535     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34536     
34537     this.radioes = [];
34538     
34539     Roo.bootstrap.RadioSet.register(this);
34540     
34541     this.addEvents({
34542         /**
34543         * @event check
34544         * Fires when the element is checked or unchecked.
34545         * @param {Roo.bootstrap.RadioSet} this This radio
34546         * @param {Roo.bootstrap.Radio} item The checked item
34547         */
34548        check : true,
34549        /**
34550         * @event click
34551         * Fires when the element is click.
34552         * @param {Roo.bootstrap.RadioSet} this This radio set
34553         * @param {Roo.bootstrap.Radio} item The checked item
34554         * @param {Roo.EventObject} e The event object
34555         */
34556        click : true
34557     });
34558     
34559 };
34560
34561 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34562
34563     radioes : false,
34564     
34565     inline : true,
34566     
34567     weight : '',
34568     
34569     indicatorpos : 'left',
34570     
34571     getAutoCreate : function()
34572     {
34573         var label = {
34574             tag : 'label',
34575             cls : 'roo-radio-set-label',
34576             cn : [
34577                 {
34578                     tag : 'span',
34579                     html : this.fieldLabel
34580                 }
34581             ]
34582         };
34583         if (Roo.bootstrap.version == 3) {
34584             
34585             
34586             if(this.indicatorpos == 'left'){
34587                 label.cn.unshift({
34588                     tag : 'i',
34589                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34590                     tooltip : 'This field is required'
34591                 });
34592             } else {
34593                 label.cn.push({
34594                     tag : 'i',
34595                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34596                     tooltip : 'This field is required'
34597                 });
34598             }
34599         }
34600         var items = {
34601             tag : 'div',
34602             cls : 'roo-radio-set-items'
34603         };
34604         
34605         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34606         
34607         if (align === 'left' && this.fieldLabel.length) {
34608             
34609             items = {
34610                 cls : "roo-radio-set-right", 
34611                 cn: [
34612                     items
34613                 ]
34614             };
34615             
34616             if(this.labelWidth > 12){
34617                 label.style = "width: " + this.labelWidth + 'px';
34618             }
34619             
34620             if(this.labelWidth < 13 && this.labelmd == 0){
34621                 this.labelmd = this.labelWidth;
34622             }
34623             
34624             if(this.labellg > 0){
34625                 label.cls += ' col-lg-' + this.labellg;
34626                 items.cls += ' col-lg-' + (12 - this.labellg);
34627             }
34628             
34629             if(this.labelmd > 0){
34630                 label.cls += ' col-md-' + this.labelmd;
34631                 items.cls += ' col-md-' + (12 - this.labelmd);
34632             }
34633             
34634             if(this.labelsm > 0){
34635                 label.cls += ' col-sm-' + this.labelsm;
34636                 items.cls += ' col-sm-' + (12 - this.labelsm);
34637             }
34638             
34639             if(this.labelxs > 0){
34640                 label.cls += ' col-xs-' + this.labelxs;
34641                 items.cls += ' col-xs-' + (12 - this.labelxs);
34642             }
34643         }
34644         
34645         var cfg = {
34646             tag : 'div',
34647             cls : 'roo-radio-set',
34648             cn : [
34649                 {
34650                     tag : 'input',
34651                     cls : 'roo-radio-set-input',
34652                     type : 'hidden',
34653                     name : this.name,
34654                     value : this.value ? this.value :  ''
34655                 },
34656                 label,
34657                 items
34658             ]
34659         };
34660         
34661         if(this.weight.length){
34662             cfg.cls += ' roo-radio-' + this.weight;
34663         }
34664         
34665         if(this.inline) {
34666             cfg.cls += ' roo-radio-set-inline';
34667         }
34668         
34669         var settings=this;
34670         ['xs','sm','md','lg'].map(function(size){
34671             if (settings[size]) {
34672                 cfg.cls += ' col-' + size + '-' + settings[size];
34673             }
34674         });
34675         
34676         return cfg;
34677         
34678     },
34679
34680     initEvents : function()
34681     {
34682         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34683         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34684         
34685         if(!this.fieldLabel.length){
34686             this.labelEl.hide();
34687         }
34688         
34689         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34690         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34691         
34692         this.indicator = this.indicatorEl();
34693         
34694         if(this.indicator){
34695             this.indicator.addClass('invisible');
34696         }
34697         
34698         this.originalValue = this.getValue();
34699         
34700     },
34701     
34702     inputEl: function ()
34703     {
34704         return this.el.select('.roo-radio-set-input', true).first();
34705     },
34706     
34707     getChildContainer : function()
34708     {
34709         return this.itemsEl;
34710     },
34711     
34712     register : function(item)
34713     {
34714         this.radioes.push(item);
34715         
34716     },
34717     
34718     validate : function()
34719     {   
34720         if(this.getVisibilityEl().hasClass('hidden')){
34721             return true;
34722         }
34723         
34724         var valid = false;
34725         
34726         Roo.each(this.radioes, function(i){
34727             if(!i.checked){
34728                 return;
34729             }
34730             
34731             valid = true;
34732             return false;
34733         });
34734         
34735         if(this.allowBlank) {
34736             return true;
34737         }
34738         
34739         if(this.disabled || valid){
34740             this.markValid();
34741             return true;
34742         }
34743         
34744         this.markInvalid();
34745         return false;
34746         
34747     },
34748     
34749     markValid : function()
34750     {
34751         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34752             this.indicatorEl().removeClass('visible');
34753             this.indicatorEl().addClass('invisible');
34754         }
34755         
34756         
34757         if (Roo.bootstrap.version == 3) {
34758             this.el.removeClass([this.invalidClass, this.validClass]);
34759             this.el.addClass(this.validClass);
34760         } else {
34761             this.el.removeClass(['is-invalid','is-valid']);
34762             this.el.addClass(['is-valid']);
34763         }
34764         this.fireEvent('valid', this);
34765     },
34766     
34767     markInvalid : function(msg)
34768     {
34769         if(this.allowBlank || this.disabled){
34770             return;
34771         }
34772         
34773         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34774             this.indicatorEl().removeClass('invisible');
34775             this.indicatorEl().addClass('visible');
34776         }
34777         if (Roo.bootstrap.version == 3) {
34778             this.el.removeClass([this.invalidClass, this.validClass]);
34779             this.el.addClass(this.invalidClass);
34780         } else {
34781             this.el.removeClass(['is-invalid','is-valid']);
34782             this.el.addClass(['is-invalid']);
34783         }
34784         
34785         this.fireEvent('invalid', this, msg);
34786         
34787     },
34788     
34789     setValue : function(v, suppressEvent)
34790     {   
34791         if(this.value === v){
34792             return;
34793         }
34794         
34795         this.value = v;
34796         
34797         if(this.rendered){
34798             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34799         }
34800         
34801         Roo.each(this.radioes, function(i){
34802             i.checked = false;
34803             i.el.removeClass('checked');
34804         });
34805         
34806         Roo.each(this.radioes, function(i){
34807             
34808             if(i.value === v || i.value.toString() === v.toString()){
34809                 i.checked = true;
34810                 i.el.addClass('checked');
34811                 
34812                 if(suppressEvent !== true){
34813                     this.fireEvent('check', this, i);
34814                 }
34815                 
34816                 return false;
34817             }
34818             
34819         }, this);
34820         
34821         this.validate();
34822     },
34823     
34824     clearInvalid : function(){
34825         
34826         if(!this.el || this.preventMark){
34827             return;
34828         }
34829         
34830         this.el.removeClass([this.invalidClass]);
34831         
34832         this.fireEvent('valid', this);
34833     }
34834     
34835 });
34836
34837 Roo.apply(Roo.bootstrap.RadioSet, {
34838     
34839     groups: {},
34840     
34841     register : function(set)
34842     {
34843         this.groups[set.name] = set;
34844     },
34845     
34846     get: function(name) 
34847     {
34848         if (typeof(this.groups[name]) == 'undefined') {
34849             return false;
34850         }
34851         
34852         return this.groups[name] ;
34853     }
34854     
34855 });
34856 /*
34857  * Based on:
34858  * Ext JS Library 1.1.1
34859  * Copyright(c) 2006-2007, Ext JS, LLC.
34860  *
34861  * Originally Released Under LGPL - original licence link has changed is not relivant.
34862  *
34863  * Fork - LGPL
34864  * <script type="text/javascript">
34865  */
34866
34867
34868 /**
34869  * @class Roo.bootstrap.SplitBar
34870  * @extends Roo.util.Observable
34871  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34872  * <br><br>
34873  * Usage:
34874  * <pre><code>
34875 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34876                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34877 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34878 split.minSize = 100;
34879 split.maxSize = 600;
34880 split.animate = true;
34881 split.on('moved', splitterMoved);
34882 </code></pre>
34883  * @constructor
34884  * Create a new SplitBar
34885  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34886  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34887  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34888  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34889                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34890                         position of the SplitBar).
34891  */
34892 Roo.bootstrap.SplitBar = function(cfg){
34893     
34894     /** @private */
34895     
34896     //{
34897     //  dragElement : elm
34898     //  resizingElement: el,
34899         // optional..
34900     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34901     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34902         // existingProxy ???
34903     //}
34904     
34905     this.el = Roo.get(cfg.dragElement, true);
34906     this.el.dom.unselectable = "on";
34907     /** @private */
34908     this.resizingEl = Roo.get(cfg.resizingElement, true);
34909
34910     /**
34911      * @private
34912      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34913      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34914      * @type Number
34915      */
34916     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34917     
34918     /**
34919      * The minimum size of the resizing element. (Defaults to 0)
34920      * @type Number
34921      */
34922     this.minSize = 0;
34923     
34924     /**
34925      * The maximum size of the resizing element. (Defaults to 2000)
34926      * @type Number
34927      */
34928     this.maxSize = 2000;
34929     
34930     /**
34931      * Whether to animate the transition to the new size
34932      * @type Boolean
34933      */
34934     this.animate = false;
34935     
34936     /**
34937      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34938      * @type Boolean
34939      */
34940     this.useShim = false;
34941     
34942     /** @private */
34943     this.shim = null;
34944     
34945     if(!cfg.existingProxy){
34946         /** @private */
34947         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34948     }else{
34949         this.proxy = Roo.get(cfg.existingProxy).dom;
34950     }
34951     /** @private */
34952     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34953     
34954     /** @private */
34955     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34956     
34957     /** @private */
34958     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34959     
34960     /** @private */
34961     this.dragSpecs = {};
34962     
34963     /**
34964      * @private The adapter to use to positon and resize elements
34965      */
34966     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34967     this.adapter.init(this);
34968     
34969     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34970         /** @private */
34971         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34972         this.el.addClass("roo-splitbar-h");
34973     }else{
34974         /** @private */
34975         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34976         this.el.addClass("roo-splitbar-v");
34977     }
34978     
34979     this.addEvents({
34980         /**
34981          * @event resize
34982          * Fires when the splitter is moved (alias for {@link #event-moved})
34983          * @param {Roo.bootstrap.SplitBar} this
34984          * @param {Number} newSize the new width or height
34985          */
34986         "resize" : true,
34987         /**
34988          * @event moved
34989          * Fires when the splitter is moved
34990          * @param {Roo.bootstrap.SplitBar} this
34991          * @param {Number} newSize the new width or height
34992          */
34993         "moved" : true,
34994         /**
34995          * @event beforeresize
34996          * Fires before the splitter is dragged
34997          * @param {Roo.bootstrap.SplitBar} this
34998          */
34999         "beforeresize" : true,
35000
35001         "beforeapply" : true
35002     });
35003
35004     Roo.util.Observable.call(this);
35005 };
35006
35007 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
35008     onStartProxyDrag : function(x, y){
35009         this.fireEvent("beforeresize", this);
35010         if(!this.overlay){
35011             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
35012             o.unselectable();
35013             o.enableDisplayMode("block");
35014             // all splitbars share the same overlay
35015             Roo.bootstrap.SplitBar.prototype.overlay = o;
35016         }
35017         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
35018         this.overlay.show();
35019         Roo.get(this.proxy).setDisplayed("block");
35020         var size = this.adapter.getElementSize(this);
35021         this.activeMinSize = this.getMinimumSize();;
35022         this.activeMaxSize = this.getMaximumSize();;
35023         var c1 = size - this.activeMinSize;
35024         var c2 = Math.max(this.activeMaxSize - size, 0);
35025         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35026             this.dd.resetConstraints();
35027             this.dd.setXConstraint(
35028                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
35029                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
35030             );
35031             this.dd.setYConstraint(0, 0);
35032         }else{
35033             this.dd.resetConstraints();
35034             this.dd.setXConstraint(0, 0);
35035             this.dd.setYConstraint(
35036                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
35037                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
35038             );
35039          }
35040         this.dragSpecs.startSize = size;
35041         this.dragSpecs.startPoint = [x, y];
35042         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
35043     },
35044     
35045     /** 
35046      * @private Called after the drag operation by the DDProxy
35047      */
35048     onEndProxyDrag : function(e){
35049         Roo.get(this.proxy).setDisplayed(false);
35050         var endPoint = Roo.lib.Event.getXY(e);
35051         if(this.overlay){
35052             this.overlay.hide();
35053         }
35054         var newSize;
35055         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35056             newSize = this.dragSpecs.startSize + 
35057                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
35058                     endPoint[0] - this.dragSpecs.startPoint[0] :
35059                     this.dragSpecs.startPoint[0] - endPoint[0]
35060                 );
35061         }else{
35062             newSize = this.dragSpecs.startSize + 
35063                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
35064                     endPoint[1] - this.dragSpecs.startPoint[1] :
35065                     this.dragSpecs.startPoint[1] - endPoint[1]
35066                 );
35067         }
35068         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
35069         if(newSize != this.dragSpecs.startSize){
35070             if(this.fireEvent('beforeapply', this, newSize) !== false){
35071                 this.adapter.setElementSize(this, newSize);
35072                 this.fireEvent("moved", this, newSize);
35073                 this.fireEvent("resize", this, newSize);
35074             }
35075         }
35076     },
35077     
35078     /**
35079      * Get the adapter this SplitBar uses
35080      * @return The adapter object
35081      */
35082     getAdapter : function(){
35083         return this.adapter;
35084     },
35085     
35086     /**
35087      * Set the adapter this SplitBar uses
35088      * @param {Object} adapter A SplitBar adapter object
35089      */
35090     setAdapter : function(adapter){
35091         this.adapter = adapter;
35092         this.adapter.init(this);
35093     },
35094     
35095     /**
35096      * Gets the minimum size for the resizing element
35097      * @return {Number} The minimum size
35098      */
35099     getMinimumSize : function(){
35100         return this.minSize;
35101     },
35102     
35103     /**
35104      * Sets the minimum size for the resizing element
35105      * @param {Number} minSize The minimum size
35106      */
35107     setMinimumSize : function(minSize){
35108         this.minSize = minSize;
35109     },
35110     
35111     /**
35112      * Gets the maximum size for the resizing element
35113      * @return {Number} The maximum size
35114      */
35115     getMaximumSize : function(){
35116         return this.maxSize;
35117     },
35118     
35119     /**
35120      * Sets the maximum size for the resizing element
35121      * @param {Number} maxSize The maximum size
35122      */
35123     setMaximumSize : function(maxSize){
35124         this.maxSize = maxSize;
35125     },
35126     
35127     /**
35128      * Sets the initialize size for the resizing element
35129      * @param {Number} size The initial size
35130      */
35131     setCurrentSize : function(size){
35132         var oldAnimate = this.animate;
35133         this.animate = false;
35134         this.adapter.setElementSize(this, size);
35135         this.animate = oldAnimate;
35136     },
35137     
35138     /**
35139      * Destroy this splitbar. 
35140      * @param {Boolean} removeEl True to remove the element
35141      */
35142     destroy : function(removeEl){
35143         if(this.shim){
35144             this.shim.remove();
35145         }
35146         this.dd.unreg();
35147         this.proxy.parentNode.removeChild(this.proxy);
35148         if(removeEl){
35149             this.el.remove();
35150         }
35151     }
35152 });
35153
35154 /**
35155  * @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.
35156  */
35157 Roo.bootstrap.SplitBar.createProxy = function(dir){
35158     var proxy = new Roo.Element(document.createElement("div"));
35159     proxy.unselectable();
35160     var cls = 'roo-splitbar-proxy';
35161     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
35162     document.body.appendChild(proxy.dom);
35163     return proxy.dom;
35164 };
35165
35166 /** 
35167  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
35168  * Default Adapter. It assumes the splitter and resizing element are not positioned
35169  * elements and only gets/sets the width of the element. Generally used for table based layouts.
35170  */
35171 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
35172 };
35173
35174 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
35175     // do nothing for now
35176     init : function(s){
35177     
35178     },
35179     /**
35180      * Called before drag operations to get the current size of the resizing element. 
35181      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35182      */
35183      getElementSize : function(s){
35184         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35185             return s.resizingEl.getWidth();
35186         }else{
35187             return s.resizingEl.getHeight();
35188         }
35189     },
35190     
35191     /**
35192      * Called after drag operations to set the size of the resizing element.
35193      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35194      * @param {Number} newSize The new size to set
35195      * @param {Function} onComplete A function to be invoked when resizing is complete
35196      */
35197     setElementSize : function(s, newSize, onComplete){
35198         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35199             if(!s.animate){
35200                 s.resizingEl.setWidth(newSize);
35201                 if(onComplete){
35202                     onComplete(s, newSize);
35203                 }
35204             }else{
35205                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
35206             }
35207         }else{
35208             
35209             if(!s.animate){
35210                 s.resizingEl.setHeight(newSize);
35211                 if(onComplete){
35212                     onComplete(s, newSize);
35213                 }
35214             }else{
35215                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
35216             }
35217         }
35218     }
35219 };
35220
35221 /** 
35222  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
35223  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
35224  * Adapter that  moves the splitter element to align with the resized sizing element. 
35225  * Used with an absolute positioned SplitBar.
35226  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
35227  * document.body, make sure you assign an id to the body element.
35228  */
35229 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
35230     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35231     this.container = Roo.get(container);
35232 };
35233
35234 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
35235     init : function(s){
35236         this.basic.init(s);
35237     },
35238     
35239     getElementSize : function(s){
35240         return this.basic.getElementSize(s);
35241     },
35242     
35243     setElementSize : function(s, newSize, onComplete){
35244         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
35245     },
35246     
35247     moveSplitter : function(s){
35248         var yes = Roo.bootstrap.SplitBar;
35249         switch(s.placement){
35250             case yes.LEFT:
35251                 s.el.setX(s.resizingEl.getRight());
35252                 break;
35253             case yes.RIGHT:
35254                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
35255                 break;
35256             case yes.TOP:
35257                 s.el.setY(s.resizingEl.getBottom());
35258                 break;
35259             case yes.BOTTOM:
35260                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
35261                 break;
35262         }
35263     }
35264 };
35265
35266 /**
35267  * Orientation constant - Create a vertical SplitBar
35268  * @static
35269  * @type Number
35270  */
35271 Roo.bootstrap.SplitBar.VERTICAL = 1;
35272
35273 /**
35274  * Orientation constant - Create a horizontal SplitBar
35275  * @static
35276  * @type Number
35277  */
35278 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
35279
35280 /**
35281  * Placement constant - The resizing element is to the left of the splitter element
35282  * @static
35283  * @type Number
35284  */
35285 Roo.bootstrap.SplitBar.LEFT = 1;
35286
35287 /**
35288  * Placement constant - The resizing element is to the right of the splitter element
35289  * @static
35290  * @type Number
35291  */
35292 Roo.bootstrap.SplitBar.RIGHT = 2;
35293
35294 /**
35295  * Placement constant - The resizing element is positioned above the splitter element
35296  * @static
35297  * @type Number
35298  */
35299 Roo.bootstrap.SplitBar.TOP = 3;
35300
35301 /**
35302  * Placement constant - The resizing element is positioned under splitter element
35303  * @static
35304  * @type Number
35305  */
35306 Roo.bootstrap.SplitBar.BOTTOM = 4;
35307 Roo.namespace("Roo.bootstrap.layout");/*
35308  * Based on:
35309  * Ext JS Library 1.1.1
35310  * Copyright(c) 2006-2007, Ext JS, LLC.
35311  *
35312  * Originally Released Under LGPL - original licence link has changed is not relivant.
35313  *
35314  * Fork - LGPL
35315  * <script type="text/javascript">
35316  */
35317
35318 /**
35319  * @class Roo.bootstrap.layout.Manager
35320  * @extends Roo.bootstrap.Component
35321  * Base class for layout managers.
35322  */
35323 Roo.bootstrap.layout.Manager = function(config)
35324 {
35325     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
35326
35327
35328
35329
35330
35331     /** false to disable window resize monitoring @type Boolean */
35332     this.monitorWindowResize = true;
35333     this.regions = {};
35334     this.addEvents({
35335         /**
35336          * @event layout
35337          * Fires when a layout is performed.
35338          * @param {Roo.LayoutManager} this
35339          */
35340         "layout" : true,
35341         /**
35342          * @event regionresized
35343          * Fires when the user resizes a region.
35344          * @param {Roo.LayoutRegion} region The resized region
35345          * @param {Number} newSize The new size (width for east/west, height for north/south)
35346          */
35347         "regionresized" : true,
35348         /**
35349          * @event regioncollapsed
35350          * Fires when a region is collapsed.
35351          * @param {Roo.LayoutRegion} region The collapsed region
35352          */
35353         "regioncollapsed" : true,
35354         /**
35355          * @event regionexpanded
35356          * Fires when a region is expanded.
35357          * @param {Roo.LayoutRegion} region The expanded region
35358          */
35359         "regionexpanded" : true
35360     });
35361     this.updating = false;
35362
35363     if (config.el) {
35364         this.el = Roo.get(config.el);
35365         this.initEvents();
35366     }
35367
35368 };
35369
35370 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35371
35372
35373     regions : null,
35374
35375     monitorWindowResize : true,
35376
35377
35378     updating : false,
35379
35380
35381     onRender : function(ct, position)
35382     {
35383         if(!this.el){
35384             this.el = Roo.get(ct);
35385             this.initEvents();
35386         }
35387         //this.fireEvent('render',this);
35388     },
35389
35390
35391     initEvents: function()
35392     {
35393
35394
35395         // ie scrollbar fix
35396         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35397             document.body.scroll = "no";
35398         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35399             this.el.position('relative');
35400         }
35401         this.id = this.el.id;
35402         this.el.addClass("roo-layout-container");
35403         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35404         if(this.el.dom != document.body ) {
35405             this.el.on('resize', this.layout,this);
35406             this.el.on('show', this.layout,this);
35407         }
35408
35409     },
35410
35411     /**
35412      * Returns true if this layout is currently being updated
35413      * @return {Boolean}
35414      */
35415     isUpdating : function(){
35416         return this.updating;
35417     },
35418
35419     /**
35420      * Suspend the LayoutManager from doing auto-layouts while
35421      * making multiple add or remove calls
35422      */
35423     beginUpdate : function(){
35424         this.updating = true;
35425     },
35426
35427     /**
35428      * Restore auto-layouts and optionally disable the manager from performing a layout
35429      * @param {Boolean} noLayout true to disable a layout update
35430      */
35431     endUpdate : function(noLayout){
35432         this.updating = false;
35433         if(!noLayout){
35434             this.layout();
35435         }
35436     },
35437
35438     layout: function(){
35439         // abstract...
35440     },
35441
35442     onRegionResized : function(region, newSize){
35443         this.fireEvent("regionresized", region, newSize);
35444         this.layout();
35445     },
35446
35447     onRegionCollapsed : function(region){
35448         this.fireEvent("regioncollapsed", region);
35449     },
35450
35451     onRegionExpanded : function(region){
35452         this.fireEvent("regionexpanded", region);
35453     },
35454
35455     /**
35456      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35457      * performs box-model adjustments.
35458      * @return {Object} The size as an object {width: (the width), height: (the height)}
35459      */
35460     getViewSize : function()
35461     {
35462         var size;
35463         if(this.el.dom != document.body){
35464             size = this.el.getSize();
35465         }else{
35466             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35467         }
35468         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35469         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35470         return size;
35471     },
35472
35473     /**
35474      * Returns the Element this layout is bound to.
35475      * @return {Roo.Element}
35476      */
35477     getEl : function(){
35478         return this.el;
35479     },
35480
35481     /**
35482      * Returns the specified region.
35483      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35484      * @return {Roo.LayoutRegion}
35485      */
35486     getRegion : function(target){
35487         return this.regions[target.toLowerCase()];
35488     },
35489
35490     onWindowResize : function(){
35491         if(this.monitorWindowResize){
35492             this.layout();
35493         }
35494     }
35495 });
35496 /*
35497  * Based on:
35498  * Ext JS Library 1.1.1
35499  * Copyright(c) 2006-2007, Ext JS, LLC.
35500  *
35501  * Originally Released Under LGPL - original licence link has changed is not relivant.
35502  *
35503  * Fork - LGPL
35504  * <script type="text/javascript">
35505  */
35506 /**
35507  * @class Roo.bootstrap.layout.Border
35508  * @extends Roo.bootstrap.layout.Manager
35509  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35510  * please see: examples/bootstrap/nested.html<br><br>
35511  
35512 <b>The container the layout is rendered into can be either the body element or any other element.
35513 If it is not the body element, the container needs to either be an absolute positioned element,
35514 or you will need to add "position:relative" to the css of the container.  You will also need to specify
35515 the container size if it is not the body element.</b>
35516
35517 * @constructor
35518 * Create a new Border
35519 * @param {Object} config Configuration options
35520  */
35521 Roo.bootstrap.layout.Border = function(config){
35522     config = config || {};
35523     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35524     
35525     
35526     
35527     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35528         if(config[region]){
35529             config[region].region = region;
35530             this.addRegion(config[region]);
35531         }
35532     },this);
35533     
35534 };
35535
35536 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35537
35538 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35539     
35540     parent : false, // this might point to a 'nest' or a ???
35541     
35542     /**
35543      * Creates and adds a new region if it doesn't already exist.
35544      * @param {String} target The target region key (north, south, east, west or center).
35545      * @param {Object} config The regions config object
35546      * @return {BorderLayoutRegion} The new region
35547      */
35548     addRegion : function(config)
35549     {
35550         if(!this.regions[config.region]){
35551             var r = this.factory(config);
35552             this.bindRegion(r);
35553         }
35554         return this.regions[config.region];
35555     },
35556
35557     // private (kinda)
35558     bindRegion : function(r){
35559         this.regions[r.config.region] = r;
35560         
35561         r.on("visibilitychange",    this.layout, this);
35562         r.on("paneladded",          this.layout, this);
35563         r.on("panelremoved",        this.layout, this);
35564         r.on("invalidated",         this.layout, this);
35565         r.on("resized",             this.onRegionResized, this);
35566         r.on("collapsed",           this.onRegionCollapsed, this);
35567         r.on("expanded",            this.onRegionExpanded, this);
35568     },
35569
35570     /**
35571      * Performs a layout update.
35572      */
35573     layout : function()
35574     {
35575         if(this.updating) {
35576             return;
35577         }
35578         
35579         // render all the rebions if they have not been done alreayd?
35580         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35581             if(this.regions[region] && !this.regions[region].bodyEl){
35582                 this.regions[region].onRender(this.el)
35583             }
35584         },this);
35585         
35586         var size = this.getViewSize();
35587         var w = size.width;
35588         var h = size.height;
35589         var centerW = w;
35590         var centerH = h;
35591         var centerY = 0;
35592         var centerX = 0;
35593         //var x = 0, y = 0;
35594
35595         var rs = this.regions;
35596         var north = rs["north"];
35597         var south = rs["south"]; 
35598         var west = rs["west"];
35599         var east = rs["east"];
35600         var center = rs["center"];
35601         //if(this.hideOnLayout){ // not supported anymore
35602             //c.el.setStyle("display", "none");
35603         //}
35604         if(north && north.isVisible()){
35605             var b = north.getBox();
35606             var m = north.getMargins();
35607             b.width = w - (m.left+m.right);
35608             b.x = m.left;
35609             b.y = m.top;
35610             centerY = b.height + b.y + m.bottom;
35611             centerH -= centerY;
35612             north.updateBox(this.safeBox(b));
35613         }
35614         if(south && south.isVisible()){
35615             var b = south.getBox();
35616             var m = south.getMargins();
35617             b.width = w - (m.left+m.right);
35618             b.x = m.left;
35619             var totalHeight = (b.height + m.top + m.bottom);
35620             b.y = h - totalHeight + m.top;
35621             centerH -= totalHeight;
35622             south.updateBox(this.safeBox(b));
35623         }
35624         if(west && west.isVisible()){
35625             var b = west.getBox();
35626             var m = west.getMargins();
35627             b.height = centerH - (m.top+m.bottom);
35628             b.x = m.left;
35629             b.y = centerY + m.top;
35630             var totalWidth = (b.width + m.left + m.right);
35631             centerX += totalWidth;
35632             centerW -= totalWidth;
35633             west.updateBox(this.safeBox(b));
35634         }
35635         if(east && east.isVisible()){
35636             var b = east.getBox();
35637             var m = east.getMargins();
35638             b.height = centerH - (m.top+m.bottom);
35639             var totalWidth = (b.width + m.left + m.right);
35640             b.x = w - totalWidth + m.left;
35641             b.y = centerY + m.top;
35642             centerW -= totalWidth;
35643             east.updateBox(this.safeBox(b));
35644         }
35645         if(center){
35646             var m = center.getMargins();
35647             var centerBox = {
35648                 x: centerX + m.left,
35649                 y: centerY + m.top,
35650                 width: centerW - (m.left+m.right),
35651                 height: centerH - (m.top+m.bottom)
35652             };
35653             //if(this.hideOnLayout){
35654                 //center.el.setStyle("display", "block");
35655             //}
35656             center.updateBox(this.safeBox(centerBox));
35657         }
35658         this.el.repaint();
35659         this.fireEvent("layout", this);
35660     },
35661
35662     // private
35663     safeBox : function(box){
35664         box.width = Math.max(0, box.width);
35665         box.height = Math.max(0, box.height);
35666         return box;
35667     },
35668
35669     /**
35670      * Adds a ContentPanel (or subclass) to this layout.
35671      * @param {String} target The target region key (north, south, east, west or center).
35672      * @param {Roo.ContentPanel} panel The panel to add
35673      * @return {Roo.ContentPanel} The added panel
35674      */
35675     add : function(target, panel){
35676          
35677         target = target.toLowerCase();
35678         return this.regions[target].add(panel);
35679     },
35680
35681     /**
35682      * Remove a ContentPanel (or subclass) to this layout.
35683      * @param {String} target The target region key (north, south, east, west or center).
35684      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35685      * @return {Roo.ContentPanel} The removed panel
35686      */
35687     remove : function(target, panel){
35688         target = target.toLowerCase();
35689         return this.regions[target].remove(panel);
35690     },
35691
35692     /**
35693      * Searches all regions for a panel with the specified id
35694      * @param {String} panelId
35695      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35696      */
35697     findPanel : function(panelId){
35698         var rs = this.regions;
35699         for(var target in rs){
35700             if(typeof rs[target] != "function"){
35701                 var p = rs[target].getPanel(panelId);
35702                 if(p){
35703                     return p;
35704                 }
35705             }
35706         }
35707         return null;
35708     },
35709
35710     /**
35711      * Searches all regions for a panel with the specified id and activates (shows) it.
35712      * @param {String/ContentPanel} panelId The panels id or the panel itself
35713      * @return {Roo.ContentPanel} The shown panel or null
35714      */
35715     showPanel : function(panelId) {
35716       var rs = this.regions;
35717       for(var target in rs){
35718          var r = rs[target];
35719          if(typeof r != "function"){
35720             if(r.hasPanel(panelId)){
35721                return r.showPanel(panelId);
35722             }
35723          }
35724       }
35725       return null;
35726    },
35727
35728    /**
35729      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35730      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35731      */
35732    /*
35733     restoreState : function(provider){
35734         if(!provider){
35735             provider = Roo.state.Manager;
35736         }
35737         var sm = new Roo.LayoutStateManager();
35738         sm.init(this, provider);
35739     },
35740 */
35741  
35742  
35743     /**
35744      * Adds a xtype elements to the layout.
35745      * <pre><code>
35746
35747 layout.addxtype({
35748        xtype : 'ContentPanel',
35749        region: 'west',
35750        items: [ .... ]
35751    }
35752 );
35753
35754 layout.addxtype({
35755         xtype : 'NestedLayoutPanel',
35756         region: 'west',
35757         layout: {
35758            center: { },
35759            west: { }   
35760         },
35761         items : [ ... list of content panels or nested layout panels.. ]
35762    }
35763 );
35764 </code></pre>
35765      * @param {Object} cfg Xtype definition of item to add.
35766      */
35767     addxtype : function(cfg)
35768     {
35769         // basically accepts a pannel...
35770         // can accept a layout region..!?!?
35771         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35772         
35773         
35774         // theory?  children can only be panels??
35775         
35776         //if (!cfg.xtype.match(/Panel$/)) {
35777         //    return false;
35778         //}
35779         var ret = false;
35780         
35781         if (typeof(cfg.region) == 'undefined') {
35782             Roo.log("Failed to add Panel, region was not set");
35783             Roo.log(cfg);
35784             return false;
35785         }
35786         var region = cfg.region;
35787         delete cfg.region;
35788         
35789           
35790         var xitems = [];
35791         if (cfg.items) {
35792             xitems = cfg.items;
35793             delete cfg.items;
35794         }
35795         var nb = false;
35796         
35797         if ( region == 'center') {
35798             Roo.log("Center: " + cfg.title);
35799         }
35800         
35801         
35802         switch(cfg.xtype) 
35803         {
35804             case 'Content':  // ContentPanel (el, cfg)
35805             case 'Scroll':  // ContentPanel (el, cfg)
35806             case 'View': 
35807                 cfg.autoCreate = cfg.autoCreate || true;
35808                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35809                 //} else {
35810                 //    var el = this.el.createChild();
35811                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35812                 //}
35813                 
35814                 this.add(region, ret);
35815                 break;
35816             
35817             /*
35818             case 'TreePanel': // our new panel!
35819                 cfg.el = this.el.createChild();
35820                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35821                 this.add(region, ret);
35822                 break;
35823             */
35824             
35825             case 'Nest': 
35826                 // create a new Layout (which is  a Border Layout...
35827                 
35828                 var clayout = cfg.layout;
35829                 clayout.el  = this.el.createChild();
35830                 clayout.items   = clayout.items  || [];
35831                 
35832                 delete cfg.layout;
35833                 
35834                 // replace this exitems with the clayout ones..
35835                 xitems = clayout.items;
35836                  
35837                 // force background off if it's in center...
35838                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35839                     cfg.background = false;
35840                 }
35841                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35842                 
35843                 
35844                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35845                 //console.log('adding nested layout panel '  + cfg.toSource());
35846                 this.add(region, ret);
35847                 nb = {}; /// find first...
35848                 break;
35849             
35850             case 'Grid':
35851                 
35852                 // needs grid and region
35853                 
35854                 //var el = this.getRegion(region).el.createChild();
35855                 /*
35856                  *var el = this.el.createChild();
35857                 // create the grid first...
35858                 cfg.grid.container = el;
35859                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35860                 */
35861                 
35862                 if (region == 'center' && this.active ) {
35863                     cfg.background = false;
35864                 }
35865                 
35866                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35867                 
35868                 this.add(region, ret);
35869                 /*
35870                 if (cfg.background) {
35871                     // render grid on panel activation (if panel background)
35872                     ret.on('activate', function(gp) {
35873                         if (!gp.grid.rendered) {
35874                     //        gp.grid.render(el);
35875                         }
35876                     });
35877                 } else {
35878                   //  cfg.grid.render(el);
35879                 }
35880                 */
35881                 break;
35882            
35883            
35884             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35885                 // it was the old xcomponent building that caused this before.
35886                 // espeically if border is the top element in the tree.
35887                 ret = this;
35888                 break; 
35889                 
35890                     
35891                 
35892                 
35893                 
35894             default:
35895                 /*
35896                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35897                     
35898                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35899                     this.add(region, ret);
35900                 } else {
35901                 */
35902                     Roo.log(cfg);
35903                     throw "Can not add '" + cfg.xtype + "' to Border";
35904                     return null;
35905              
35906                                 
35907              
35908         }
35909         this.beginUpdate();
35910         // add children..
35911         var region = '';
35912         var abn = {};
35913         Roo.each(xitems, function(i)  {
35914             region = nb && i.region ? i.region : false;
35915             
35916             var add = ret.addxtype(i);
35917            
35918             if (region) {
35919                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35920                 if (!i.background) {
35921                     abn[region] = nb[region] ;
35922                 }
35923             }
35924             
35925         });
35926         this.endUpdate();
35927
35928         // make the last non-background panel active..
35929         //if (nb) { Roo.log(abn); }
35930         if (nb) {
35931             
35932             for(var r in abn) {
35933                 region = this.getRegion(r);
35934                 if (region) {
35935                     // tried using nb[r], but it does not work..
35936                      
35937                     region.showPanel(abn[r]);
35938                    
35939                 }
35940             }
35941         }
35942         return ret;
35943         
35944     },
35945     
35946     
35947 // private
35948     factory : function(cfg)
35949     {
35950         
35951         var validRegions = Roo.bootstrap.layout.Border.regions;
35952
35953         var target = cfg.region;
35954         cfg.mgr = this;
35955         
35956         var r = Roo.bootstrap.layout;
35957         Roo.log(target);
35958         switch(target){
35959             case "north":
35960                 return new r.North(cfg);
35961             case "south":
35962                 return new r.South(cfg);
35963             case "east":
35964                 return new r.East(cfg);
35965             case "west":
35966                 return new r.West(cfg);
35967             case "center":
35968                 return new r.Center(cfg);
35969         }
35970         throw 'Layout region "'+target+'" not supported.';
35971     }
35972     
35973     
35974 });
35975  /*
35976  * Based on:
35977  * Ext JS Library 1.1.1
35978  * Copyright(c) 2006-2007, Ext JS, LLC.
35979  *
35980  * Originally Released Under LGPL - original licence link has changed is not relivant.
35981  *
35982  * Fork - LGPL
35983  * <script type="text/javascript">
35984  */
35985  
35986 /**
35987  * @class Roo.bootstrap.layout.Basic
35988  * @extends Roo.util.Observable
35989  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35990  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35991  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35992  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35993  * @cfg {string}   region  the region that it inhabits..
35994  * @cfg {bool}   skipConfig skip config?
35995  * 
35996
35997  */
35998 Roo.bootstrap.layout.Basic = function(config){
35999     
36000     this.mgr = config.mgr;
36001     
36002     this.position = config.region;
36003     
36004     var skipConfig = config.skipConfig;
36005     
36006     this.events = {
36007         /**
36008          * @scope Roo.BasicLayoutRegion
36009          */
36010         
36011         /**
36012          * @event beforeremove
36013          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
36014          * @param {Roo.LayoutRegion} this
36015          * @param {Roo.ContentPanel} panel The panel
36016          * @param {Object} e The cancel event object
36017          */
36018         "beforeremove" : true,
36019         /**
36020          * @event invalidated
36021          * Fires when the layout for this region is changed.
36022          * @param {Roo.LayoutRegion} this
36023          */
36024         "invalidated" : true,
36025         /**
36026          * @event visibilitychange
36027          * Fires when this region is shown or hidden 
36028          * @param {Roo.LayoutRegion} this
36029          * @param {Boolean} visibility true or false
36030          */
36031         "visibilitychange" : true,
36032         /**
36033          * @event paneladded
36034          * Fires when a panel is added. 
36035          * @param {Roo.LayoutRegion} this
36036          * @param {Roo.ContentPanel} panel The panel
36037          */
36038         "paneladded" : true,
36039         /**
36040          * @event panelremoved
36041          * Fires when a panel is removed. 
36042          * @param {Roo.LayoutRegion} this
36043          * @param {Roo.ContentPanel} panel The panel
36044          */
36045         "panelremoved" : true,
36046         /**
36047          * @event beforecollapse
36048          * Fires when this region before collapse.
36049          * @param {Roo.LayoutRegion} this
36050          */
36051         "beforecollapse" : true,
36052         /**
36053          * @event collapsed
36054          * Fires when this region is collapsed.
36055          * @param {Roo.LayoutRegion} this
36056          */
36057         "collapsed" : true,
36058         /**
36059          * @event expanded
36060          * Fires when this region is expanded.
36061          * @param {Roo.LayoutRegion} this
36062          */
36063         "expanded" : true,
36064         /**
36065          * @event slideshow
36066          * Fires when this region is slid into view.
36067          * @param {Roo.LayoutRegion} this
36068          */
36069         "slideshow" : true,
36070         /**
36071          * @event slidehide
36072          * Fires when this region slides out of view. 
36073          * @param {Roo.LayoutRegion} this
36074          */
36075         "slidehide" : true,
36076         /**
36077          * @event panelactivated
36078          * Fires when a panel is activated. 
36079          * @param {Roo.LayoutRegion} this
36080          * @param {Roo.ContentPanel} panel The activated panel
36081          */
36082         "panelactivated" : true,
36083         /**
36084          * @event resized
36085          * Fires when the user resizes this region. 
36086          * @param {Roo.LayoutRegion} this
36087          * @param {Number} newSize The new size (width for east/west, height for north/south)
36088          */
36089         "resized" : true
36090     };
36091     /** A collection of panels in this region. @type Roo.util.MixedCollection */
36092     this.panels = new Roo.util.MixedCollection();
36093     this.panels.getKey = this.getPanelId.createDelegate(this);
36094     this.box = null;
36095     this.activePanel = null;
36096     // ensure listeners are added...
36097     
36098     if (config.listeners || config.events) {
36099         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
36100             listeners : config.listeners || {},
36101             events : config.events || {}
36102         });
36103     }
36104     
36105     if(skipConfig !== true){
36106         this.applyConfig(config);
36107     }
36108 };
36109
36110 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
36111 {
36112     getPanelId : function(p){
36113         return p.getId();
36114     },
36115     
36116     applyConfig : function(config){
36117         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36118         this.config = config;
36119         
36120     },
36121     
36122     /**
36123      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
36124      * the width, for horizontal (north, south) the height.
36125      * @param {Number} newSize The new width or height
36126      */
36127     resizeTo : function(newSize){
36128         var el = this.el ? this.el :
36129                  (this.activePanel ? this.activePanel.getEl() : null);
36130         if(el){
36131             switch(this.position){
36132                 case "east":
36133                 case "west":
36134                     el.setWidth(newSize);
36135                     this.fireEvent("resized", this, newSize);
36136                 break;
36137                 case "north":
36138                 case "south":
36139                     el.setHeight(newSize);
36140                     this.fireEvent("resized", this, newSize);
36141                 break;                
36142             }
36143         }
36144     },
36145     
36146     getBox : function(){
36147         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
36148     },
36149     
36150     getMargins : function(){
36151         return this.margins;
36152     },
36153     
36154     updateBox : function(box){
36155         this.box = box;
36156         var el = this.activePanel.getEl();
36157         el.dom.style.left = box.x + "px";
36158         el.dom.style.top = box.y + "px";
36159         this.activePanel.setSize(box.width, box.height);
36160     },
36161     
36162     /**
36163      * Returns the container element for this region.
36164      * @return {Roo.Element}
36165      */
36166     getEl : function(){
36167         return this.activePanel;
36168     },
36169     
36170     /**
36171      * Returns true if this region is currently visible.
36172      * @return {Boolean}
36173      */
36174     isVisible : function(){
36175         return this.activePanel ? true : false;
36176     },
36177     
36178     setActivePanel : function(panel){
36179         panel = this.getPanel(panel);
36180         if(this.activePanel && this.activePanel != panel){
36181             this.activePanel.setActiveState(false);
36182             this.activePanel.getEl().setLeftTop(-10000,-10000);
36183         }
36184         this.activePanel = panel;
36185         panel.setActiveState(true);
36186         if(this.box){
36187             panel.setSize(this.box.width, this.box.height);
36188         }
36189         this.fireEvent("panelactivated", this, panel);
36190         this.fireEvent("invalidated");
36191     },
36192     
36193     /**
36194      * Show the specified panel.
36195      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
36196      * @return {Roo.ContentPanel} The shown panel or null
36197      */
36198     showPanel : function(panel){
36199         panel = this.getPanel(panel);
36200         if(panel){
36201             this.setActivePanel(panel);
36202         }
36203         return panel;
36204     },
36205     
36206     /**
36207      * Get the active panel for this region.
36208      * @return {Roo.ContentPanel} The active panel or null
36209      */
36210     getActivePanel : function(){
36211         return this.activePanel;
36212     },
36213     
36214     /**
36215      * Add the passed ContentPanel(s)
36216      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36217      * @return {Roo.ContentPanel} The panel added (if only one was added)
36218      */
36219     add : function(panel){
36220         if(arguments.length > 1){
36221             for(var i = 0, len = arguments.length; i < len; i++) {
36222                 this.add(arguments[i]);
36223             }
36224             return null;
36225         }
36226         if(this.hasPanel(panel)){
36227             this.showPanel(panel);
36228             return panel;
36229         }
36230         var el = panel.getEl();
36231         if(el.dom.parentNode != this.mgr.el.dom){
36232             this.mgr.el.dom.appendChild(el.dom);
36233         }
36234         if(panel.setRegion){
36235             panel.setRegion(this);
36236         }
36237         this.panels.add(panel);
36238         el.setStyle("position", "absolute");
36239         if(!panel.background){
36240             this.setActivePanel(panel);
36241             if(this.config.initialSize && this.panels.getCount()==1){
36242                 this.resizeTo(this.config.initialSize);
36243             }
36244         }
36245         this.fireEvent("paneladded", this, panel);
36246         return panel;
36247     },
36248     
36249     /**
36250      * Returns true if the panel is in this region.
36251      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36252      * @return {Boolean}
36253      */
36254     hasPanel : function(panel){
36255         if(typeof panel == "object"){ // must be panel obj
36256             panel = panel.getId();
36257         }
36258         return this.getPanel(panel) ? true : false;
36259     },
36260     
36261     /**
36262      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36263      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36264      * @param {Boolean} preservePanel Overrides the config preservePanel option
36265      * @return {Roo.ContentPanel} The panel that was removed
36266      */
36267     remove : function(panel, preservePanel){
36268         panel = this.getPanel(panel);
36269         if(!panel){
36270             return null;
36271         }
36272         var e = {};
36273         this.fireEvent("beforeremove", this, panel, e);
36274         if(e.cancel === true){
36275             return null;
36276         }
36277         var panelId = panel.getId();
36278         this.panels.removeKey(panelId);
36279         return panel;
36280     },
36281     
36282     /**
36283      * Returns the panel specified or null if it's not in this region.
36284      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36285      * @return {Roo.ContentPanel}
36286      */
36287     getPanel : function(id){
36288         if(typeof id == "object"){ // must be panel obj
36289             return id;
36290         }
36291         return this.panels.get(id);
36292     },
36293     
36294     /**
36295      * Returns this regions position (north/south/east/west/center).
36296      * @return {String} 
36297      */
36298     getPosition: function(){
36299         return this.position;    
36300     }
36301 });/*
36302  * Based on:
36303  * Ext JS Library 1.1.1
36304  * Copyright(c) 2006-2007, Ext JS, LLC.
36305  *
36306  * Originally Released Under LGPL - original licence link has changed is not relivant.
36307  *
36308  * Fork - LGPL
36309  * <script type="text/javascript">
36310  */
36311  
36312 /**
36313  * @class Roo.bootstrap.layout.Region
36314  * @extends Roo.bootstrap.layout.Basic
36315  * This class represents a region in a layout manager.
36316  
36317  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
36318  * @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})
36319  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
36320  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
36321  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
36322  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
36323  * @cfg {String}    title           The title for the region (overrides panel titles)
36324  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
36325  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
36326  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
36327  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
36328  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
36329  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
36330  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
36331  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
36332  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
36333  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
36334
36335  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
36336  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
36337  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
36338  * @cfg {Number}    width           For East/West panels
36339  * @cfg {Number}    height          For North/South panels
36340  * @cfg {Boolean}   split           To show the splitter
36341  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
36342  * 
36343  * @cfg {string}   cls             Extra CSS classes to add to region
36344  * 
36345  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
36346  * @cfg {string}   region  the region that it inhabits..
36347  *
36348
36349  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
36350  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
36351
36352  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
36353  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
36354  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
36355  */
36356 Roo.bootstrap.layout.Region = function(config)
36357 {
36358     this.applyConfig(config);
36359
36360     var mgr = config.mgr;
36361     var pos = config.region;
36362     config.skipConfig = true;
36363     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36364     
36365     if (mgr.el) {
36366         this.onRender(mgr.el);   
36367     }
36368      
36369     this.visible = true;
36370     this.collapsed = false;
36371     this.unrendered_panels = [];
36372 };
36373
36374 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36375
36376     position: '', // set by wrapper (eg. north/south etc..)
36377     unrendered_panels : null,  // unrendered panels.
36378     
36379     tabPosition : false,
36380     
36381     mgr: false, // points to 'Border'
36382     
36383     
36384     createBody : function(){
36385         /** This region's body element 
36386         * @type Roo.Element */
36387         this.bodyEl = this.el.createChild({
36388                 tag: "div",
36389                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36390         });
36391     },
36392
36393     onRender: function(ctr, pos)
36394     {
36395         var dh = Roo.DomHelper;
36396         /** This region's container element 
36397         * @type Roo.Element */
36398         this.el = dh.append(ctr.dom, {
36399                 tag: "div",
36400                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36401             }, true);
36402         /** This region's title element 
36403         * @type Roo.Element */
36404     
36405         this.titleEl = dh.append(this.el.dom,  {
36406                 tag: "div",
36407                 unselectable: "on",
36408                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36409                 children:[
36410                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
36411                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36412                 ]
36413             }, true);
36414         
36415         this.titleEl.enableDisplayMode();
36416         /** This region's title text element 
36417         * @type HTMLElement */
36418         this.titleTextEl = this.titleEl.dom.firstChild;
36419         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36420         /*
36421         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36422         this.closeBtn.enableDisplayMode();
36423         this.closeBtn.on("click", this.closeClicked, this);
36424         this.closeBtn.hide();
36425     */
36426         this.createBody(this.config);
36427         if(this.config.hideWhenEmpty){
36428             this.hide();
36429             this.on("paneladded", this.validateVisibility, this);
36430             this.on("panelremoved", this.validateVisibility, this);
36431         }
36432         if(this.autoScroll){
36433             this.bodyEl.setStyle("overflow", "auto");
36434         }else{
36435             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36436         }
36437         //if(c.titlebar !== false){
36438             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36439                 this.titleEl.hide();
36440             }else{
36441                 this.titleEl.show();
36442                 if(this.config.title){
36443                     this.titleTextEl.innerHTML = this.config.title;
36444                 }
36445             }
36446         //}
36447         if(this.config.collapsed){
36448             this.collapse(true);
36449         }
36450         if(this.config.hidden){
36451             this.hide();
36452         }
36453         
36454         if (this.unrendered_panels && this.unrendered_panels.length) {
36455             for (var i =0;i< this.unrendered_panels.length; i++) {
36456                 this.add(this.unrendered_panels[i]);
36457             }
36458             this.unrendered_panels = null;
36459             
36460         }
36461         
36462     },
36463     
36464     applyConfig : function(c)
36465     {
36466         /*
36467          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36468             var dh = Roo.DomHelper;
36469             if(c.titlebar !== false){
36470                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36471                 this.collapseBtn.on("click", this.collapse, this);
36472                 this.collapseBtn.enableDisplayMode();
36473                 /*
36474                 if(c.showPin === true || this.showPin){
36475                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36476                     this.stickBtn.enableDisplayMode();
36477                     this.stickBtn.on("click", this.expand, this);
36478                     this.stickBtn.hide();
36479                 }
36480                 
36481             }
36482             */
36483             /** This region's collapsed element
36484             * @type Roo.Element */
36485             /*
36486              *
36487             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36488                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36489             ]}, true);
36490             
36491             if(c.floatable !== false){
36492                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36493                this.collapsedEl.on("click", this.collapseClick, this);
36494             }
36495
36496             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36497                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36498                    id: "message", unselectable: "on", style:{"float":"left"}});
36499                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36500              }
36501             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36502             this.expandBtn.on("click", this.expand, this);
36503             
36504         }
36505         
36506         if(this.collapseBtn){
36507             this.collapseBtn.setVisible(c.collapsible == true);
36508         }
36509         
36510         this.cmargins = c.cmargins || this.cmargins ||
36511                          (this.position == "west" || this.position == "east" ?
36512                              {top: 0, left: 2, right:2, bottom: 0} :
36513                              {top: 2, left: 0, right:0, bottom: 2});
36514         */
36515         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36516         
36517         
36518         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36519         
36520         this.autoScroll = c.autoScroll || false;
36521         
36522         
36523        
36524         
36525         this.duration = c.duration || .30;
36526         this.slideDuration = c.slideDuration || .45;
36527         this.config = c;
36528        
36529     },
36530     /**
36531      * Returns true if this region is currently visible.
36532      * @return {Boolean}
36533      */
36534     isVisible : function(){
36535         return this.visible;
36536     },
36537
36538     /**
36539      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36540      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36541      */
36542     //setCollapsedTitle : function(title){
36543     //    title = title || "&#160;";
36544      //   if(this.collapsedTitleTextEl){
36545       //      this.collapsedTitleTextEl.innerHTML = title;
36546        // }
36547     //},
36548
36549     getBox : function(){
36550         var b;
36551       //  if(!this.collapsed){
36552             b = this.el.getBox(false, true);
36553        // }else{
36554           //  b = this.collapsedEl.getBox(false, true);
36555         //}
36556         return b;
36557     },
36558
36559     getMargins : function(){
36560         return this.margins;
36561         //return this.collapsed ? this.cmargins : this.margins;
36562     },
36563 /*
36564     highlight : function(){
36565         this.el.addClass("x-layout-panel-dragover");
36566     },
36567
36568     unhighlight : function(){
36569         this.el.removeClass("x-layout-panel-dragover");
36570     },
36571 */
36572     updateBox : function(box)
36573     {
36574         if (!this.bodyEl) {
36575             return; // not rendered yet..
36576         }
36577         
36578         this.box = box;
36579         if(!this.collapsed){
36580             this.el.dom.style.left = box.x + "px";
36581             this.el.dom.style.top = box.y + "px";
36582             this.updateBody(box.width, box.height);
36583         }else{
36584             this.collapsedEl.dom.style.left = box.x + "px";
36585             this.collapsedEl.dom.style.top = box.y + "px";
36586             this.collapsedEl.setSize(box.width, box.height);
36587         }
36588         if(this.tabs){
36589             this.tabs.autoSizeTabs();
36590         }
36591     },
36592
36593     updateBody : function(w, h)
36594     {
36595         if(w !== null){
36596             this.el.setWidth(w);
36597             w -= this.el.getBorderWidth("rl");
36598             if(this.config.adjustments){
36599                 w += this.config.adjustments[0];
36600             }
36601         }
36602         if(h !== null && h > 0){
36603             this.el.setHeight(h);
36604             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36605             h -= this.el.getBorderWidth("tb");
36606             if(this.config.adjustments){
36607                 h += this.config.adjustments[1];
36608             }
36609             this.bodyEl.setHeight(h);
36610             if(this.tabs){
36611                 h = this.tabs.syncHeight(h);
36612             }
36613         }
36614         if(this.panelSize){
36615             w = w !== null ? w : this.panelSize.width;
36616             h = h !== null ? h : this.panelSize.height;
36617         }
36618         if(this.activePanel){
36619             var el = this.activePanel.getEl();
36620             w = w !== null ? w : el.getWidth();
36621             h = h !== null ? h : el.getHeight();
36622             this.panelSize = {width: w, height: h};
36623             this.activePanel.setSize(w, h);
36624         }
36625         if(Roo.isIE && this.tabs){
36626             this.tabs.el.repaint();
36627         }
36628     },
36629
36630     /**
36631      * Returns the container element for this region.
36632      * @return {Roo.Element}
36633      */
36634     getEl : function(){
36635         return this.el;
36636     },
36637
36638     /**
36639      * Hides this region.
36640      */
36641     hide : function(){
36642         //if(!this.collapsed){
36643             this.el.dom.style.left = "-2000px";
36644             this.el.hide();
36645         //}else{
36646          //   this.collapsedEl.dom.style.left = "-2000px";
36647          //   this.collapsedEl.hide();
36648        // }
36649         this.visible = false;
36650         this.fireEvent("visibilitychange", this, false);
36651     },
36652
36653     /**
36654      * Shows this region if it was previously hidden.
36655      */
36656     show : function(){
36657         //if(!this.collapsed){
36658             this.el.show();
36659         //}else{
36660         //    this.collapsedEl.show();
36661        // }
36662         this.visible = true;
36663         this.fireEvent("visibilitychange", this, true);
36664     },
36665 /*
36666     closeClicked : function(){
36667         if(this.activePanel){
36668             this.remove(this.activePanel);
36669         }
36670     },
36671
36672     collapseClick : function(e){
36673         if(this.isSlid){
36674            e.stopPropagation();
36675            this.slideIn();
36676         }else{
36677            e.stopPropagation();
36678            this.slideOut();
36679         }
36680     },
36681 */
36682     /**
36683      * Collapses this region.
36684      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36685      */
36686     /*
36687     collapse : function(skipAnim, skipCheck = false){
36688         if(this.collapsed) {
36689             return;
36690         }
36691         
36692         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36693             
36694             this.collapsed = true;
36695             if(this.split){
36696                 this.split.el.hide();
36697             }
36698             if(this.config.animate && skipAnim !== true){
36699                 this.fireEvent("invalidated", this);
36700                 this.animateCollapse();
36701             }else{
36702                 this.el.setLocation(-20000,-20000);
36703                 this.el.hide();
36704                 this.collapsedEl.show();
36705                 this.fireEvent("collapsed", this);
36706                 this.fireEvent("invalidated", this);
36707             }
36708         }
36709         
36710     },
36711 */
36712     animateCollapse : function(){
36713         // overridden
36714     },
36715
36716     /**
36717      * Expands this region if it was previously collapsed.
36718      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36719      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36720      */
36721     /*
36722     expand : function(e, skipAnim){
36723         if(e) {
36724             e.stopPropagation();
36725         }
36726         if(!this.collapsed || this.el.hasActiveFx()) {
36727             return;
36728         }
36729         if(this.isSlid){
36730             this.afterSlideIn();
36731             skipAnim = true;
36732         }
36733         this.collapsed = false;
36734         if(this.config.animate && skipAnim !== true){
36735             this.animateExpand();
36736         }else{
36737             this.el.show();
36738             if(this.split){
36739                 this.split.el.show();
36740             }
36741             this.collapsedEl.setLocation(-2000,-2000);
36742             this.collapsedEl.hide();
36743             this.fireEvent("invalidated", this);
36744             this.fireEvent("expanded", this);
36745         }
36746     },
36747 */
36748     animateExpand : function(){
36749         // overridden
36750     },
36751
36752     initTabs : function()
36753     {
36754         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36755         
36756         var ts = new Roo.bootstrap.panel.Tabs({
36757             el: this.bodyEl.dom,
36758             region : this,
36759             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
36760             disableTooltips: this.config.disableTabTips,
36761             toolbar : this.config.toolbar
36762         });
36763         
36764         if(this.config.hideTabs){
36765             ts.stripWrap.setDisplayed(false);
36766         }
36767         this.tabs = ts;
36768         ts.resizeTabs = this.config.resizeTabs === true;
36769         ts.minTabWidth = this.config.minTabWidth || 40;
36770         ts.maxTabWidth = this.config.maxTabWidth || 250;
36771         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36772         ts.monitorResize = false;
36773         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36774         ts.bodyEl.addClass('roo-layout-tabs-body');
36775         this.panels.each(this.initPanelAsTab, this);
36776     },
36777
36778     initPanelAsTab : function(panel){
36779         var ti = this.tabs.addTab(
36780             panel.getEl().id,
36781             panel.getTitle(),
36782             null,
36783             this.config.closeOnTab && panel.isClosable(),
36784             panel.tpl
36785         );
36786         if(panel.tabTip !== undefined){
36787             ti.setTooltip(panel.tabTip);
36788         }
36789         ti.on("activate", function(){
36790               this.setActivePanel(panel);
36791         }, this);
36792         
36793         if(this.config.closeOnTab){
36794             ti.on("beforeclose", function(t, e){
36795                 e.cancel = true;
36796                 this.remove(panel);
36797             }, this);
36798         }
36799         
36800         panel.tabItem = ti;
36801         
36802         return ti;
36803     },
36804
36805     updatePanelTitle : function(panel, title)
36806     {
36807         if(this.activePanel == panel){
36808             this.updateTitle(title);
36809         }
36810         if(this.tabs){
36811             var ti = this.tabs.getTab(panel.getEl().id);
36812             ti.setText(title);
36813             if(panel.tabTip !== undefined){
36814                 ti.setTooltip(panel.tabTip);
36815             }
36816         }
36817     },
36818
36819     updateTitle : function(title){
36820         if(this.titleTextEl && !this.config.title){
36821             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36822         }
36823     },
36824
36825     setActivePanel : function(panel)
36826     {
36827         panel = this.getPanel(panel);
36828         if(this.activePanel && this.activePanel != panel){
36829             if(this.activePanel.setActiveState(false) === false){
36830                 return;
36831             }
36832         }
36833         this.activePanel = panel;
36834         panel.setActiveState(true);
36835         if(this.panelSize){
36836             panel.setSize(this.panelSize.width, this.panelSize.height);
36837         }
36838         if(this.closeBtn){
36839             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36840         }
36841         this.updateTitle(panel.getTitle());
36842         if(this.tabs){
36843             this.fireEvent("invalidated", this);
36844         }
36845         this.fireEvent("panelactivated", this, panel);
36846     },
36847
36848     /**
36849      * Shows the specified panel.
36850      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36851      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36852      */
36853     showPanel : function(panel)
36854     {
36855         panel = this.getPanel(panel);
36856         if(panel){
36857             if(this.tabs){
36858                 var tab = this.tabs.getTab(panel.getEl().id);
36859                 if(tab.isHidden()){
36860                     this.tabs.unhideTab(tab.id);
36861                 }
36862                 tab.activate();
36863             }else{
36864                 this.setActivePanel(panel);
36865             }
36866         }
36867         return panel;
36868     },
36869
36870     /**
36871      * Get the active panel for this region.
36872      * @return {Roo.ContentPanel} The active panel or null
36873      */
36874     getActivePanel : function(){
36875         return this.activePanel;
36876     },
36877
36878     validateVisibility : function(){
36879         if(this.panels.getCount() < 1){
36880             this.updateTitle("&#160;");
36881             this.closeBtn.hide();
36882             this.hide();
36883         }else{
36884             if(!this.isVisible()){
36885                 this.show();
36886             }
36887         }
36888     },
36889
36890     /**
36891      * Adds the passed ContentPanel(s) to this region.
36892      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36893      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36894      */
36895     add : function(panel)
36896     {
36897         if(arguments.length > 1){
36898             for(var i = 0, len = arguments.length; i < len; i++) {
36899                 this.add(arguments[i]);
36900             }
36901             return null;
36902         }
36903         
36904         // if we have not been rendered yet, then we can not really do much of this..
36905         if (!this.bodyEl) {
36906             this.unrendered_panels.push(panel);
36907             return panel;
36908         }
36909         
36910         
36911         
36912         
36913         if(this.hasPanel(panel)){
36914             this.showPanel(panel);
36915             return panel;
36916         }
36917         panel.setRegion(this);
36918         this.panels.add(panel);
36919        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36920             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36921             // and hide them... ???
36922             this.bodyEl.dom.appendChild(panel.getEl().dom);
36923             if(panel.background !== true){
36924                 this.setActivePanel(panel);
36925             }
36926             this.fireEvent("paneladded", this, panel);
36927             return panel;
36928         }
36929         */
36930         if(!this.tabs){
36931             this.initTabs();
36932         }else{
36933             this.initPanelAsTab(panel);
36934         }
36935         
36936         
36937         if(panel.background !== true){
36938             this.tabs.activate(panel.getEl().id);
36939         }
36940         this.fireEvent("paneladded", this, panel);
36941         return panel;
36942     },
36943
36944     /**
36945      * Hides the tab for the specified panel.
36946      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36947      */
36948     hidePanel : function(panel){
36949         if(this.tabs && (panel = this.getPanel(panel))){
36950             this.tabs.hideTab(panel.getEl().id);
36951         }
36952     },
36953
36954     /**
36955      * Unhides the tab for a previously hidden panel.
36956      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36957      */
36958     unhidePanel : function(panel){
36959         if(this.tabs && (panel = this.getPanel(panel))){
36960             this.tabs.unhideTab(panel.getEl().id);
36961         }
36962     },
36963
36964     clearPanels : function(){
36965         while(this.panels.getCount() > 0){
36966              this.remove(this.panels.first());
36967         }
36968     },
36969
36970     /**
36971      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36972      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36973      * @param {Boolean} preservePanel Overrides the config preservePanel option
36974      * @return {Roo.ContentPanel} The panel that was removed
36975      */
36976     remove : function(panel, preservePanel)
36977     {
36978         panel = this.getPanel(panel);
36979         if(!panel){
36980             return null;
36981         }
36982         var e = {};
36983         this.fireEvent("beforeremove", this, panel, e);
36984         if(e.cancel === true){
36985             return null;
36986         }
36987         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36988         var panelId = panel.getId();
36989         this.panels.removeKey(panelId);
36990         if(preservePanel){
36991             document.body.appendChild(panel.getEl().dom);
36992         }
36993         if(this.tabs){
36994             this.tabs.removeTab(panel.getEl().id);
36995         }else if (!preservePanel){
36996             this.bodyEl.dom.removeChild(panel.getEl().dom);
36997         }
36998         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36999             var p = this.panels.first();
37000             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
37001             tempEl.appendChild(p.getEl().dom);
37002             this.bodyEl.update("");
37003             this.bodyEl.dom.appendChild(p.getEl().dom);
37004             tempEl = null;
37005             this.updateTitle(p.getTitle());
37006             this.tabs = null;
37007             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
37008             this.setActivePanel(p);
37009         }
37010         panel.setRegion(null);
37011         if(this.activePanel == panel){
37012             this.activePanel = null;
37013         }
37014         if(this.config.autoDestroy !== false && preservePanel !== true){
37015             try{panel.destroy();}catch(e){}
37016         }
37017         this.fireEvent("panelremoved", this, panel);
37018         return panel;
37019     },
37020
37021     /**
37022      * Returns the TabPanel component used by this region
37023      * @return {Roo.TabPanel}
37024      */
37025     getTabs : function(){
37026         return this.tabs;
37027     },
37028
37029     createTool : function(parentEl, className){
37030         var btn = Roo.DomHelper.append(parentEl, {
37031             tag: "div",
37032             cls: "x-layout-tools-button",
37033             children: [ {
37034                 tag: "div",
37035                 cls: "roo-layout-tools-button-inner " + className,
37036                 html: "&#160;"
37037             }]
37038         }, true);
37039         btn.addClassOnOver("roo-layout-tools-button-over");
37040         return btn;
37041     }
37042 });/*
37043  * Based on:
37044  * Ext JS Library 1.1.1
37045  * Copyright(c) 2006-2007, Ext JS, LLC.
37046  *
37047  * Originally Released Under LGPL - original licence link has changed is not relivant.
37048  *
37049  * Fork - LGPL
37050  * <script type="text/javascript">
37051  */
37052  
37053
37054
37055 /**
37056  * @class Roo.SplitLayoutRegion
37057  * @extends Roo.LayoutRegion
37058  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
37059  */
37060 Roo.bootstrap.layout.Split = function(config){
37061     this.cursor = config.cursor;
37062     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
37063 };
37064
37065 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
37066 {
37067     splitTip : "Drag to resize.",
37068     collapsibleSplitTip : "Drag to resize. Double click to hide.",
37069     useSplitTips : false,
37070
37071     applyConfig : function(config){
37072         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
37073     },
37074     
37075     onRender : function(ctr,pos) {
37076         
37077         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
37078         if(!this.config.split){
37079             return;
37080         }
37081         if(!this.split){
37082             
37083             var splitEl = Roo.DomHelper.append(ctr.dom,  {
37084                             tag: "div",
37085                             id: this.el.id + "-split",
37086                             cls: "roo-layout-split roo-layout-split-"+this.position,
37087                             html: "&#160;"
37088             });
37089             /** The SplitBar for this region 
37090             * @type Roo.SplitBar */
37091             // does not exist yet...
37092             Roo.log([this.position, this.orientation]);
37093             
37094             this.split = new Roo.bootstrap.SplitBar({
37095                 dragElement : splitEl,
37096                 resizingElement: this.el,
37097                 orientation : this.orientation
37098             });
37099             
37100             this.split.on("moved", this.onSplitMove, this);
37101             this.split.useShim = this.config.useShim === true;
37102             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
37103             if(this.useSplitTips){
37104                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
37105             }
37106             //if(config.collapsible){
37107             //    this.split.el.on("dblclick", this.collapse,  this);
37108             //}
37109         }
37110         if(typeof this.config.minSize != "undefined"){
37111             this.split.minSize = this.config.minSize;
37112         }
37113         if(typeof this.config.maxSize != "undefined"){
37114             this.split.maxSize = this.config.maxSize;
37115         }
37116         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
37117             this.hideSplitter();
37118         }
37119         
37120     },
37121
37122     getHMaxSize : function(){
37123          var cmax = this.config.maxSize || 10000;
37124          var center = this.mgr.getRegion("center");
37125          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
37126     },
37127
37128     getVMaxSize : function(){
37129          var cmax = this.config.maxSize || 10000;
37130          var center = this.mgr.getRegion("center");
37131          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
37132     },
37133
37134     onSplitMove : function(split, newSize){
37135         this.fireEvent("resized", this, newSize);
37136     },
37137     
37138     /** 
37139      * Returns the {@link Roo.SplitBar} for this region.
37140      * @return {Roo.SplitBar}
37141      */
37142     getSplitBar : function(){
37143         return this.split;
37144     },
37145     
37146     hide : function(){
37147         this.hideSplitter();
37148         Roo.bootstrap.layout.Split.superclass.hide.call(this);
37149     },
37150
37151     hideSplitter : function(){
37152         if(this.split){
37153             this.split.el.setLocation(-2000,-2000);
37154             this.split.el.hide();
37155         }
37156     },
37157
37158     show : function(){
37159         if(this.split){
37160             this.split.el.show();
37161         }
37162         Roo.bootstrap.layout.Split.superclass.show.call(this);
37163     },
37164     
37165     beforeSlide: function(){
37166         if(Roo.isGecko){// firefox overflow auto bug workaround
37167             this.bodyEl.clip();
37168             if(this.tabs) {
37169                 this.tabs.bodyEl.clip();
37170             }
37171             if(this.activePanel){
37172                 this.activePanel.getEl().clip();
37173                 
37174                 if(this.activePanel.beforeSlide){
37175                     this.activePanel.beforeSlide();
37176                 }
37177             }
37178         }
37179     },
37180     
37181     afterSlide : function(){
37182         if(Roo.isGecko){// firefox overflow auto bug workaround
37183             this.bodyEl.unclip();
37184             if(this.tabs) {
37185                 this.tabs.bodyEl.unclip();
37186             }
37187             if(this.activePanel){
37188                 this.activePanel.getEl().unclip();
37189                 if(this.activePanel.afterSlide){
37190                     this.activePanel.afterSlide();
37191                 }
37192             }
37193         }
37194     },
37195
37196     initAutoHide : function(){
37197         if(this.autoHide !== false){
37198             if(!this.autoHideHd){
37199                 var st = new Roo.util.DelayedTask(this.slideIn, this);
37200                 this.autoHideHd = {
37201                     "mouseout": function(e){
37202                         if(!e.within(this.el, true)){
37203                             st.delay(500);
37204                         }
37205                     },
37206                     "mouseover" : function(e){
37207                         st.cancel();
37208                     },
37209                     scope : this
37210                 };
37211             }
37212             this.el.on(this.autoHideHd);
37213         }
37214     },
37215
37216     clearAutoHide : function(){
37217         if(this.autoHide !== false){
37218             this.el.un("mouseout", this.autoHideHd.mouseout);
37219             this.el.un("mouseover", this.autoHideHd.mouseover);
37220         }
37221     },
37222
37223     clearMonitor : function(){
37224         Roo.get(document).un("click", this.slideInIf, this);
37225     },
37226
37227     // these names are backwards but not changed for compat
37228     slideOut : function(){
37229         if(this.isSlid || this.el.hasActiveFx()){
37230             return;
37231         }
37232         this.isSlid = true;
37233         if(this.collapseBtn){
37234             this.collapseBtn.hide();
37235         }
37236         this.closeBtnState = this.closeBtn.getStyle('display');
37237         this.closeBtn.hide();
37238         if(this.stickBtn){
37239             this.stickBtn.show();
37240         }
37241         this.el.show();
37242         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
37243         this.beforeSlide();
37244         this.el.setStyle("z-index", 10001);
37245         this.el.slideIn(this.getSlideAnchor(), {
37246             callback: function(){
37247                 this.afterSlide();
37248                 this.initAutoHide();
37249                 Roo.get(document).on("click", this.slideInIf, this);
37250                 this.fireEvent("slideshow", this);
37251             },
37252             scope: this,
37253             block: true
37254         });
37255     },
37256
37257     afterSlideIn : function(){
37258         this.clearAutoHide();
37259         this.isSlid = false;
37260         this.clearMonitor();
37261         this.el.setStyle("z-index", "");
37262         if(this.collapseBtn){
37263             this.collapseBtn.show();
37264         }
37265         this.closeBtn.setStyle('display', this.closeBtnState);
37266         if(this.stickBtn){
37267             this.stickBtn.hide();
37268         }
37269         this.fireEvent("slidehide", this);
37270     },
37271
37272     slideIn : function(cb){
37273         if(!this.isSlid || this.el.hasActiveFx()){
37274             Roo.callback(cb);
37275             return;
37276         }
37277         this.isSlid = false;
37278         this.beforeSlide();
37279         this.el.slideOut(this.getSlideAnchor(), {
37280             callback: function(){
37281                 this.el.setLeftTop(-10000, -10000);
37282                 this.afterSlide();
37283                 this.afterSlideIn();
37284                 Roo.callback(cb);
37285             },
37286             scope: this,
37287             block: true
37288         });
37289     },
37290     
37291     slideInIf : function(e){
37292         if(!e.within(this.el)){
37293             this.slideIn();
37294         }
37295     },
37296
37297     animateCollapse : function(){
37298         this.beforeSlide();
37299         this.el.setStyle("z-index", 20000);
37300         var anchor = this.getSlideAnchor();
37301         this.el.slideOut(anchor, {
37302             callback : function(){
37303                 this.el.setStyle("z-index", "");
37304                 this.collapsedEl.slideIn(anchor, {duration:.3});
37305                 this.afterSlide();
37306                 this.el.setLocation(-10000,-10000);
37307                 this.el.hide();
37308                 this.fireEvent("collapsed", this);
37309             },
37310             scope: this,
37311             block: true
37312         });
37313     },
37314
37315     animateExpand : function(){
37316         this.beforeSlide();
37317         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
37318         this.el.setStyle("z-index", 20000);
37319         this.collapsedEl.hide({
37320             duration:.1
37321         });
37322         this.el.slideIn(this.getSlideAnchor(), {
37323             callback : function(){
37324                 this.el.setStyle("z-index", "");
37325                 this.afterSlide();
37326                 if(this.split){
37327                     this.split.el.show();
37328                 }
37329                 this.fireEvent("invalidated", this);
37330                 this.fireEvent("expanded", this);
37331             },
37332             scope: this,
37333             block: true
37334         });
37335     },
37336
37337     anchors : {
37338         "west" : "left",
37339         "east" : "right",
37340         "north" : "top",
37341         "south" : "bottom"
37342     },
37343
37344     sanchors : {
37345         "west" : "l",
37346         "east" : "r",
37347         "north" : "t",
37348         "south" : "b"
37349     },
37350
37351     canchors : {
37352         "west" : "tl-tr",
37353         "east" : "tr-tl",
37354         "north" : "tl-bl",
37355         "south" : "bl-tl"
37356     },
37357
37358     getAnchor : function(){
37359         return this.anchors[this.position];
37360     },
37361
37362     getCollapseAnchor : function(){
37363         return this.canchors[this.position];
37364     },
37365
37366     getSlideAnchor : function(){
37367         return this.sanchors[this.position];
37368     },
37369
37370     getAlignAdj : function(){
37371         var cm = this.cmargins;
37372         switch(this.position){
37373             case "west":
37374                 return [0, 0];
37375             break;
37376             case "east":
37377                 return [0, 0];
37378             break;
37379             case "north":
37380                 return [0, 0];
37381             break;
37382             case "south":
37383                 return [0, 0];
37384             break;
37385         }
37386     },
37387
37388     getExpandAdj : function(){
37389         var c = this.collapsedEl, cm = this.cmargins;
37390         switch(this.position){
37391             case "west":
37392                 return [-(cm.right+c.getWidth()+cm.left), 0];
37393             break;
37394             case "east":
37395                 return [cm.right+c.getWidth()+cm.left, 0];
37396             break;
37397             case "north":
37398                 return [0, -(cm.top+cm.bottom+c.getHeight())];
37399             break;
37400             case "south":
37401                 return [0, cm.top+cm.bottom+c.getHeight()];
37402             break;
37403         }
37404     }
37405 });/*
37406  * Based on:
37407  * Ext JS Library 1.1.1
37408  * Copyright(c) 2006-2007, Ext JS, LLC.
37409  *
37410  * Originally Released Under LGPL - original licence link has changed is not relivant.
37411  *
37412  * Fork - LGPL
37413  * <script type="text/javascript">
37414  */
37415 /*
37416  * These classes are private internal classes
37417  */
37418 Roo.bootstrap.layout.Center = function(config){
37419     config.region = "center";
37420     Roo.bootstrap.layout.Region.call(this, config);
37421     this.visible = true;
37422     this.minWidth = config.minWidth || 20;
37423     this.minHeight = config.minHeight || 20;
37424 };
37425
37426 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37427     hide : function(){
37428         // center panel can't be hidden
37429     },
37430     
37431     show : function(){
37432         // center panel can't be hidden
37433     },
37434     
37435     getMinWidth: function(){
37436         return this.minWidth;
37437     },
37438     
37439     getMinHeight: function(){
37440         return this.minHeight;
37441     }
37442 });
37443
37444
37445
37446
37447  
37448
37449
37450
37451
37452
37453
37454 Roo.bootstrap.layout.North = function(config)
37455 {
37456     config.region = 'north';
37457     config.cursor = 'n-resize';
37458     
37459     Roo.bootstrap.layout.Split.call(this, config);
37460     
37461     
37462     if(this.split){
37463         this.split.placement = Roo.bootstrap.SplitBar.TOP;
37464         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37465         this.split.el.addClass("roo-layout-split-v");
37466     }
37467     var size = config.initialSize || config.height;
37468     if(typeof size != "undefined"){
37469         this.el.setHeight(size);
37470     }
37471 };
37472 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37473 {
37474     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37475     
37476     
37477     
37478     getBox : function(){
37479         if(this.collapsed){
37480             return this.collapsedEl.getBox();
37481         }
37482         var box = this.el.getBox();
37483         if(this.split){
37484             box.height += this.split.el.getHeight();
37485         }
37486         return box;
37487     },
37488     
37489     updateBox : function(box){
37490         if(this.split && !this.collapsed){
37491             box.height -= this.split.el.getHeight();
37492             this.split.el.setLeft(box.x);
37493             this.split.el.setTop(box.y+box.height);
37494             this.split.el.setWidth(box.width);
37495         }
37496         if(this.collapsed){
37497             this.updateBody(box.width, null);
37498         }
37499         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37500     }
37501 });
37502
37503
37504
37505
37506
37507 Roo.bootstrap.layout.South = function(config){
37508     config.region = 'south';
37509     config.cursor = 's-resize';
37510     Roo.bootstrap.layout.Split.call(this, config);
37511     if(this.split){
37512         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37513         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37514         this.split.el.addClass("roo-layout-split-v");
37515     }
37516     var size = config.initialSize || config.height;
37517     if(typeof size != "undefined"){
37518         this.el.setHeight(size);
37519     }
37520 };
37521
37522 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37523     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37524     getBox : function(){
37525         if(this.collapsed){
37526             return this.collapsedEl.getBox();
37527         }
37528         var box = this.el.getBox();
37529         if(this.split){
37530             var sh = this.split.el.getHeight();
37531             box.height += sh;
37532             box.y -= sh;
37533         }
37534         return box;
37535     },
37536     
37537     updateBox : function(box){
37538         if(this.split && !this.collapsed){
37539             var sh = this.split.el.getHeight();
37540             box.height -= sh;
37541             box.y += sh;
37542             this.split.el.setLeft(box.x);
37543             this.split.el.setTop(box.y-sh);
37544             this.split.el.setWidth(box.width);
37545         }
37546         if(this.collapsed){
37547             this.updateBody(box.width, null);
37548         }
37549         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37550     }
37551 });
37552
37553 Roo.bootstrap.layout.East = function(config){
37554     config.region = "east";
37555     config.cursor = "e-resize";
37556     Roo.bootstrap.layout.Split.call(this, config);
37557     if(this.split){
37558         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37559         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37560         this.split.el.addClass("roo-layout-split-h");
37561     }
37562     var size = config.initialSize || config.width;
37563     if(typeof size != "undefined"){
37564         this.el.setWidth(size);
37565     }
37566 };
37567 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37568     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37569     getBox : function(){
37570         if(this.collapsed){
37571             return this.collapsedEl.getBox();
37572         }
37573         var box = this.el.getBox();
37574         if(this.split){
37575             var sw = this.split.el.getWidth();
37576             box.width += sw;
37577             box.x -= sw;
37578         }
37579         return box;
37580     },
37581
37582     updateBox : function(box){
37583         if(this.split && !this.collapsed){
37584             var sw = this.split.el.getWidth();
37585             box.width -= sw;
37586             this.split.el.setLeft(box.x);
37587             this.split.el.setTop(box.y);
37588             this.split.el.setHeight(box.height);
37589             box.x += sw;
37590         }
37591         if(this.collapsed){
37592             this.updateBody(null, box.height);
37593         }
37594         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37595     }
37596 });
37597
37598 Roo.bootstrap.layout.West = function(config){
37599     config.region = "west";
37600     config.cursor = "w-resize";
37601     
37602     Roo.bootstrap.layout.Split.call(this, config);
37603     if(this.split){
37604         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37605         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37606         this.split.el.addClass("roo-layout-split-h");
37607     }
37608     
37609 };
37610 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37611     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37612     
37613     onRender: function(ctr, pos)
37614     {
37615         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37616         var size = this.config.initialSize || this.config.width;
37617         if(typeof size != "undefined"){
37618             this.el.setWidth(size);
37619         }
37620     },
37621     
37622     getBox : function(){
37623         if(this.collapsed){
37624             return this.collapsedEl.getBox();
37625         }
37626         var box = this.el.getBox();
37627         if(this.split){
37628             box.width += this.split.el.getWidth();
37629         }
37630         return box;
37631     },
37632     
37633     updateBox : function(box){
37634         if(this.split && !this.collapsed){
37635             var sw = this.split.el.getWidth();
37636             box.width -= sw;
37637             this.split.el.setLeft(box.x+box.width);
37638             this.split.el.setTop(box.y);
37639             this.split.el.setHeight(box.height);
37640         }
37641         if(this.collapsed){
37642             this.updateBody(null, box.height);
37643         }
37644         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37645     }
37646 });Roo.namespace("Roo.bootstrap.panel");/*
37647  * Based on:
37648  * Ext JS Library 1.1.1
37649  * Copyright(c) 2006-2007, Ext JS, LLC.
37650  *
37651  * Originally Released Under LGPL - original licence link has changed is not relivant.
37652  *
37653  * Fork - LGPL
37654  * <script type="text/javascript">
37655  */
37656 /**
37657  * @class Roo.ContentPanel
37658  * @extends Roo.util.Observable
37659  * A basic ContentPanel element.
37660  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37661  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37662  * @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
37663  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37664  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37665  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37666  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37667  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37668  * @cfg {String} title          The title for this panel
37669  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37670  * @cfg {String} url            Calls {@link #setUrl} with this value
37671  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37672  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37673  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37674  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37675  * @cfg {Boolean} badges render the badges
37676
37677  * @constructor
37678  * Create a new ContentPanel.
37679  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37680  * @param {String/Object} config A string to set only the title or a config object
37681  * @param {String} content (optional) Set the HTML content for this panel
37682  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37683  */
37684 Roo.bootstrap.panel.Content = function( config){
37685     
37686     this.tpl = config.tpl || false;
37687     
37688     var el = config.el;
37689     var content = config.content;
37690
37691     if(config.autoCreate){ // xtype is available if this is called from factory
37692         el = Roo.id();
37693     }
37694     this.el = Roo.get(el);
37695     if(!this.el && config && config.autoCreate){
37696         if(typeof config.autoCreate == "object"){
37697             if(!config.autoCreate.id){
37698                 config.autoCreate.id = config.id||el;
37699             }
37700             this.el = Roo.DomHelper.append(document.body,
37701                         config.autoCreate, true);
37702         }else{
37703             var elcfg =  {   tag: "div",
37704                             cls: "roo-layout-inactive-content",
37705                             id: config.id||el
37706                             };
37707             if (config.html) {
37708                 elcfg.html = config.html;
37709                 
37710             }
37711                         
37712             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37713         }
37714     } 
37715     this.closable = false;
37716     this.loaded = false;
37717     this.active = false;
37718    
37719       
37720     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37721         
37722         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37723         
37724         this.wrapEl = this.el; //this.el.wrap();
37725         var ti = [];
37726         if (config.toolbar.items) {
37727             ti = config.toolbar.items ;
37728             delete config.toolbar.items ;
37729         }
37730         
37731         var nitems = [];
37732         this.toolbar.render(this.wrapEl, 'before');
37733         for(var i =0;i < ti.length;i++) {
37734           //  Roo.log(['add child', items[i]]);
37735             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37736         }
37737         this.toolbar.items = nitems;
37738         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37739         delete config.toolbar;
37740         
37741     }
37742     /*
37743     // xtype created footer. - not sure if will work as we normally have to render first..
37744     if (this.footer && !this.footer.el && this.footer.xtype) {
37745         if (!this.wrapEl) {
37746             this.wrapEl = this.el.wrap();
37747         }
37748     
37749         this.footer.container = this.wrapEl.createChild();
37750          
37751         this.footer = Roo.factory(this.footer, Roo);
37752         
37753     }
37754     */
37755     
37756      if(typeof config == "string"){
37757         this.title = config;
37758     }else{
37759         Roo.apply(this, config);
37760     }
37761     
37762     if(this.resizeEl){
37763         this.resizeEl = Roo.get(this.resizeEl, true);
37764     }else{
37765         this.resizeEl = this.el;
37766     }
37767     // handle view.xtype
37768     
37769  
37770     
37771     
37772     this.addEvents({
37773         /**
37774          * @event activate
37775          * Fires when this panel is activated. 
37776          * @param {Roo.ContentPanel} this
37777          */
37778         "activate" : true,
37779         /**
37780          * @event deactivate
37781          * Fires when this panel is activated. 
37782          * @param {Roo.ContentPanel} this
37783          */
37784         "deactivate" : true,
37785
37786         /**
37787          * @event resize
37788          * Fires when this panel is resized if fitToFrame is true.
37789          * @param {Roo.ContentPanel} this
37790          * @param {Number} width The width after any component adjustments
37791          * @param {Number} height The height after any component adjustments
37792          */
37793         "resize" : true,
37794         
37795          /**
37796          * @event render
37797          * Fires when this tab is created
37798          * @param {Roo.ContentPanel} this
37799          */
37800         "render" : true
37801         
37802         
37803         
37804     });
37805     
37806
37807     
37808     
37809     if(this.autoScroll){
37810         this.resizeEl.setStyle("overflow", "auto");
37811     } else {
37812         // fix randome scrolling
37813         //this.el.on('scroll', function() {
37814         //    Roo.log('fix random scolling');
37815         //    this.scrollTo('top',0); 
37816         //});
37817     }
37818     content = content || this.content;
37819     if(content){
37820         this.setContent(content);
37821     }
37822     if(config && config.url){
37823         this.setUrl(this.url, this.params, this.loadOnce);
37824     }
37825     
37826     
37827     
37828     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37829     
37830     if (this.view && typeof(this.view.xtype) != 'undefined') {
37831         this.view.el = this.el.appendChild(document.createElement("div"));
37832         this.view = Roo.factory(this.view); 
37833         this.view.render  &&  this.view.render(false, '');  
37834     }
37835     
37836     
37837     this.fireEvent('render', this);
37838 };
37839
37840 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37841     
37842     tabTip : '',
37843     
37844     setRegion : function(region){
37845         this.region = region;
37846         this.setActiveClass(region && !this.background);
37847     },
37848     
37849     
37850     setActiveClass: function(state)
37851     {
37852         if(state){
37853            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37854            this.el.setStyle('position','relative');
37855         }else{
37856            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37857            this.el.setStyle('position', 'absolute');
37858         } 
37859     },
37860     
37861     /**
37862      * Returns the toolbar for this Panel if one was configured. 
37863      * @return {Roo.Toolbar} 
37864      */
37865     getToolbar : function(){
37866         return this.toolbar;
37867     },
37868     
37869     setActiveState : function(active)
37870     {
37871         this.active = active;
37872         this.setActiveClass(active);
37873         if(!active){
37874             if(this.fireEvent("deactivate", this) === false){
37875                 return false;
37876             }
37877             return true;
37878         }
37879         this.fireEvent("activate", this);
37880         return true;
37881     },
37882     /**
37883      * Updates this panel's element
37884      * @param {String} content The new content
37885      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37886     */
37887     setContent : function(content, loadScripts){
37888         this.el.update(content, loadScripts);
37889     },
37890
37891     ignoreResize : function(w, h){
37892         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37893             return true;
37894         }else{
37895             this.lastSize = {width: w, height: h};
37896             return false;
37897         }
37898     },
37899     /**
37900      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37901      * @return {Roo.UpdateManager} The UpdateManager
37902      */
37903     getUpdateManager : function(){
37904         return this.el.getUpdateManager();
37905     },
37906      /**
37907      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37908      * @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:
37909 <pre><code>
37910 panel.load({
37911     url: "your-url.php",
37912     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37913     callback: yourFunction,
37914     scope: yourObject, //(optional scope)
37915     discardUrl: false,
37916     nocache: false,
37917     text: "Loading...",
37918     timeout: 30,
37919     scripts: false
37920 });
37921 </code></pre>
37922      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37923      * 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.
37924      * @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}
37925      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37926      * @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.
37927      * @return {Roo.ContentPanel} this
37928      */
37929     load : function(){
37930         var um = this.el.getUpdateManager();
37931         um.update.apply(um, arguments);
37932         return this;
37933     },
37934
37935
37936     /**
37937      * 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.
37938      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37939      * @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)
37940      * @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)
37941      * @return {Roo.UpdateManager} The UpdateManager
37942      */
37943     setUrl : function(url, params, loadOnce){
37944         if(this.refreshDelegate){
37945             this.removeListener("activate", this.refreshDelegate);
37946         }
37947         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37948         this.on("activate", this.refreshDelegate);
37949         return this.el.getUpdateManager();
37950     },
37951     
37952     _handleRefresh : function(url, params, loadOnce){
37953         if(!loadOnce || !this.loaded){
37954             var updater = this.el.getUpdateManager();
37955             updater.update(url, params, this._setLoaded.createDelegate(this));
37956         }
37957     },
37958     
37959     _setLoaded : function(){
37960         this.loaded = true;
37961     }, 
37962     
37963     /**
37964      * Returns this panel's id
37965      * @return {String} 
37966      */
37967     getId : function(){
37968         return this.el.id;
37969     },
37970     
37971     /** 
37972      * Returns this panel's element - used by regiosn to add.
37973      * @return {Roo.Element} 
37974      */
37975     getEl : function(){
37976         return this.wrapEl || this.el;
37977     },
37978     
37979    
37980     
37981     adjustForComponents : function(width, height)
37982     {
37983         //Roo.log('adjustForComponents ');
37984         if(this.resizeEl != this.el){
37985             width -= this.el.getFrameWidth('lr');
37986             height -= this.el.getFrameWidth('tb');
37987         }
37988         if(this.toolbar){
37989             var te = this.toolbar.getEl();
37990             te.setWidth(width);
37991             height -= te.getHeight();
37992         }
37993         if(this.footer){
37994             var te = this.footer.getEl();
37995             te.setWidth(width);
37996             height -= te.getHeight();
37997         }
37998         
37999         
38000         if(this.adjustments){
38001             width += this.adjustments[0];
38002             height += this.adjustments[1];
38003         }
38004         return {"width": width, "height": height};
38005     },
38006     
38007     setSize : function(width, height){
38008         if(this.fitToFrame && !this.ignoreResize(width, height)){
38009             if(this.fitContainer && this.resizeEl != this.el){
38010                 this.el.setSize(width, height);
38011             }
38012             var size = this.adjustForComponents(width, height);
38013             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
38014             this.fireEvent('resize', this, size.width, size.height);
38015         }
38016     },
38017     
38018     /**
38019      * Returns this panel's title
38020      * @return {String} 
38021      */
38022     getTitle : function(){
38023         
38024         if (typeof(this.title) != 'object') {
38025             return this.title;
38026         }
38027         
38028         var t = '';
38029         for (var k in this.title) {
38030             if (!this.title.hasOwnProperty(k)) {
38031                 continue;
38032             }
38033             
38034             if (k.indexOf('-') >= 0) {
38035                 var s = k.split('-');
38036                 for (var i = 0; i<s.length; i++) {
38037                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
38038                 }
38039             } else {
38040                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
38041             }
38042         }
38043         return t;
38044     },
38045     
38046     /**
38047      * Set this panel's title
38048      * @param {String} title
38049      */
38050     setTitle : function(title){
38051         this.title = title;
38052         if(this.region){
38053             this.region.updatePanelTitle(this, title);
38054         }
38055     },
38056     
38057     /**
38058      * Returns true is this panel was configured to be closable
38059      * @return {Boolean} 
38060      */
38061     isClosable : function(){
38062         return this.closable;
38063     },
38064     
38065     beforeSlide : function(){
38066         this.el.clip();
38067         this.resizeEl.clip();
38068     },
38069     
38070     afterSlide : function(){
38071         this.el.unclip();
38072         this.resizeEl.unclip();
38073     },
38074     
38075     /**
38076      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
38077      *   Will fail silently if the {@link #setUrl} method has not been called.
38078      *   This does not activate the panel, just updates its content.
38079      */
38080     refresh : function(){
38081         if(this.refreshDelegate){
38082            this.loaded = false;
38083            this.refreshDelegate();
38084         }
38085     },
38086     
38087     /**
38088      * Destroys this panel
38089      */
38090     destroy : function(){
38091         this.el.removeAllListeners();
38092         var tempEl = document.createElement("span");
38093         tempEl.appendChild(this.el.dom);
38094         tempEl.innerHTML = "";
38095         this.el.remove();
38096         this.el = null;
38097     },
38098     
38099     /**
38100      * form - if the content panel contains a form - this is a reference to it.
38101      * @type {Roo.form.Form}
38102      */
38103     form : false,
38104     /**
38105      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
38106      *    This contains a reference to it.
38107      * @type {Roo.View}
38108      */
38109     view : false,
38110     
38111       /**
38112      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
38113      * <pre><code>
38114
38115 layout.addxtype({
38116        xtype : 'Form',
38117        items: [ .... ]
38118    }
38119 );
38120
38121 </code></pre>
38122      * @param {Object} cfg Xtype definition of item to add.
38123      */
38124     
38125     
38126     getChildContainer: function () {
38127         return this.getEl();
38128     }
38129     
38130     
38131     /*
38132         var  ret = new Roo.factory(cfg);
38133         return ret;
38134         
38135         
38136         // add form..
38137         if (cfg.xtype.match(/^Form$/)) {
38138             
38139             var el;
38140             //if (this.footer) {
38141             //    el = this.footer.container.insertSibling(false, 'before');
38142             //} else {
38143                 el = this.el.createChild();
38144             //}
38145
38146             this.form = new  Roo.form.Form(cfg);
38147             
38148             
38149             if ( this.form.allItems.length) {
38150                 this.form.render(el.dom);
38151             }
38152             return this.form;
38153         }
38154         // should only have one of theses..
38155         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
38156             // views.. should not be just added - used named prop 'view''
38157             
38158             cfg.el = this.el.appendChild(document.createElement("div"));
38159             // factory?
38160             
38161             var ret = new Roo.factory(cfg);
38162              
38163              ret.render && ret.render(false, ''); // render blank..
38164             this.view = ret;
38165             return ret;
38166         }
38167         return false;
38168     }
38169     \*/
38170 });
38171  
38172 /**
38173  * @class Roo.bootstrap.panel.Grid
38174  * @extends Roo.bootstrap.panel.Content
38175  * @constructor
38176  * Create a new GridPanel.
38177  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
38178  * @param {Object} config A the config object
38179   
38180  */
38181
38182
38183
38184 Roo.bootstrap.panel.Grid = function(config)
38185 {
38186     
38187       
38188     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
38189         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
38190
38191     config.el = this.wrapper;
38192     //this.el = this.wrapper;
38193     
38194       if (config.container) {
38195         // ctor'ed from a Border/panel.grid
38196         
38197         
38198         this.wrapper.setStyle("overflow", "hidden");
38199         this.wrapper.addClass('roo-grid-container');
38200
38201     }
38202     
38203     
38204     if(config.toolbar){
38205         var tool_el = this.wrapper.createChild();    
38206         this.toolbar = Roo.factory(config.toolbar);
38207         var ti = [];
38208         if (config.toolbar.items) {
38209             ti = config.toolbar.items ;
38210             delete config.toolbar.items ;
38211         }
38212         
38213         var nitems = [];
38214         this.toolbar.render(tool_el);
38215         for(var i =0;i < ti.length;i++) {
38216           //  Roo.log(['add child', items[i]]);
38217             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
38218         }
38219         this.toolbar.items = nitems;
38220         
38221         delete config.toolbar;
38222     }
38223     
38224     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
38225     config.grid.scrollBody = true;;
38226     config.grid.monitorWindowResize = false; // turn off autosizing
38227     config.grid.autoHeight = false;
38228     config.grid.autoWidth = false;
38229     
38230     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
38231     
38232     if (config.background) {
38233         // render grid on panel activation (if panel background)
38234         this.on('activate', function(gp) {
38235             if (!gp.grid.rendered) {
38236                 gp.grid.render(this.wrapper);
38237                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
38238             }
38239         });
38240             
38241     } else {
38242         this.grid.render(this.wrapper);
38243         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
38244
38245     }
38246     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
38247     // ??? needed ??? config.el = this.wrapper;
38248     
38249     
38250     
38251   
38252     // xtype created footer. - not sure if will work as we normally have to render first..
38253     if (this.footer && !this.footer.el && this.footer.xtype) {
38254         
38255         var ctr = this.grid.getView().getFooterPanel(true);
38256         this.footer.dataSource = this.grid.dataSource;
38257         this.footer = Roo.factory(this.footer, Roo);
38258         this.footer.render(ctr);
38259         
38260     }
38261     
38262     
38263     
38264     
38265      
38266 };
38267
38268 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
38269     getId : function(){
38270         return this.grid.id;
38271     },
38272     
38273     /**
38274      * Returns the grid for this panel
38275      * @return {Roo.bootstrap.Table} 
38276      */
38277     getGrid : function(){
38278         return this.grid;    
38279     },
38280     
38281     setSize : function(width, height){
38282         if(!this.ignoreResize(width, height)){
38283             var grid = this.grid;
38284             var size = this.adjustForComponents(width, height);
38285             var gridel = grid.getGridEl();
38286             gridel.setSize(size.width, size.height);
38287             /*
38288             var thd = grid.getGridEl().select('thead',true).first();
38289             var tbd = grid.getGridEl().select('tbody', true).first();
38290             if (tbd) {
38291                 tbd.setSize(width, height - thd.getHeight());
38292             }
38293             */
38294             grid.autoSize();
38295         }
38296     },
38297      
38298     
38299     
38300     beforeSlide : function(){
38301         this.grid.getView().scroller.clip();
38302     },
38303     
38304     afterSlide : function(){
38305         this.grid.getView().scroller.unclip();
38306     },
38307     
38308     destroy : function(){
38309         this.grid.destroy();
38310         delete this.grid;
38311         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
38312     }
38313 });
38314
38315 /**
38316  * @class Roo.bootstrap.panel.Nest
38317  * @extends Roo.bootstrap.panel.Content
38318  * @constructor
38319  * Create a new Panel, that can contain a layout.Border.
38320  * 
38321  * 
38322  * @param {Roo.BorderLayout} layout The layout for this panel
38323  * @param {String/Object} config A string to set only the title or a config object
38324  */
38325 Roo.bootstrap.panel.Nest = function(config)
38326 {
38327     // construct with only one argument..
38328     /* FIXME - implement nicer consturctors
38329     if (layout.layout) {
38330         config = layout;
38331         layout = config.layout;
38332         delete config.layout;
38333     }
38334     if (layout.xtype && !layout.getEl) {
38335         // then layout needs constructing..
38336         layout = Roo.factory(layout, Roo);
38337     }
38338     */
38339     
38340     config.el =  config.layout.getEl();
38341     
38342     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
38343     
38344     config.layout.monitorWindowResize = false; // turn off autosizing
38345     this.layout = config.layout;
38346     this.layout.getEl().addClass("roo-layout-nested-layout");
38347     this.layout.parent = this;
38348     
38349     
38350     
38351     
38352 };
38353
38354 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
38355
38356     setSize : function(width, height){
38357         if(!this.ignoreResize(width, height)){
38358             var size = this.adjustForComponents(width, height);
38359             var el = this.layout.getEl();
38360             if (size.height < 1) {
38361                 el.setWidth(size.width);   
38362             } else {
38363                 el.setSize(size.width, size.height);
38364             }
38365             var touch = el.dom.offsetWidth;
38366             this.layout.layout();
38367             // ie requires a double layout on the first pass
38368             if(Roo.isIE && !this.initialized){
38369                 this.initialized = true;
38370                 this.layout.layout();
38371             }
38372         }
38373     },
38374     
38375     // activate all subpanels if not currently active..
38376     
38377     setActiveState : function(active){
38378         this.active = active;
38379         this.setActiveClass(active);
38380         
38381         if(!active){
38382             this.fireEvent("deactivate", this);
38383             return;
38384         }
38385         
38386         this.fireEvent("activate", this);
38387         // not sure if this should happen before or after..
38388         if (!this.layout) {
38389             return; // should not happen..
38390         }
38391         var reg = false;
38392         for (var r in this.layout.regions) {
38393             reg = this.layout.getRegion(r);
38394             if (reg.getActivePanel()) {
38395                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
38396                 reg.setActivePanel(reg.getActivePanel());
38397                 continue;
38398             }
38399             if (!reg.panels.length) {
38400                 continue;
38401             }
38402             reg.showPanel(reg.getPanel(0));
38403         }
38404         
38405         
38406         
38407         
38408     },
38409     
38410     /**
38411      * Returns the nested BorderLayout for this panel
38412      * @return {Roo.BorderLayout} 
38413      */
38414     getLayout : function(){
38415         return this.layout;
38416     },
38417     
38418      /**
38419      * Adds a xtype elements to the layout of the nested panel
38420      * <pre><code>
38421
38422 panel.addxtype({
38423        xtype : 'ContentPanel',
38424        region: 'west',
38425        items: [ .... ]
38426    }
38427 );
38428
38429 panel.addxtype({
38430         xtype : 'NestedLayoutPanel',
38431         region: 'west',
38432         layout: {
38433            center: { },
38434            west: { }   
38435         },
38436         items : [ ... list of content panels or nested layout panels.. ]
38437    }
38438 );
38439 </code></pre>
38440      * @param {Object} cfg Xtype definition of item to add.
38441      */
38442     addxtype : function(cfg) {
38443         return this.layout.addxtype(cfg);
38444     
38445     }
38446 });/*
38447  * Based on:
38448  * Ext JS Library 1.1.1
38449  * Copyright(c) 2006-2007, Ext JS, LLC.
38450  *
38451  * Originally Released Under LGPL - original licence link has changed is not relivant.
38452  *
38453  * Fork - LGPL
38454  * <script type="text/javascript">
38455  */
38456 /**
38457  * @class Roo.TabPanel
38458  * @extends Roo.util.Observable
38459  * A lightweight tab container.
38460  * <br><br>
38461  * Usage:
38462  * <pre><code>
38463 // basic tabs 1, built from existing content
38464 var tabs = new Roo.TabPanel("tabs1");
38465 tabs.addTab("script", "View Script");
38466 tabs.addTab("markup", "View Markup");
38467 tabs.activate("script");
38468
38469 // more advanced tabs, built from javascript
38470 var jtabs = new Roo.TabPanel("jtabs");
38471 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38472
38473 // set up the UpdateManager
38474 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38475 var updater = tab2.getUpdateManager();
38476 updater.setDefaultUrl("ajax1.htm");
38477 tab2.on('activate', updater.refresh, updater, true);
38478
38479 // Use setUrl for Ajax loading
38480 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38481 tab3.setUrl("ajax2.htm", null, true);
38482
38483 // Disabled tab
38484 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38485 tab4.disable();
38486
38487 jtabs.activate("jtabs-1");
38488  * </code></pre>
38489  * @constructor
38490  * Create a new TabPanel.
38491  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38492  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38493  */
38494 Roo.bootstrap.panel.Tabs = function(config){
38495     /**
38496     * The container element for this TabPanel.
38497     * @type Roo.Element
38498     */
38499     this.el = Roo.get(config.el);
38500     delete config.el;
38501     if(config){
38502         if(typeof config == "boolean"){
38503             this.tabPosition = config ? "bottom" : "top";
38504         }else{
38505             Roo.apply(this, config);
38506         }
38507     }
38508     
38509     if(this.tabPosition == "bottom"){
38510         // if tabs are at the bottom = create the body first.
38511         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38512         this.el.addClass("roo-tabs-bottom");
38513     }
38514     // next create the tabs holders
38515     
38516     if (this.tabPosition == "west"){
38517         
38518         var reg = this.region; // fake it..
38519         while (reg) {
38520             if (!reg.mgr.parent) {
38521                 break;
38522             }
38523             reg = reg.mgr.parent.region;
38524         }
38525         Roo.log("got nest?");
38526         Roo.log(reg);
38527         if (reg.mgr.getRegion('west')) {
38528             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38529             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38530             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38531             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38532             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38533         
38534             
38535         }
38536         
38537         
38538     } else {
38539      
38540         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38541         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38542         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38543         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38544     }
38545     
38546     
38547     if(Roo.isIE){
38548         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38549     }
38550     
38551     // finally - if tabs are at the top, then create the body last..
38552     if(this.tabPosition != "bottom"){
38553         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38554          * @type Roo.Element
38555          */
38556         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38557         this.el.addClass("roo-tabs-top");
38558     }
38559     this.items = [];
38560
38561     this.bodyEl.setStyle("position", "relative");
38562
38563     this.active = null;
38564     this.activateDelegate = this.activate.createDelegate(this);
38565
38566     this.addEvents({
38567         /**
38568          * @event tabchange
38569          * Fires when the active tab changes
38570          * @param {Roo.TabPanel} this
38571          * @param {Roo.TabPanelItem} activePanel The new active tab
38572          */
38573         "tabchange": true,
38574         /**
38575          * @event beforetabchange
38576          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38577          * @param {Roo.TabPanel} this
38578          * @param {Object} e Set cancel to true on this object to cancel the tab change
38579          * @param {Roo.TabPanelItem} tab The tab being changed to
38580          */
38581         "beforetabchange" : true
38582     });
38583
38584     Roo.EventManager.onWindowResize(this.onResize, this);
38585     this.cpad = this.el.getPadding("lr");
38586     this.hiddenCount = 0;
38587
38588
38589     // toolbar on the tabbar support...
38590     if (this.toolbar) {
38591         alert("no toolbar support yet");
38592         this.toolbar  = false;
38593         /*
38594         var tcfg = this.toolbar;
38595         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38596         this.toolbar = new Roo.Toolbar(tcfg);
38597         if (Roo.isSafari) {
38598             var tbl = tcfg.container.child('table', true);
38599             tbl.setAttribute('width', '100%');
38600         }
38601         */
38602         
38603     }
38604    
38605
38606
38607     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38608 };
38609
38610 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38611     /*
38612      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38613      */
38614     tabPosition : "top",
38615     /*
38616      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38617      */
38618     currentTabWidth : 0,
38619     /*
38620      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38621      */
38622     minTabWidth : 40,
38623     /*
38624      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38625      */
38626     maxTabWidth : 250,
38627     /*
38628      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38629      */
38630     preferredTabWidth : 175,
38631     /*
38632      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38633      */
38634     resizeTabs : false,
38635     /*
38636      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38637      */
38638     monitorResize : true,
38639     /*
38640      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38641      */
38642     toolbar : false,  // set by caller..
38643     
38644     region : false, /// set by caller
38645     
38646     disableTooltips : true, // not used yet...
38647
38648     /**
38649      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38650      * @param {String} id The id of the div to use <b>or create</b>
38651      * @param {String} text The text for the tab
38652      * @param {String} content (optional) Content to put in the TabPanelItem body
38653      * @param {Boolean} closable (optional) True to create a close icon on the tab
38654      * @return {Roo.TabPanelItem} The created TabPanelItem
38655      */
38656     addTab : function(id, text, content, closable, tpl)
38657     {
38658         var item = new Roo.bootstrap.panel.TabItem({
38659             panel: this,
38660             id : id,
38661             text : text,
38662             closable : closable,
38663             tpl : tpl
38664         });
38665         this.addTabItem(item);
38666         if(content){
38667             item.setContent(content);
38668         }
38669         return item;
38670     },
38671
38672     /**
38673      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38674      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38675      * @return {Roo.TabPanelItem}
38676      */
38677     getTab : function(id){
38678         return this.items[id];
38679     },
38680
38681     /**
38682      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38683      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38684      */
38685     hideTab : function(id){
38686         var t = this.items[id];
38687         if(!t.isHidden()){
38688            t.setHidden(true);
38689            this.hiddenCount++;
38690            this.autoSizeTabs();
38691         }
38692     },
38693
38694     /**
38695      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38696      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38697      */
38698     unhideTab : function(id){
38699         var t = this.items[id];
38700         if(t.isHidden()){
38701            t.setHidden(false);
38702            this.hiddenCount--;
38703            this.autoSizeTabs();
38704         }
38705     },
38706
38707     /**
38708      * Adds an existing {@link Roo.TabPanelItem}.
38709      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38710      */
38711     addTabItem : function(item)
38712     {
38713         this.items[item.id] = item;
38714         this.items.push(item);
38715         this.autoSizeTabs();
38716       //  if(this.resizeTabs){
38717     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38718   //         this.autoSizeTabs();
38719 //        }else{
38720 //            item.autoSize();
38721        // }
38722     },
38723
38724     /**
38725      * Removes a {@link Roo.TabPanelItem}.
38726      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38727      */
38728     removeTab : function(id){
38729         var items = this.items;
38730         var tab = items[id];
38731         if(!tab) { return; }
38732         var index = items.indexOf(tab);
38733         if(this.active == tab && items.length > 1){
38734             var newTab = this.getNextAvailable(index);
38735             if(newTab) {
38736                 newTab.activate();
38737             }
38738         }
38739         this.stripEl.dom.removeChild(tab.pnode.dom);
38740         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38741             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38742         }
38743         items.splice(index, 1);
38744         delete this.items[tab.id];
38745         tab.fireEvent("close", tab);
38746         tab.purgeListeners();
38747         this.autoSizeTabs();
38748     },
38749
38750     getNextAvailable : function(start){
38751         var items = this.items;
38752         var index = start;
38753         // look for a next tab that will slide over to
38754         // replace the one being removed
38755         while(index < items.length){
38756             var item = items[++index];
38757             if(item && !item.isHidden()){
38758                 return item;
38759             }
38760         }
38761         // if one isn't found select the previous tab (on the left)
38762         index = start;
38763         while(index >= 0){
38764             var item = items[--index];
38765             if(item && !item.isHidden()){
38766                 return item;
38767             }
38768         }
38769         return null;
38770     },
38771
38772     /**
38773      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38774      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38775      */
38776     disableTab : function(id){
38777         var tab = this.items[id];
38778         if(tab && this.active != tab){
38779             tab.disable();
38780         }
38781     },
38782
38783     /**
38784      * Enables a {@link Roo.TabPanelItem} that is disabled.
38785      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38786      */
38787     enableTab : function(id){
38788         var tab = this.items[id];
38789         tab.enable();
38790     },
38791
38792     /**
38793      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38794      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38795      * @return {Roo.TabPanelItem} The TabPanelItem.
38796      */
38797     activate : function(id)
38798     {
38799         //Roo.log('activite:'  + id);
38800         
38801         var tab = this.items[id];
38802         if(!tab){
38803             return null;
38804         }
38805         if(tab == this.active || tab.disabled){
38806             return tab;
38807         }
38808         var e = {};
38809         this.fireEvent("beforetabchange", this, e, tab);
38810         if(e.cancel !== true && !tab.disabled){
38811             if(this.active){
38812                 this.active.hide();
38813             }
38814             this.active = this.items[id];
38815             this.active.show();
38816             this.fireEvent("tabchange", this, this.active);
38817         }
38818         return tab;
38819     },
38820
38821     /**
38822      * Gets the active {@link Roo.TabPanelItem}.
38823      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38824      */
38825     getActiveTab : function(){
38826         return this.active;
38827     },
38828
38829     /**
38830      * Updates the tab body element to fit the height of the container element
38831      * for overflow scrolling
38832      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38833      */
38834     syncHeight : function(targetHeight){
38835         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38836         var bm = this.bodyEl.getMargins();
38837         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38838         this.bodyEl.setHeight(newHeight);
38839         return newHeight;
38840     },
38841
38842     onResize : function(){
38843         if(this.monitorResize){
38844             this.autoSizeTabs();
38845         }
38846     },
38847
38848     /**
38849      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38850      */
38851     beginUpdate : function(){
38852         this.updating = true;
38853     },
38854
38855     /**
38856      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38857      */
38858     endUpdate : function(){
38859         this.updating = false;
38860         this.autoSizeTabs();
38861     },
38862
38863     /**
38864      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38865      */
38866     autoSizeTabs : function()
38867     {
38868         var count = this.items.length;
38869         var vcount = count - this.hiddenCount;
38870         
38871         if (vcount < 2) {
38872             this.stripEl.hide();
38873         } else {
38874             this.stripEl.show();
38875         }
38876         
38877         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38878             return;
38879         }
38880         
38881         
38882         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38883         var availWidth = Math.floor(w / vcount);
38884         var b = this.stripBody;
38885         if(b.getWidth() > w){
38886             var tabs = this.items;
38887             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38888             if(availWidth < this.minTabWidth){
38889                 /*if(!this.sleft){    // incomplete scrolling code
38890                     this.createScrollButtons();
38891                 }
38892                 this.showScroll();
38893                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38894             }
38895         }else{
38896             if(this.currentTabWidth < this.preferredTabWidth){
38897                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38898             }
38899         }
38900     },
38901
38902     /**
38903      * Returns the number of tabs in this TabPanel.
38904      * @return {Number}
38905      */
38906      getCount : function(){
38907          return this.items.length;
38908      },
38909
38910     /**
38911      * Resizes all the tabs to the passed width
38912      * @param {Number} The new width
38913      */
38914     setTabWidth : function(width){
38915         this.currentTabWidth = width;
38916         for(var i = 0, len = this.items.length; i < len; i++) {
38917                 if(!this.items[i].isHidden()) {
38918                 this.items[i].setWidth(width);
38919             }
38920         }
38921     },
38922
38923     /**
38924      * Destroys this TabPanel
38925      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38926      */
38927     destroy : function(removeEl){
38928         Roo.EventManager.removeResizeListener(this.onResize, this);
38929         for(var i = 0, len = this.items.length; i < len; i++){
38930             this.items[i].purgeListeners();
38931         }
38932         if(removeEl === true){
38933             this.el.update("");
38934             this.el.remove();
38935         }
38936     },
38937     
38938     createStrip : function(container)
38939     {
38940         var strip = document.createElement("nav");
38941         strip.className = Roo.bootstrap.version == 4 ?
38942             "navbar-light bg-light" : 
38943             "navbar navbar-default"; //"x-tabs-wrap";
38944         container.appendChild(strip);
38945         return strip;
38946     },
38947     
38948     createStripList : function(strip)
38949     {
38950         // div wrapper for retard IE
38951         // returns the "tr" element.
38952         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38953         //'<div class="x-tabs-strip-wrap">'+
38954           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38955           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38956         return strip.firstChild; //.firstChild.firstChild.firstChild;
38957     },
38958     createBody : function(container)
38959     {
38960         var body = document.createElement("div");
38961         Roo.id(body, "tab-body");
38962         //Roo.fly(body).addClass("x-tabs-body");
38963         Roo.fly(body).addClass("tab-content");
38964         container.appendChild(body);
38965         return body;
38966     },
38967     createItemBody :function(bodyEl, id){
38968         var body = Roo.getDom(id);
38969         if(!body){
38970             body = document.createElement("div");
38971             body.id = id;
38972         }
38973         //Roo.fly(body).addClass("x-tabs-item-body");
38974         Roo.fly(body).addClass("tab-pane");
38975          bodyEl.insertBefore(body, bodyEl.firstChild);
38976         return body;
38977     },
38978     /** @private */
38979     createStripElements :  function(stripEl, text, closable, tpl)
38980     {
38981         var td = document.createElement("li"); // was td..
38982         td.className = 'nav-item';
38983         
38984         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38985         
38986         
38987         stripEl.appendChild(td);
38988         /*if(closable){
38989             td.className = "x-tabs-closable";
38990             if(!this.closeTpl){
38991                 this.closeTpl = new Roo.Template(
38992                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38993                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38994                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38995                 );
38996             }
38997             var el = this.closeTpl.overwrite(td, {"text": text});
38998             var close = el.getElementsByTagName("div")[0];
38999             var inner = el.getElementsByTagName("em")[0];
39000             return {"el": el, "close": close, "inner": inner};
39001         } else {
39002         */
39003         // not sure what this is..
39004 //            if(!this.tabTpl){
39005                 //this.tabTpl = new Roo.Template(
39006                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39007                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
39008                 //);
39009 //                this.tabTpl = new Roo.Template(
39010 //                   '<a href="#">' +
39011 //                   '<span unselectable="on"' +
39012 //                            (this.disableTooltips ? '' : ' title="{text}"') +
39013 //                            ' >{text}</span></a>'
39014 //                );
39015 //                
39016 //            }
39017
39018
39019             var template = tpl || this.tabTpl || false;
39020             
39021             if(!template){
39022                 template =  new Roo.Template(
39023                         Roo.bootstrap.version == 4 ? 
39024                             (
39025                                 '<a class="nav-link" href="#" unselectable="on"' +
39026                                      (this.disableTooltips ? '' : ' title="{text}"') +
39027                                      ' >{text}</a>'
39028                             ) : (
39029                                 '<a class="nav-link" href="#">' +
39030                                 '<span unselectable="on"' +
39031                                          (this.disableTooltips ? '' : ' title="{text}"') +
39032                                     ' >{text}</span></a>'
39033                             )
39034                 );
39035             }
39036             
39037             switch (typeof(template)) {
39038                 case 'object' :
39039                     break;
39040                 case 'string' :
39041                     template = new Roo.Template(template);
39042                     break;
39043                 default :
39044                     break;
39045             }
39046             
39047             var el = template.overwrite(td, {"text": text});
39048             
39049             var inner = el.getElementsByTagName("span")[0];
39050             
39051             return {"el": el, "inner": inner};
39052             
39053     }
39054         
39055     
39056 });
39057
39058 /**
39059  * @class Roo.TabPanelItem
39060  * @extends Roo.util.Observable
39061  * Represents an individual item (tab plus body) in a TabPanel.
39062  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
39063  * @param {String} id The id of this TabPanelItem
39064  * @param {String} text The text for the tab of this TabPanelItem
39065  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
39066  */
39067 Roo.bootstrap.panel.TabItem = function(config){
39068     /**
39069      * The {@link Roo.TabPanel} this TabPanelItem belongs to
39070      * @type Roo.TabPanel
39071      */
39072     this.tabPanel = config.panel;
39073     /**
39074      * The id for this TabPanelItem
39075      * @type String
39076      */
39077     this.id = config.id;
39078     /** @private */
39079     this.disabled = false;
39080     /** @private */
39081     this.text = config.text;
39082     /** @private */
39083     this.loaded = false;
39084     this.closable = config.closable;
39085
39086     /**
39087      * The body element for this TabPanelItem.
39088      * @type Roo.Element
39089      */
39090     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
39091     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
39092     this.bodyEl.setStyle("display", "block");
39093     this.bodyEl.setStyle("zoom", "1");
39094     //this.hideAction();
39095
39096     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
39097     /** @private */
39098     this.el = Roo.get(els.el);
39099     this.inner = Roo.get(els.inner, true);
39100      this.textEl = Roo.bootstrap.version == 4 ?
39101         this.el : Roo.get(this.el.dom.firstChild, true);
39102
39103     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
39104     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
39105
39106     
39107 //    this.el.on("mousedown", this.onTabMouseDown, this);
39108     this.el.on("click", this.onTabClick, this);
39109     /** @private */
39110     if(config.closable){
39111         var c = Roo.get(els.close, true);
39112         c.dom.title = this.closeText;
39113         c.addClassOnOver("close-over");
39114         c.on("click", this.closeClick, this);
39115      }
39116
39117     this.addEvents({
39118          /**
39119          * @event activate
39120          * Fires when this tab becomes the active tab.
39121          * @param {Roo.TabPanel} tabPanel The parent TabPanel
39122          * @param {Roo.TabPanelItem} this
39123          */
39124         "activate": true,
39125         /**
39126          * @event beforeclose
39127          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
39128          * @param {Roo.TabPanelItem} this
39129          * @param {Object} e Set cancel to true on this object to cancel the close.
39130          */
39131         "beforeclose": true,
39132         /**
39133          * @event close
39134          * Fires when this tab is closed.
39135          * @param {Roo.TabPanelItem} this
39136          */
39137          "close": true,
39138         /**
39139          * @event deactivate
39140          * Fires when this tab is no longer the active tab.
39141          * @param {Roo.TabPanel} tabPanel The parent TabPanel
39142          * @param {Roo.TabPanelItem} this
39143          */
39144          "deactivate" : true
39145     });
39146     this.hidden = false;
39147
39148     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
39149 };
39150
39151 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
39152            {
39153     purgeListeners : function(){
39154        Roo.util.Observable.prototype.purgeListeners.call(this);
39155        this.el.removeAllListeners();
39156     },
39157     /**
39158      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
39159      */
39160     show : function(){
39161         this.status_node.addClass("active");
39162         this.showAction();
39163         if(Roo.isOpera){
39164             this.tabPanel.stripWrap.repaint();
39165         }
39166         this.fireEvent("activate", this.tabPanel, this);
39167     },
39168
39169     /**
39170      * Returns true if this tab is the active tab.
39171      * @return {Boolean}
39172      */
39173     isActive : function(){
39174         return this.tabPanel.getActiveTab() == this;
39175     },
39176
39177     /**
39178      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
39179      */
39180     hide : function(){
39181         this.status_node.removeClass("active");
39182         this.hideAction();
39183         this.fireEvent("deactivate", this.tabPanel, this);
39184     },
39185
39186     hideAction : function(){
39187         this.bodyEl.hide();
39188         this.bodyEl.setStyle("position", "absolute");
39189         this.bodyEl.setLeft("-20000px");
39190         this.bodyEl.setTop("-20000px");
39191     },
39192
39193     showAction : function(){
39194         this.bodyEl.setStyle("position", "relative");
39195         this.bodyEl.setTop("");
39196         this.bodyEl.setLeft("");
39197         this.bodyEl.show();
39198     },
39199
39200     /**
39201      * Set the tooltip for the tab.
39202      * @param {String} tooltip The tab's tooltip
39203      */
39204     setTooltip : function(text){
39205         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
39206             this.textEl.dom.qtip = text;
39207             this.textEl.dom.removeAttribute('title');
39208         }else{
39209             this.textEl.dom.title = text;
39210         }
39211     },
39212
39213     onTabClick : function(e){
39214         e.preventDefault();
39215         this.tabPanel.activate(this.id);
39216     },
39217
39218     onTabMouseDown : function(e){
39219         e.preventDefault();
39220         this.tabPanel.activate(this.id);
39221     },
39222 /*
39223     getWidth : function(){
39224         return this.inner.getWidth();
39225     },
39226
39227     setWidth : function(width){
39228         var iwidth = width - this.linode.getPadding("lr");
39229         this.inner.setWidth(iwidth);
39230         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
39231         this.linode.setWidth(width);
39232     },
39233 */
39234     /**
39235      * Show or hide the tab
39236      * @param {Boolean} hidden True to hide or false to show.
39237      */
39238     setHidden : function(hidden){
39239         this.hidden = hidden;
39240         this.linode.setStyle("display", hidden ? "none" : "");
39241     },
39242
39243     /**
39244      * Returns true if this tab is "hidden"
39245      * @return {Boolean}
39246      */
39247     isHidden : function(){
39248         return this.hidden;
39249     },
39250
39251     /**
39252      * Returns the text for this tab
39253      * @return {String}
39254      */
39255     getText : function(){
39256         return this.text;
39257     },
39258     /*
39259     autoSize : function(){
39260         //this.el.beginMeasure();
39261         this.textEl.setWidth(1);
39262         /*
39263          *  #2804 [new] Tabs in Roojs
39264          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
39265          */
39266         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
39267         //this.el.endMeasure();
39268     //},
39269
39270     /**
39271      * Sets the text for the tab (Note: this also sets the tooltip text)
39272      * @param {String} text The tab's text and tooltip
39273      */
39274     setText : function(text){
39275         this.text = text;
39276         this.textEl.update(text);
39277         this.setTooltip(text);
39278         //if(!this.tabPanel.resizeTabs){
39279         //    this.autoSize();
39280         //}
39281     },
39282     /**
39283      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
39284      */
39285     activate : function(){
39286         this.tabPanel.activate(this.id);
39287     },
39288
39289     /**
39290      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
39291      */
39292     disable : function(){
39293         if(this.tabPanel.active != this){
39294             this.disabled = true;
39295             this.status_node.addClass("disabled");
39296         }
39297     },
39298
39299     /**
39300      * Enables this TabPanelItem if it was previously disabled.
39301      */
39302     enable : function(){
39303         this.disabled = false;
39304         this.status_node.removeClass("disabled");
39305     },
39306
39307     /**
39308      * Sets the content for this TabPanelItem.
39309      * @param {String} content The content
39310      * @param {Boolean} loadScripts true to look for and load scripts
39311      */
39312     setContent : function(content, loadScripts){
39313         this.bodyEl.update(content, loadScripts);
39314     },
39315
39316     /**
39317      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
39318      * @return {Roo.UpdateManager} The UpdateManager
39319      */
39320     getUpdateManager : function(){
39321         return this.bodyEl.getUpdateManager();
39322     },
39323
39324     /**
39325      * Set a URL to be used to load the content for this TabPanelItem.
39326      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
39327      * @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)
39328      * @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)
39329      * @return {Roo.UpdateManager} The UpdateManager
39330      */
39331     setUrl : function(url, params, loadOnce){
39332         if(this.refreshDelegate){
39333             this.un('activate', this.refreshDelegate);
39334         }
39335         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39336         this.on("activate", this.refreshDelegate);
39337         return this.bodyEl.getUpdateManager();
39338     },
39339
39340     /** @private */
39341     _handleRefresh : function(url, params, loadOnce){
39342         if(!loadOnce || !this.loaded){
39343             var updater = this.bodyEl.getUpdateManager();
39344             updater.update(url, params, this._setLoaded.createDelegate(this));
39345         }
39346     },
39347
39348     /**
39349      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
39350      *   Will fail silently if the setUrl method has not been called.
39351      *   This does not activate the panel, just updates its content.
39352      */
39353     refresh : function(){
39354         if(this.refreshDelegate){
39355            this.loaded = false;
39356            this.refreshDelegate();
39357         }
39358     },
39359
39360     /** @private */
39361     _setLoaded : function(){
39362         this.loaded = true;
39363     },
39364
39365     /** @private */
39366     closeClick : function(e){
39367         var o = {};
39368         e.stopEvent();
39369         this.fireEvent("beforeclose", this, o);
39370         if(o.cancel !== true){
39371             this.tabPanel.removeTab(this.id);
39372         }
39373     },
39374     /**
39375      * The text displayed in the tooltip for the close icon.
39376      * @type String
39377      */
39378     closeText : "Close this tab"
39379 });
39380 /**
39381 *    This script refer to:
39382 *    Title: International Telephone Input
39383 *    Author: Jack O'Connor
39384 *    Code version:  v12.1.12
39385 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39386 **/
39387
39388 Roo.bootstrap.PhoneInputData = function() {
39389     var d = [
39390       [
39391         "Afghanistan (‫افغانستان‬‎)",
39392         "af",
39393         "93"
39394       ],
39395       [
39396         "Albania (Shqipëri)",
39397         "al",
39398         "355"
39399       ],
39400       [
39401         "Algeria (‫الجزائر‬‎)",
39402         "dz",
39403         "213"
39404       ],
39405       [
39406         "American Samoa",
39407         "as",
39408         "1684"
39409       ],
39410       [
39411         "Andorra",
39412         "ad",
39413         "376"
39414       ],
39415       [
39416         "Angola",
39417         "ao",
39418         "244"
39419       ],
39420       [
39421         "Anguilla",
39422         "ai",
39423         "1264"
39424       ],
39425       [
39426         "Antigua and Barbuda",
39427         "ag",
39428         "1268"
39429       ],
39430       [
39431         "Argentina",
39432         "ar",
39433         "54"
39434       ],
39435       [
39436         "Armenia (Հայաստան)",
39437         "am",
39438         "374"
39439       ],
39440       [
39441         "Aruba",
39442         "aw",
39443         "297"
39444       ],
39445       [
39446         "Australia",
39447         "au",
39448         "61",
39449         0
39450       ],
39451       [
39452         "Austria (Österreich)",
39453         "at",
39454         "43"
39455       ],
39456       [
39457         "Azerbaijan (Azərbaycan)",
39458         "az",
39459         "994"
39460       ],
39461       [
39462         "Bahamas",
39463         "bs",
39464         "1242"
39465       ],
39466       [
39467         "Bahrain (‫البحرين‬‎)",
39468         "bh",
39469         "973"
39470       ],
39471       [
39472         "Bangladesh (বাংলাদেশ)",
39473         "bd",
39474         "880"
39475       ],
39476       [
39477         "Barbados",
39478         "bb",
39479         "1246"
39480       ],
39481       [
39482         "Belarus (Беларусь)",
39483         "by",
39484         "375"
39485       ],
39486       [
39487         "Belgium (België)",
39488         "be",
39489         "32"
39490       ],
39491       [
39492         "Belize",
39493         "bz",
39494         "501"
39495       ],
39496       [
39497         "Benin (Bénin)",
39498         "bj",
39499         "229"
39500       ],
39501       [
39502         "Bermuda",
39503         "bm",
39504         "1441"
39505       ],
39506       [
39507         "Bhutan (འབྲུག)",
39508         "bt",
39509         "975"
39510       ],
39511       [
39512         "Bolivia",
39513         "bo",
39514         "591"
39515       ],
39516       [
39517         "Bosnia and Herzegovina (Босна и Херцеговина)",
39518         "ba",
39519         "387"
39520       ],
39521       [
39522         "Botswana",
39523         "bw",
39524         "267"
39525       ],
39526       [
39527         "Brazil (Brasil)",
39528         "br",
39529         "55"
39530       ],
39531       [
39532         "British Indian Ocean Territory",
39533         "io",
39534         "246"
39535       ],
39536       [
39537         "British Virgin Islands",
39538         "vg",
39539         "1284"
39540       ],
39541       [
39542         "Brunei",
39543         "bn",
39544         "673"
39545       ],
39546       [
39547         "Bulgaria (България)",
39548         "bg",
39549         "359"
39550       ],
39551       [
39552         "Burkina Faso",
39553         "bf",
39554         "226"
39555       ],
39556       [
39557         "Burundi (Uburundi)",
39558         "bi",
39559         "257"
39560       ],
39561       [
39562         "Cambodia (កម្ពុជា)",
39563         "kh",
39564         "855"
39565       ],
39566       [
39567         "Cameroon (Cameroun)",
39568         "cm",
39569         "237"
39570       ],
39571       [
39572         "Canada",
39573         "ca",
39574         "1",
39575         1,
39576         ["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"]
39577       ],
39578       [
39579         "Cape Verde (Kabu Verdi)",
39580         "cv",
39581         "238"
39582       ],
39583       [
39584         "Caribbean Netherlands",
39585         "bq",
39586         "599",
39587         1
39588       ],
39589       [
39590         "Cayman Islands",
39591         "ky",
39592         "1345"
39593       ],
39594       [
39595         "Central African Republic (République centrafricaine)",
39596         "cf",
39597         "236"
39598       ],
39599       [
39600         "Chad (Tchad)",
39601         "td",
39602         "235"
39603       ],
39604       [
39605         "Chile",
39606         "cl",
39607         "56"
39608       ],
39609       [
39610         "China (中国)",
39611         "cn",
39612         "86"
39613       ],
39614       [
39615         "Christmas Island",
39616         "cx",
39617         "61",
39618         2
39619       ],
39620       [
39621         "Cocos (Keeling) Islands",
39622         "cc",
39623         "61",
39624         1
39625       ],
39626       [
39627         "Colombia",
39628         "co",
39629         "57"
39630       ],
39631       [
39632         "Comoros (‫جزر القمر‬‎)",
39633         "km",
39634         "269"
39635       ],
39636       [
39637         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39638         "cd",
39639         "243"
39640       ],
39641       [
39642         "Congo (Republic) (Congo-Brazzaville)",
39643         "cg",
39644         "242"
39645       ],
39646       [
39647         "Cook Islands",
39648         "ck",
39649         "682"
39650       ],
39651       [
39652         "Costa Rica",
39653         "cr",
39654         "506"
39655       ],
39656       [
39657         "Côte d’Ivoire",
39658         "ci",
39659         "225"
39660       ],
39661       [
39662         "Croatia (Hrvatska)",
39663         "hr",
39664         "385"
39665       ],
39666       [
39667         "Cuba",
39668         "cu",
39669         "53"
39670       ],
39671       [
39672         "Curaçao",
39673         "cw",
39674         "599",
39675         0
39676       ],
39677       [
39678         "Cyprus (Κύπρος)",
39679         "cy",
39680         "357"
39681       ],
39682       [
39683         "Czech Republic (Česká republika)",
39684         "cz",
39685         "420"
39686       ],
39687       [
39688         "Denmark (Danmark)",
39689         "dk",
39690         "45"
39691       ],
39692       [
39693         "Djibouti",
39694         "dj",
39695         "253"
39696       ],
39697       [
39698         "Dominica",
39699         "dm",
39700         "1767"
39701       ],
39702       [
39703         "Dominican Republic (República Dominicana)",
39704         "do",
39705         "1",
39706         2,
39707         ["809", "829", "849"]
39708       ],
39709       [
39710         "Ecuador",
39711         "ec",
39712         "593"
39713       ],
39714       [
39715         "Egypt (‫مصر‬‎)",
39716         "eg",
39717         "20"
39718       ],
39719       [
39720         "El Salvador",
39721         "sv",
39722         "503"
39723       ],
39724       [
39725         "Equatorial Guinea (Guinea Ecuatorial)",
39726         "gq",
39727         "240"
39728       ],
39729       [
39730         "Eritrea",
39731         "er",
39732         "291"
39733       ],
39734       [
39735         "Estonia (Eesti)",
39736         "ee",
39737         "372"
39738       ],
39739       [
39740         "Ethiopia",
39741         "et",
39742         "251"
39743       ],
39744       [
39745         "Falkland Islands (Islas Malvinas)",
39746         "fk",
39747         "500"
39748       ],
39749       [
39750         "Faroe Islands (Føroyar)",
39751         "fo",
39752         "298"
39753       ],
39754       [
39755         "Fiji",
39756         "fj",
39757         "679"
39758       ],
39759       [
39760         "Finland (Suomi)",
39761         "fi",
39762         "358",
39763         0
39764       ],
39765       [
39766         "France",
39767         "fr",
39768         "33"
39769       ],
39770       [
39771         "French Guiana (Guyane française)",
39772         "gf",
39773         "594"
39774       ],
39775       [
39776         "French Polynesia (Polynésie française)",
39777         "pf",
39778         "689"
39779       ],
39780       [
39781         "Gabon",
39782         "ga",
39783         "241"
39784       ],
39785       [
39786         "Gambia",
39787         "gm",
39788         "220"
39789       ],
39790       [
39791         "Georgia (საქართველო)",
39792         "ge",
39793         "995"
39794       ],
39795       [
39796         "Germany (Deutschland)",
39797         "de",
39798         "49"
39799       ],
39800       [
39801         "Ghana (Gaana)",
39802         "gh",
39803         "233"
39804       ],
39805       [
39806         "Gibraltar",
39807         "gi",
39808         "350"
39809       ],
39810       [
39811         "Greece (Ελλάδα)",
39812         "gr",
39813         "30"
39814       ],
39815       [
39816         "Greenland (Kalaallit Nunaat)",
39817         "gl",
39818         "299"
39819       ],
39820       [
39821         "Grenada",
39822         "gd",
39823         "1473"
39824       ],
39825       [
39826         "Guadeloupe",
39827         "gp",
39828         "590",
39829         0
39830       ],
39831       [
39832         "Guam",
39833         "gu",
39834         "1671"
39835       ],
39836       [
39837         "Guatemala",
39838         "gt",
39839         "502"
39840       ],
39841       [
39842         "Guernsey",
39843         "gg",
39844         "44",
39845         1
39846       ],
39847       [
39848         "Guinea (Guinée)",
39849         "gn",
39850         "224"
39851       ],
39852       [
39853         "Guinea-Bissau (Guiné Bissau)",
39854         "gw",
39855         "245"
39856       ],
39857       [
39858         "Guyana",
39859         "gy",
39860         "592"
39861       ],
39862       [
39863         "Haiti",
39864         "ht",
39865         "509"
39866       ],
39867       [
39868         "Honduras",
39869         "hn",
39870         "504"
39871       ],
39872       [
39873         "Hong Kong (香港)",
39874         "hk",
39875         "852"
39876       ],
39877       [
39878         "Hungary (Magyarország)",
39879         "hu",
39880         "36"
39881       ],
39882       [
39883         "Iceland (Ísland)",
39884         "is",
39885         "354"
39886       ],
39887       [
39888         "India (भारत)",
39889         "in",
39890         "91"
39891       ],
39892       [
39893         "Indonesia",
39894         "id",
39895         "62"
39896       ],
39897       [
39898         "Iran (‫ایران‬‎)",
39899         "ir",
39900         "98"
39901       ],
39902       [
39903         "Iraq (‫العراق‬‎)",
39904         "iq",
39905         "964"
39906       ],
39907       [
39908         "Ireland",
39909         "ie",
39910         "353"
39911       ],
39912       [
39913         "Isle of Man",
39914         "im",
39915         "44",
39916         2
39917       ],
39918       [
39919         "Israel (‫ישראל‬‎)",
39920         "il",
39921         "972"
39922       ],
39923       [
39924         "Italy (Italia)",
39925         "it",
39926         "39",
39927         0
39928       ],
39929       [
39930         "Jamaica",
39931         "jm",
39932         "1876"
39933       ],
39934       [
39935         "Japan (日本)",
39936         "jp",
39937         "81"
39938       ],
39939       [
39940         "Jersey",
39941         "je",
39942         "44",
39943         3
39944       ],
39945       [
39946         "Jordan (‫الأردن‬‎)",
39947         "jo",
39948         "962"
39949       ],
39950       [
39951         "Kazakhstan (Казахстан)",
39952         "kz",
39953         "7",
39954         1
39955       ],
39956       [
39957         "Kenya",
39958         "ke",
39959         "254"
39960       ],
39961       [
39962         "Kiribati",
39963         "ki",
39964         "686"
39965       ],
39966       [
39967         "Kosovo",
39968         "xk",
39969         "383"
39970       ],
39971       [
39972         "Kuwait (‫الكويت‬‎)",
39973         "kw",
39974         "965"
39975       ],
39976       [
39977         "Kyrgyzstan (Кыргызстан)",
39978         "kg",
39979         "996"
39980       ],
39981       [
39982         "Laos (ລາວ)",
39983         "la",
39984         "856"
39985       ],
39986       [
39987         "Latvia (Latvija)",
39988         "lv",
39989         "371"
39990       ],
39991       [
39992         "Lebanon (‫لبنان‬‎)",
39993         "lb",
39994         "961"
39995       ],
39996       [
39997         "Lesotho",
39998         "ls",
39999         "266"
40000       ],
40001       [
40002         "Liberia",
40003         "lr",
40004         "231"
40005       ],
40006       [
40007         "Libya (‫ليبيا‬‎)",
40008         "ly",
40009         "218"
40010       ],
40011       [
40012         "Liechtenstein",
40013         "li",
40014         "423"
40015       ],
40016       [
40017         "Lithuania (Lietuva)",
40018         "lt",
40019         "370"
40020       ],
40021       [
40022         "Luxembourg",
40023         "lu",
40024         "352"
40025       ],
40026       [
40027         "Macau (澳門)",
40028         "mo",
40029         "853"
40030       ],
40031       [
40032         "Macedonia (FYROM) (Македонија)",
40033         "mk",
40034         "389"
40035       ],
40036       [
40037         "Madagascar (Madagasikara)",
40038         "mg",
40039         "261"
40040       ],
40041       [
40042         "Malawi",
40043         "mw",
40044         "265"
40045       ],
40046       [
40047         "Malaysia",
40048         "my",
40049         "60"
40050       ],
40051       [
40052         "Maldives",
40053         "mv",
40054         "960"
40055       ],
40056       [
40057         "Mali",
40058         "ml",
40059         "223"
40060       ],
40061       [
40062         "Malta",
40063         "mt",
40064         "356"
40065       ],
40066       [
40067         "Marshall Islands",
40068         "mh",
40069         "692"
40070       ],
40071       [
40072         "Martinique",
40073         "mq",
40074         "596"
40075       ],
40076       [
40077         "Mauritania (‫موريتانيا‬‎)",
40078         "mr",
40079         "222"
40080       ],
40081       [
40082         "Mauritius (Moris)",
40083         "mu",
40084         "230"
40085       ],
40086       [
40087         "Mayotte",
40088         "yt",
40089         "262",
40090         1
40091       ],
40092       [
40093         "Mexico (México)",
40094         "mx",
40095         "52"
40096       ],
40097       [
40098         "Micronesia",
40099         "fm",
40100         "691"
40101       ],
40102       [
40103         "Moldova (Republica Moldova)",
40104         "md",
40105         "373"
40106       ],
40107       [
40108         "Monaco",
40109         "mc",
40110         "377"
40111       ],
40112       [
40113         "Mongolia (Монгол)",
40114         "mn",
40115         "976"
40116       ],
40117       [
40118         "Montenegro (Crna Gora)",
40119         "me",
40120         "382"
40121       ],
40122       [
40123         "Montserrat",
40124         "ms",
40125         "1664"
40126       ],
40127       [
40128         "Morocco (‫المغرب‬‎)",
40129         "ma",
40130         "212",
40131         0
40132       ],
40133       [
40134         "Mozambique (Moçambique)",
40135         "mz",
40136         "258"
40137       ],
40138       [
40139         "Myanmar (Burma) (မြန်မာ)",
40140         "mm",
40141         "95"
40142       ],
40143       [
40144         "Namibia (Namibië)",
40145         "na",
40146         "264"
40147       ],
40148       [
40149         "Nauru",
40150         "nr",
40151         "674"
40152       ],
40153       [
40154         "Nepal (नेपाल)",
40155         "np",
40156         "977"
40157       ],
40158       [
40159         "Netherlands (Nederland)",
40160         "nl",
40161         "31"
40162       ],
40163       [
40164         "New Caledonia (Nouvelle-Calédonie)",
40165         "nc",
40166         "687"
40167       ],
40168       [
40169         "New Zealand",
40170         "nz",
40171         "64"
40172       ],
40173       [
40174         "Nicaragua",
40175         "ni",
40176         "505"
40177       ],
40178       [
40179         "Niger (Nijar)",
40180         "ne",
40181         "227"
40182       ],
40183       [
40184         "Nigeria",
40185         "ng",
40186         "234"
40187       ],
40188       [
40189         "Niue",
40190         "nu",
40191         "683"
40192       ],
40193       [
40194         "Norfolk Island",
40195         "nf",
40196         "672"
40197       ],
40198       [
40199         "North Korea (조선 민주주의 인민 공화국)",
40200         "kp",
40201         "850"
40202       ],
40203       [
40204         "Northern Mariana Islands",
40205         "mp",
40206         "1670"
40207       ],
40208       [
40209         "Norway (Norge)",
40210         "no",
40211         "47",
40212         0
40213       ],
40214       [
40215         "Oman (‫عُمان‬‎)",
40216         "om",
40217         "968"
40218       ],
40219       [
40220         "Pakistan (‫پاکستان‬‎)",
40221         "pk",
40222         "92"
40223       ],
40224       [
40225         "Palau",
40226         "pw",
40227         "680"
40228       ],
40229       [
40230         "Palestine (‫فلسطين‬‎)",
40231         "ps",
40232         "970"
40233       ],
40234       [
40235         "Panama (Panamá)",
40236         "pa",
40237         "507"
40238       ],
40239       [
40240         "Papua New Guinea",
40241         "pg",
40242         "675"
40243       ],
40244       [
40245         "Paraguay",
40246         "py",
40247         "595"
40248       ],
40249       [
40250         "Peru (Perú)",
40251         "pe",
40252         "51"
40253       ],
40254       [
40255         "Philippines",
40256         "ph",
40257         "63"
40258       ],
40259       [
40260         "Poland (Polska)",
40261         "pl",
40262         "48"
40263       ],
40264       [
40265         "Portugal",
40266         "pt",
40267         "351"
40268       ],
40269       [
40270         "Puerto Rico",
40271         "pr",
40272         "1",
40273         3,
40274         ["787", "939"]
40275       ],
40276       [
40277         "Qatar (‫قطر‬‎)",
40278         "qa",
40279         "974"
40280       ],
40281       [
40282         "Réunion (La Réunion)",
40283         "re",
40284         "262",
40285         0
40286       ],
40287       [
40288         "Romania (România)",
40289         "ro",
40290         "40"
40291       ],
40292       [
40293         "Russia (Россия)",
40294         "ru",
40295         "7",
40296         0
40297       ],
40298       [
40299         "Rwanda",
40300         "rw",
40301         "250"
40302       ],
40303       [
40304         "Saint Barthélemy",
40305         "bl",
40306         "590",
40307         1
40308       ],
40309       [
40310         "Saint Helena",
40311         "sh",
40312         "290"
40313       ],
40314       [
40315         "Saint Kitts and Nevis",
40316         "kn",
40317         "1869"
40318       ],
40319       [
40320         "Saint Lucia",
40321         "lc",
40322         "1758"
40323       ],
40324       [
40325         "Saint Martin (Saint-Martin (partie française))",
40326         "mf",
40327         "590",
40328         2
40329       ],
40330       [
40331         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
40332         "pm",
40333         "508"
40334       ],
40335       [
40336         "Saint Vincent and the Grenadines",
40337         "vc",
40338         "1784"
40339       ],
40340       [
40341         "Samoa",
40342         "ws",
40343         "685"
40344       ],
40345       [
40346         "San Marino",
40347         "sm",
40348         "378"
40349       ],
40350       [
40351         "São Tomé and Príncipe (São Tomé e Príncipe)",
40352         "st",
40353         "239"
40354       ],
40355       [
40356         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
40357         "sa",
40358         "966"
40359       ],
40360       [
40361         "Senegal (Sénégal)",
40362         "sn",
40363         "221"
40364       ],
40365       [
40366         "Serbia (Србија)",
40367         "rs",
40368         "381"
40369       ],
40370       [
40371         "Seychelles",
40372         "sc",
40373         "248"
40374       ],
40375       [
40376         "Sierra Leone",
40377         "sl",
40378         "232"
40379       ],
40380       [
40381         "Singapore",
40382         "sg",
40383         "65"
40384       ],
40385       [
40386         "Sint Maarten",
40387         "sx",
40388         "1721"
40389       ],
40390       [
40391         "Slovakia (Slovensko)",
40392         "sk",
40393         "421"
40394       ],
40395       [
40396         "Slovenia (Slovenija)",
40397         "si",
40398         "386"
40399       ],
40400       [
40401         "Solomon Islands",
40402         "sb",
40403         "677"
40404       ],
40405       [
40406         "Somalia (Soomaaliya)",
40407         "so",
40408         "252"
40409       ],
40410       [
40411         "South Africa",
40412         "za",
40413         "27"
40414       ],
40415       [
40416         "South Korea (대한민국)",
40417         "kr",
40418         "82"
40419       ],
40420       [
40421         "South Sudan (‫جنوب السودان‬‎)",
40422         "ss",
40423         "211"
40424       ],
40425       [
40426         "Spain (España)",
40427         "es",
40428         "34"
40429       ],
40430       [
40431         "Sri Lanka (ශ්‍රී ලංකාව)",
40432         "lk",
40433         "94"
40434       ],
40435       [
40436         "Sudan (‫السودان‬‎)",
40437         "sd",
40438         "249"
40439       ],
40440       [
40441         "Suriname",
40442         "sr",
40443         "597"
40444       ],
40445       [
40446         "Svalbard and Jan Mayen",
40447         "sj",
40448         "47",
40449         1
40450       ],
40451       [
40452         "Swaziland",
40453         "sz",
40454         "268"
40455       ],
40456       [
40457         "Sweden (Sverige)",
40458         "se",
40459         "46"
40460       ],
40461       [
40462         "Switzerland (Schweiz)",
40463         "ch",
40464         "41"
40465       ],
40466       [
40467         "Syria (‫سوريا‬‎)",
40468         "sy",
40469         "963"
40470       ],
40471       [
40472         "Taiwan (台灣)",
40473         "tw",
40474         "886"
40475       ],
40476       [
40477         "Tajikistan",
40478         "tj",
40479         "992"
40480       ],
40481       [
40482         "Tanzania",
40483         "tz",
40484         "255"
40485       ],
40486       [
40487         "Thailand (ไทย)",
40488         "th",
40489         "66"
40490       ],
40491       [
40492         "Timor-Leste",
40493         "tl",
40494         "670"
40495       ],
40496       [
40497         "Togo",
40498         "tg",
40499         "228"
40500       ],
40501       [
40502         "Tokelau",
40503         "tk",
40504         "690"
40505       ],
40506       [
40507         "Tonga",
40508         "to",
40509         "676"
40510       ],
40511       [
40512         "Trinidad and Tobago",
40513         "tt",
40514         "1868"
40515       ],
40516       [
40517         "Tunisia (‫تونس‬‎)",
40518         "tn",
40519         "216"
40520       ],
40521       [
40522         "Turkey (Türkiye)",
40523         "tr",
40524         "90"
40525       ],
40526       [
40527         "Turkmenistan",
40528         "tm",
40529         "993"
40530       ],
40531       [
40532         "Turks and Caicos Islands",
40533         "tc",
40534         "1649"
40535       ],
40536       [
40537         "Tuvalu",
40538         "tv",
40539         "688"
40540       ],
40541       [
40542         "U.S. Virgin Islands",
40543         "vi",
40544         "1340"
40545       ],
40546       [
40547         "Uganda",
40548         "ug",
40549         "256"
40550       ],
40551       [
40552         "Ukraine (Україна)",
40553         "ua",
40554         "380"
40555       ],
40556       [
40557         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
40558         "ae",
40559         "971"
40560       ],
40561       [
40562         "United Kingdom",
40563         "gb",
40564         "44",
40565         0
40566       ],
40567       [
40568         "United States",
40569         "us",
40570         "1",
40571         0
40572       ],
40573       [
40574         "Uruguay",
40575         "uy",
40576         "598"
40577       ],
40578       [
40579         "Uzbekistan (Oʻzbekiston)",
40580         "uz",
40581         "998"
40582       ],
40583       [
40584         "Vanuatu",
40585         "vu",
40586         "678"
40587       ],
40588       [
40589         "Vatican City (Città del Vaticano)",
40590         "va",
40591         "39",
40592         1
40593       ],
40594       [
40595         "Venezuela",
40596         "ve",
40597         "58"
40598       ],
40599       [
40600         "Vietnam (Việt Nam)",
40601         "vn",
40602         "84"
40603       ],
40604       [
40605         "Wallis and Futuna (Wallis-et-Futuna)",
40606         "wf",
40607         "681"
40608       ],
40609       [
40610         "Western Sahara (‫الصحراء الغربية‬‎)",
40611         "eh",
40612         "212",
40613         1
40614       ],
40615       [
40616         "Yemen (‫اليمن‬‎)",
40617         "ye",
40618         "967"
40619       ],
40620       [
40621         "Zambia",
40622         "zm",
40623         "260"
40624       ],
40625       [
40626         "Zimbabwe",
40627         "zw",
40628         "263"
40629       ],
40630       [
40631         "Åland Islands",
40632         "ax",
40633         "358",
40634         1
40635       ]
40636   ];
40637   
40638   return d;
40639 }/**
40640 *    This script refer to:
40641 *    Title: International Telephone Input
40642 *    Author: Jack O'Connor
40643 *    Code version:  v12.1.12
40644 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40645 **/
40646
40647 /**
40648  * @class Roo.bootstrap.PhoneInput
40649  * @extends Roo.bootstrap.TriggerField
40650  * An input with International dial-code selection
40651  
40652  * @cfg {String} defaultDialCode default '+852'
40653  * @cfg {Array} preferedCountries default []
40654   
40655  * @constructor
40656  * Create a new PhoneInput.
40657  * @param {Object} config Configuration options
40658  */
40659
40660 Roo.bootstrap.PhoneInput = function(config) {
40661     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40662 };
40663
40664 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40665         
40666         listWidth: undefined,
40667         
40668         selectedClass: 'active',
40669         
40670         invalidClass : "has-warning",
40671         
40672         validClass: 'has-success',
40673         
40674         allowed: '0123456789',
40675         
40676         max_length: 15,
40677         
40678         /**
40679          * @cfg {String} defaultDialCode The default dial code when initializing the input
40680          */
40681         defaultDialCode: '+852',
40682         
40683         /**
40684          * @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
40685          */
40686         preferedCountries: false,
40687         
40688         getAutoCreate : function()
40689         {
40690             var data = Roo.bootstrap.PhoneInputData();
40691             var align = this.labelAlign || this.parentLabelAlign();
40692             var id = Roo.id();
40693             
40694             this.allCountries = [];
40695             this.dialCodeMapping = [];
40696             
40697             for (var i = 0; i < data.length; i++) {
40698               var c = data[i];
40699               this.allCountries[i] = {
40700                 name: c[0],
40701                 iso2: c[1],
40702                 dialCode: c[2],
40703                 priority: c[3] || 0,
40704                 areaCodes: c[4] || null
40705               };
40706               this.dialCodeMapping[c[2]] = {
40707                   name: c[0],
40708                   iso2: c[1],
40709                   priority: c[3] || 0,
40710                   areaCodes: c[4] || null
40711               };
40712             }
40713             
40714             var cfg = {
40715                 cls: 'form-group',
40716                 cn: []
40717             };
40718             
40719             var input =  {
40720                 tag: 'input',
40721                 id : id,
40722                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40723                 maxlength: this.max_length,
40724                 cls : 'form-control tel-input',
40725                 autocomplete: 'new-password'
40726             };
40727             
40728             var hiddenInput = {
40729                 tag: 'input',
40730                 type: 'hidden',
40731                 cls: 'hidden-tel-input'
40732             };
40733             
40734             if (this.name) {
40735                 hiddenInput.name = this.name;
40736             }
40737             
40738             if (this.disabled) {
40739                 input.disabled = true;
40740             }
40741             
40742             var flag_container = {
40743                 tag: 'div',
40744                 cls: 'flag-box',
40745                 cn: [
40746                     {
40747                         tag: 'div',
40748                         cls: 'flag'
40749                     },
40750                     {
40751                         tag: 'div',
40752                         cls: 'caret'
40753                     }
40754                 ]
40755             };
40756             
40757             var box = {
40758                 tag: 'div',
40759                 cls: this.hasFeedback ? 'has-feedback' : '',
40760                 cn: [
40761                     hiddenInput,
40762                     input,
40763                     {
40764                         tag: 'input',
40765                         cls: 'dial-code-holder',
40766                         disabled: true
40767                     }
40768                 ]
40769             };
40770             
40771             var container = {
40772                 cls: 'roo-select2-container input-group',
40773                 cn: [
40774                     flag_container,
40775                     box
40776                 ]
40777             };
40778             
40779             if (this.fieldLabel.length) {
40780                 var indicator = {
40781                     tag: 'i',
40782                     tooltip: 'This field is required'
40783                 };
40784                 
40785                 var label = {
40786                     tag: 'label',
40787                     'for':  id,
40788                     cls: 'control-label',
40789                     cn: []
40790                 };
40791                 
40792                 var label_text = {
40793                     tag: 'span',
40794                     html: this.fieldLabel
40795                 };
40796                 
40797                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40798                 label.cn = [
40799                     indicator,
40800                     label_text
40801                 ];
40802                 
40803                 if(this.indicatorpos == 'right') {
40804                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40805                     label.cn = [
40806                         label_text,
40807                         indicator
40808                     ];
40809                 }
40810                 
40811                 if(align == 'left') {
40812                     container = {
40813                         tag: 'div',
40814                         cn: [
40815                             container
40816                         ]
40817                     };
40818                     
40819                     if(this.labelWidth > 12){
40820                         label.style = "width: " + this.labelWidth + 'px';
40821                     }
40822                     if(this.labelWidth < 13 && this.labelmd == 0){
40823                         this.labelmd = this.labelWidth;
40824                     }
40825                     if(this.labellg > 0){
40826                         label.cls += ' col-lg-' + this.labellg;
40827                         input.cls += ' col-lg-' + (12 - this.labellg);
40828                     }
40829                     if(this.labelmd > 0){
40830                         label.cls += ' col-md-' + this.labelmd;
40831                         container.cls += ' col-md-' + (12 - this.labelmd);
40832                     }
40833                     if(this.labelsm > 0){
40834                         label.cls += ' col-sm-' + this.labelsm;
40835                         container.cls += ' col-sm-' + (12 - this.labelsm);
40836                     }
40837                     if(this.labelxs > 0){
40838                         label.cls += ' col-xs-' + this.labelxs;
40839                         container.cls += ' col-xs-' + (12 - this.labelxs);
40840                     }
40841                 }
40842             }
40843             
40844             cfg.cn = [
40845                 label,
40846                 container
40847             ];
40848             
40849             var settings = this;
40850             
40851             ['xs','sm','md','lg'].map(function(size){
40852                 if (settings[size]) {
40853                     cfg.cls += ' col-' + size + '-' + settings[size];
40854                 }
40855             });
40856             
40857             this.store = new Roo.data.Store({
40858                 proxy : new Roo.data.MemoryProxy({}),
40859                 reader : new Roo.data.JsonReader({
40860                     fields : [
40861                         {
40862                             'name' : 'name',
40863                             'type' : 'string'
40864                         },
40865                         {
40866                             'name' : 'iso2',
40867                             'type' : 'string'
40868                         },
40869                         {
40870                             'name' : 'dialCode',
40871                             'type' : 'string'
40872                         },
40873                         {
40874                             'name' : 'priority',
40875                             'type' : 'string'
40876                         },
40877                         {
40878                             'name' : 'areaCodes',
40879                             'type' : 'string'
40880                         }
40881                     ]
40882                 })
40883             });
40884             
40885             if(!this.preferedCountries) {
40886                 this.preferedCountries = [
40887                     'hk',
40888                     'gb',
40889                     'us'
40890                 ];
40891             }
40892             
40893             var p = this.preferedCountries.reverse();
40894             
40895             if(p) {
40896                 for (var i = 0; i < p.length; i++) {
40897                     for (var j = 0; j < this.allCountries.length; j++) {
40898                         if(this.allCountries[j].iso2 == p[i]) {
40899                             var t = this.allCountries[j];
40900                             this.allCountries.splice(j,1);
40901                             this.allCountries.unshift(t);
40902                         }
40903                     } 
40904                 }
40905             }
40906             
40907             this.store.proxy.data = {
40908                 success: true,
40909                 data: this.allCountries
40910             };
40911             
40912             return cfg;
40913         },
40914         
40915         initEvents : function()
40916         {
40917             this.createList();
40918             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40919             
40920             this.indicator = this.indicatorEl();
40921             this.flag = this.flagEl();
40922             this.dialCodeHolder = this.dialCodeHolderEl();
40923             
40924             this.trigger = this.el.select('div.flag-box',true).first();
40925             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40926             
40927             var _this = this;
40928             
40929             (function(){
40930                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40931                 _this.list.setWidth(lw);
40932             }).defer(100);
40933             
40934             this.list.on('mouseover', this.onViewOver, this);
40935             this.list.on('mousemove', this.onViewMove, this);
40936             this.inputEl().on("keyup", this.onKeyUp, this);
40937             this.inputEl().on("keypress", this.onKeyPress, this);
40938             
40939             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40940
40941             this.view = new Roo.View(this.list, this.tpl, {
40942                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40943             });
40944             
40945             this.view.on('click', this.onViewClick, this);
40946             this.setValue(this.defaultDialCode);
40947         },
40948         
40949         onTriggerClick : function(e)
40950         {
40951             Roo.log('trigger click');
40952             if(this.disabled){
40953                 return;
40954             }
40955             
40956             if(this.isExpanded()){
40957                 this.collapse();
40958                 this.hasFocus = false;
40959             }else {
40960                 this.store.load({});
40961                 this.hasFocus = true;
40962                 this.expand();
40963             }
40964         },
40965         
40966         isExpanded : function()
40967         {
40968             return this.list.isVisible();
40969         },
40970         
40971         collapse : function()
40972         {
40973             if(!this.isExpanded()){
40974                 return;
40975             }
40976             this.list.hide();
40977             Roo.get(document).un('mousedown', this.collapseIf, this);
40978             Roo.get(document).un('mousewheel', this.collapseIf, this);
40979             this.fireEvent('collapse', this);
40980             this.validate();
40981         },
40982         
40983         expand : function()
40984         {
40985             Roo.log('expand');
40986
40987             if(this.isExpanded() || !this.hasFocus){
40988                 return;
40989             }
40990             
40991             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40992             this.list.setWidth(lw);
40993             
40994             this.list.show();
40995             this.restrictHeight();
40996             
40997             Roo.get(document).on('mousedown', this.collapseIf, this);
40998             Roo.get(document).on('mousewheel', this.collapseIf, this);
40999             
41000             this.fireEvent('expand', this);
41001         },
41002         
41003         restrictHeight : function()
41004         {
41005             this.list.alignTo(this.inputEl(), this.listAlign);
41006             this.list.alignTo(this.inputEl(), this.listAlign);
41007         },
41008         
41009         onViewOver : function(e, t)
41010         {
41011             if(this.inKeyMode){
41012                 return;
41013             }
41014             var item = this.view.findItemFromChild(t);
41015             
41016             if(item){
41017                 var index = this.view.indexOf(item);
41018                 this.select(index, false);
41019             }
41020         },
41021
41022         // private
41023         onViewClick : function(view, doFocus, el, e)
41024         {
41025             var index = this.view.getSelectedIndexes()[0];
41026             
41027             var r = this.store.getAt(index);
41028             
41029             if(r){
41030                 this.onSelect(r, index);
41031             }
41032             if(doFocus !== false && !this.blockFocus){
41033                 this.inputEl().focus();
41034             }
41035         },
41036         
41037         onViewMove : function(e, t)
41038         {
41039             this.inKeyMode = false;
41040         },
41041         
41042         select : function(index, scrollIntoView)
41043         {
41044             this.selectedIndex = index;
41045             this.view.select(index);
41046             if(scrollIntoView !== false){
41047                 var el = this.view.getNode(index);
41048                 if(el){
41049                     this.list.scrollChildIntoView(el, false);
41050                 }
41051             }
41052         },
41053         
41054         createList : function()
41055         {
41056             this.list = Roo.get(document.body).createChild({
41057                 tag: 'ul',
41058                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
41059                 style: 'display:none'
41060             });
41061             
41062             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
41063         },
41064         
41065         collapseIf : function(e)
41066         {
41067             var in_combo  = e.within(this.el);
41068             var in_list =  e.within(this.list);
41069             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
41070             
41071             if (in_combo || in_list || is_list) {
41072                 return;
41073             }
41074             this.collapse();
41075         },
41076         
41077         onSelect : function(record, index)
41078         {
41079             if(this.fireEvent('beforeselect', this, record, index) !== false){
41080                 
41081                 this.setFlagClass(record.data.iso2);
41082                 this.setDialCode(record.data.dialCode);
41083                 this.hasFocus = false;
41084                 this.collapse();
41085                 this.fireEvent('select', this, record, index);
41086             }
41087         },
41088         
41089         flagEl : function()
41090         {
41091             var flag = this.el.select('div.flag',true).first();
41092             if(!flag){
41093                 return false;
41094             }
41095             return flag;
41096         },
41097         
41098         dialCodeHolderEl : function()
41099         {
41100             var d = this.el.select('input.dial-code-holder',true).first();
41101             if(!d){
41102                 return false;
41103             }
41104             return d;
41105         },
41106         
41107         setDialCode : function(v)
41108         {
41109             this.dialCodeHolder.dom.value = '+'+v;
41110         },
41111         
41112         setFlagClass : function(n)
41113         {
41114             this.flag.dom.className = 'flag '+n;
41115         },
41116         
41117         getValue : function()
41118         {
41119             var v = this.inputEl().getValue();
41120             if(this.dialCodeHolder) {
41121                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
41122             }
41123             return v;
41124         },
41125         
41126         setValue : function(v)
41127         {
41128             var d = this.getDialCode(v);
41129             
41130             //invalid dial code
41131             if(v.length == 0 || !d || d.length == 0) {
41132                 if(this.rendered){
41133                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41134                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41135                 }
41136                 return;
41137             }
41138             
41139             //valid dial code
41140             this.setFlagClass(this.dialCodeMapping[d].iso2);
41141             this.setDialCode(d);
41142             this.inputEl().dom.value = v.replace('+'+d,'');
41143             this.hiddenEl().dom.value = this.getValue();
41144             
41145             this.validate();
41146         },
41147         
41148         getDialCode : function(v)
41149         {
41150             v = v ||  '';
41151             
41152             if (v.length == 0) {
41153                 return this.dialCodeHolder.dom.value;
41154             }
41155             
41156             var dialCode = "";
41157             if (v.charAt(0) != "+") {
41158                 return false;
41159             }
41160             var numericChars = "";
41161             for (var i = 1; i < v.length; i++) {
41162               var c = v.charAt(i);
41163               if (!isNaN(c)) {
41164                 numericChars += c;
41165                 if (this.dialCodeMapping[numericChars]) {
41166                   dialCode = v.substr(1, i);
41167                 }
41168                 if (numericChars.length == 4) {
41169                   break;
41170                 }
41171               }
41172             }
41173             return dialCode;
41174         },
41175         
41176         reset : function()
41177         {
41178             this.setValue(this.defaultDialCode);
41179             this.validate();
41180         },
41181         
41182         hiddenEl : function()
41183         {
41184             return this.el.select('input.hidden-tel-input',true).first();
41185         },
41186         
41187         // after setting val
41188         onKeyUp : function(e){
41189             this.setValue(this.getValue());
41190         },
41191         
41192         onKeyPress : function(e){
41193             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
41194                 e.stopEvent();
41195             }
41196         }
41197         
41198 });
41199 /**
41200  * @class Roo.bootstrap.MoneyField
41201  * @extends Roo.bootstrap.ComboBox
41202  * Bootstrap MoneyField class
41203  * 
41204  * @constructor
41205  * Create a new MoneyField.
41206  * @param {Object} config Configuration options
41207  */
41208
41209 Roo.bootstrap.MoneyField = function(config) {
41210     
41211     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
41212     
41213 };
41214
41215 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
41216     
41217     /**
41218      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41219      */
41220     allowDecimals : true,
41221     /**
41222      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41223      */
41224     decimalSeparator : ".",
41225     /**
41226      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41227      */
41228     decimalPrecision : 0,
41229     /**
41230      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41231      */
41232     allowNegative : true,
41233     /**
41234      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41235      */
41236     allowZero: true,
41237     /**
41238      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41239      */
41240     minValue : Number.NEGATIVE_INFINITY,
41241     /**
41242      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41243      */
41244     maxValue : Number.MAX_VALUE,
41245     /**
41246      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41247      */
41248     minText : "The minimum value for this field is {0}",
41249     /**
41250      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41251      */
41252     maxText : "The maximum value for this field is {0}",
41253     /**
41254      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41255      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41256      */
41257     nanText : "{0} is not a valid number",
41258     /**
41259      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
41260      */
41261     castInt : true,
41262     /**
41263      * @cfg {String} defaults currency of the MoneyField
41264      * value should be in lkey
41265      */
41266     defaultCurrency : false,
41267     /**
41268      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41269      */
41270     thousandsDelimiter : false,
41271     /**
41272      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
41273      */
41274     max_length: false,
41275     
41276     inputlg : 9,
41277     inputmd : 9,
41278     inputsm : 9,
41279     inputxs : 6,
41280     
41281     store : false,
41282     
41283     getAutoCreate : function()
41284     {
41285         var align = this.labelAlign || this.parentLabelAlign();
41286         
41287         var id = Roo.id();
41288
41289         var cfg = {
41290             cls: 'form-group',
41291             cn: []
41292         };
41293
41294         var input =  {
41295             tag: 'input',
41296             id : id,
41297             cls : 'form-control roo-money-amount-input',
41298             autocomplete: 'new-password'
41299         };
41300         
41301         var hiddenInput = {
41302             tag: 'input',
41303             type: 'hidden',
41304             id: Roo.id(),
41305             cls: 'hidden-number-input'
41306         };
41307         
41308         if(this.max_length) {
41309             input.maxlength = this.max_length; 
41310         }
41311         
41312         if (this.name) {
41313             hiddenInput.name = this.name;
41314         }
41315
41316         if (this.disabled) {
41317             input.disabled = true;
41318         }
41319
41320         var clg = 12 - this.inputlg;
41321         var cmd = 12 - this.inputmd;
41322         var csm = 12 - this.inputsm;
41323         var cxs = 12 - this.inputxs;
41324         
41325         var container = {
41326             tag : 'div',
41327             cls : 'row roo-money-field',
41328             cn : [
41329                 {
41330                     tag : 'div',
41331                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
41332                     cn : [
41333                         {
41334                             tag : 'div',
41335                             cls: 'roo-select2-container input-group',
41336                             cn: [
41337                                 {
41338                                     tag : 'input',
41339                                     cls : 'form-control roo-money-currency-input',
41340                                     autocomplete: 'new-password',
41341                                     readOnly : 1,
41342                                     name : this.currencyName
41343                                 },
41344                                 {
41345                                     tag :'span',
41346                                     cls : 'input-group-addon',
41347                                     cn : [
41348                                         {
41349                                             tag: 'span',
41350                                             cls: 'caret'
41351                                         }
41352                                     ]
41353                                 }
41354                             ]
41355                         }
41356                     ]
41357                 },
41358                 {
41359                     tag : 'div',
41360                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41361                     cn : [
41362                         {
41363                             tag: 'div',
41364                             cls: this.hasFeedback ? 'has-feedback' : '',
41365                             cn: [
41366                                 input
41367                             ]
41368                         }
41369                     ]
41370                 }
41371             ]
41372             
41373         };
41374         
41375         if (this.fieldLabel.length) {
41376             var indicator = {
41377                 tag: 'i',
41378                 tooltip: 'This field is required'
41379             };
41380
41381             var label = {
41382                 tag: 'label',
41383                 'for':  id,
41384                 cls: 'control-label',
41385                 cn: []
41386             };
41387
41388             var label_text = {
41389                 tag: 'span',
41390                 html: this.fieldLabel
41391             };
41392
41393             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41394             label.cn = [
41395                 indicator,
41396                 label_text
41397             ];
41398
41399             if(this.indicatorpos == 'right') {
41400                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41401                 label.cn = [
41402                     label_text,
41403                     indicator
41404                 ];
41405             }
41406
41407             if(align == 'left') {
41408                 container = {
41409                     tag: 'div',
41410                     cn: [
41411                         container
41412                     ]
41413                 };
41414
41415                 if(this.labelWidth > 12){
41416                     label.style = "width: " + this.labelWidth + 'px';
41417                 }
41418                 if(this.labelWidth < 13 && this.labelmd == 0){
41419                     this.labelmd = this.labelWidth;
41420                 }
41421                 if(this.labellg > 0){
41422                     label.cls += ' col-lg-' + this.labellg;
41423                     input.cls += ' col-lg-' + (12 - this.labellg);
41424                 }
41425                 if(this.labelmd > 0){
41426                     label.cls += ' col-md-' + this.labelmd;
41427                     container.cls += ' col-md-' + (12 - this.labelmd);
41428                 }
41429                 if(this.labelsm > 0){
41430                     label.cls += ' col-sm-' + this.labelsm;
41431                     container.cls += ' col-sm-' + (12 - this.labelsm);
41432                 }
41433                 if(this.labelxs > 0){
41434                     label.cls += ' col-xs-' + this.labelxs;
41435                     container.cls += ' col-xs-' + (12 - this.labelxs);
41436                 }
41437             }
41438         }
41439
41440         cfg.cn = [
41441             label,
41442             container,
41443             hiddenInput
41444         ];
41445         
41446         var settings = this;
41447
41448         ['xs','sm','md','lg'].map(function(size){
41449             if (settings[size]) {
41450                 cfg.cls += ' col-' + size + '-' + settings[size];
41451             }
41452         });
41453         
41454         return cfg;
41455     },
41456     
41457     initEvents : function()
41458     {
41459         this.indicator = this.indicatorEl();
41460         
41461         this.initCurrencyEvent();
41462         
41463         this.initNumberEvent();
41464     },
41465     
41466     initCurrencyEvent : function()
41467     {
41468         if (!this.store) {
41469             throw "can not find store for combo";
41470         }
41471         
41472         this.store = Roo.factory(this.store, Roo.data);
41473         this.store.parent = this;
41474         
41475         this.createList();
41476         
41477         this.triggerEl = this.el.select('.input-group-addon', true).first();
41478         
41479         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41480         
41481         var _this = this;
41482         
41483         (function(){
41484             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41485             _this.list.setWidth(lw);
41486         }).defer(100);
41487         
41488         this.list.on('mouseover', this.onViewOver, this);
41489         this.list.on('mousemove', this.onViewMove, this);
41490         this.list.on('scroll', this.onViewScroll, this);
41491         
41492         if(!this.tpl){
41493             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41494         }
41495         
41496         this.view = new Roo.View(this.list, this.tpl, {
41497             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41498         });
41499         
41500         this.view.on('click', this.onViewClick, this);
41501         
41502         this.store.on('beforeload', this.onBeforeLoad, this);
41503         this.store.on('load', this.onLoad, this);
41504         this.store.on('loadexception', this.onLoadException, this);
41505         
41506         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41507             "up" : function(e){
41508                 this.inKeyMode = true;
41509                 this.selectPrev();
41510             },
41511
41512             "down" : function(e){
41513                 if(!this.isExpanded()){
41514                     this.onTriggerClick();
41515                 }else{
41516                     this.inKeyMode = true;
41517                     this.selectNext();
41518                 }
41519             },
41520
41521             "enter" : function(e){
41522                 this.collapse();
41523                 
41524                 if(this.fireEvent("specialkey", this, e)){
41525                     this.onViewClick(false);
41526                 }
41527                 
41528                 return true;
41529             },
41530
41531             "esc" : function(e){
41532                 this.collapse();
41533             },
41534
41535             "tab" : function(e){
41536                 this.collapse();
41537                 
41538                 if(this.fireEvent("specialkey", this, e)){
41539                     this.onViewClick(false);
41540                 }
41541                 
41542                 return true;
41543             },
41544
41545             scope : this,
41546
41547             doRelay : function(foo, bar, hname){
41548                 if(hname == 'down' || this.scope.isExpanded()){
41549                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41550                 }
41551                 return true;
41552             },
41553
41554             forceKeyDown: true
41555         });
41556         
41557         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41558         
41559     },
41560     
41561     initNumberEvent : function(e)
41562     {
41563         this.inputEl().on("keydown" , this.fireKey,  this);
41564         this.inputEl().on("focus", this.onFocus,  this);
41565         this.inputEl().on("blur", this.onBlur,  this);
41566         
41567         this.inputEl().relayEvent('keyup', this);
41568         
41569         if(this.indicator){
41570             this.indicator.addClass('invisible');
41571         }
41572  
41573         this.originalValue = this.getValue();
41574         
41575         if(this.validationEvent == 'keyup'){
41576             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41577             this.inputEl().on('keyup', this.filterValidation, this);
41578         }
41579         else if(this.validationEvent !== false){
41580             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41581         }
41582         
41583         if(this.selectOnFocus){
41584             this.on("focus", this.preFocus, this);
41585             
41586         }
41587         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41588             this.inputEl().on("keypress", this.filterKeys, this);
41589         } else {
41590             this.inputEl().relayEvent('keypress', this);
41591         }
41592         
41593         var allowed = "0123456789";
41594         
41595         if(this.allowDecimals){
41596             allowed += this.decimalSeparator;
41597         }
41598         
41599         if(this.allowNegative){
41600             allowed += "-";
41601         }
41602         
41603         if(this.thousandsDelimiter) {
41604             allowed += ",";
41605         }
41606         
41607         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41608         
41609         var keyPress = function(e){
41610             
41611             var k = e.getKey();
41612             
41613             var c = e.getCharCode();
41614             
41615             if(
41616                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41617                     allowed.indexOf(String.fromCharCode(c)) === -1
41618             ){
41619                 e.stopEvent();
41620                 return;
41621             }
41622             
41623             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41624                 return;
41625             }
41626             
41627             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41628                 e.stopEvent();
41629             }
41630         };
41631         
41632         this.inputEl().on("keypress", keyPress, this);
41633         
41634     },
41635     
41636     onTriggerClick : function(e)
41637     {   
41638         if(this.disabled){
41639             return;
41640         }
41641         
41642         this.page = 0;
41643         this.loadNext = false;
41644         
41645         if(this.isExpanded()){
41646             this.collapse();
41647             return;
41648         }
41649         
41650         this.hasFocus = true;
41651         
41652         if(this.triggerAction == 'all') {
41653             this.doQuery(this.allQuery, true);
41654             return;
41655         }
41656         
41657         this.doQuery(this.getRawValue());
41658     },
41659     
41660     getCurrency : function()
41661     {   
41662         var v = this.currencyEl().getValue();
41663         
41664         return v;
41665     },
41666     
41667     restrictHeight : function()
41668     {
41669         this.list.alignTo(this.currencyEl(), this.listAlign);
41670         this.list.alignTo(this.currencyEl(), this.listAlign);
41671     },
41672     
41673     onViewClick : function(view, doFocus, el, e)
41674     {
41675         var index = this.view.getSelectedIndexes()[0];
41676         
41677         var r = this.store.getAt(index);
41678         
41679         if(r){
41680             this.onSelect(r, index);
41681         }
41682     },
41683     
41684     onSelect : function(record, index){
41685         
41686         if(this.fireEvent('beforeselect', this, record, index) !== false){
41687         
41688             this.setFromCurrencyData(index > -1 ? record.data : false);
41689             
41690             this.collapse();
41691             
41692             this.fireEvent('select', this, record, index);
41693         }
41694     },
41695     
41696     setFromCurrencyData : function(o)
41697     {
41698         var currency = '';
41699         
41700         this.lastCurrency = o;
41701         
41702         if (this.currencyField) {
41703             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41704         } else {
41705             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41706         }
41707         
41708         this.lastSelectionText = currency;
41709         
41710         //setting default currency
41711         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41712             this.setCurrency(this.defaultCurrency);
41713             return;
41714         }
41715         
41716         this.setCurrency(currency);
41717     },
41718     
41719     setFromData : function(o)
41720     {
41721         var c = {};
41722         
41723         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41724         
41725         this.setFromCurrencyData(c);
41726         
41727         var value = '';
41728         
41729         if (this.name) {
41730             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41731         } else {
41732             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41733         }
41734         
41735         this.setValue(value);
41736         
41737     },
41738     
41739     setCurrency : function(v)
41740     {   
41741         this.currencyValue = v;
41742         
41743         if(this.rendered){
41744             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41745             this.validate();
41746         }
41747     },
41748     
41749     setValue : function(v)
41750     {
41751         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41752         
41753         this.value = v;
41754         
41755         if(this.rendered){
41756             
41757             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41758             
41759             this.inputEl().dom.value = (v == '') ? '' :
41760                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41761             
41762             if(!this.allowZero && v === '0') {
41763                 this.hiddenEl().dom.value = '';
41764                 this.inputEl().dom.value = '';
41765             }
41766             
41767             this.validate();
41768         }
41769     },
41770     
41771     getRawValue : function()
41772     {
41773         var v = this.inputEl().getValue();
41774         
41775         return v;
41776     },
41777     
41778     getValue : function()
41779     {
41780         return this.fixPrecision(this.parseValue(this.getRawValue()));
41781     },
41782     
41783     parseValue : function(value)
41784     {
41785         if(this.thousandsDelimiter) {
41786             value += "";
41787             r = new RegExp(",", "g");
41788             value = value.replace(r, "");
41789         }
41790         
41791         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41792         return isNaN(value) ? '' : value;
41793         
41794     },
41795     
41796     fixPrecision : function(value)
41797     {
41798         if(this.thousandsDelimiter) {
41799             value += "";
41800             r = new RegExp(",", "g");
41801             value = value.replace(r, "");
41802         }
41803         
41804         var nan = isNaN(value);
41805         
41806         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41807             return nan ? '' : value;
41808         }
41809         return parseFloat(value).toFixed(this.decimalPrecision);
41810     },
41811     
41812     decimalPrecisionFcn : function(v)
41813     {
41814         return Math.floor(v);
41815     },
41816     
41817     validateValue : function(value)
41818     {
41819         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41820             return false;
41821         }
41822         
41823         var num = this.parseValue(value);
41824         
41825         if(isNaN(num)){
41826             this.markInvalid(String.format(this.nanText, value));
41827             return false;
41828         }
41829         
41830         if(num < this.minValue){
41831             this.markInvalid(String.format(this.minText, this.minValue));
41832             return false;
41833         }
41834         
41835         if(num > this.maxValue){
41836             this.markInvalid(String.format(this.maxText, this.maxValue));
41837             return false;
41838         }
41839         
41840         return true;
41841     },
41842     
41843     validate : function()
41844     {
41845         if(this.disabled || this.allowBlank){
41846             this.markValid();
41847             return true;
41848         }
41849         
41850         var currency = this.getCurrency();
41851         
41852         if(this.validateValue(this.getRawValue()) && currency.length){
41853             this.markValid();
41854             return true;
41855         }
41856         
41857         this.markInvalid();
41858         return false;
41859     },
41860     
41861     getName: function()
41862     {
41863         return this.name;
41864     },
41865     
41866     beforeBlur : function()
41867     {
41868         if(!this.castInt){
41869             return;
41870         }
41871         
41872         var v = this.parseValue(this.getRawValue());
41873         
41874         if(v || v == 0){
41875             this.setValue(v);
41876         }
41877     },
41878     
41879     onBlur : function()
41880     {
41881         this.beforeBlur();
41882         
41883         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41884             //this.el.removeClass(this.focusClass);
41885         }
41886         
41887         this.hasFocus = false;
41888         
41889         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41890             this.validate();
41891         }
41892         
41893         var v = this.getValue();
41894         
41895         if(String(v) !== String(this.startValue)){
41896             this.fireEvent('change', this, v, this.startValue);
41897         }
41898         
41899         this.fireEvent("blur", this);
41900     },
41901     
41902     inputEl : function()
41903     {
41904         return this.el.select('.roo-money-amount-input', true).first();
41905     },
41906     
41907     currencyEl : function()
41908     {
41909         return this.el.select('.roo-money-currency-input', true).first();
41910     },
41911     
41912     hiddenEl : function()
41913     {
41914         return this.el.select('input.hidden-number-input',true).first();
41915     }
41916     
41917 });/**
41918  * @class Roo.bootstrap.BezierSignature
41919  * @extends Roo.bootstrap.Component
41920  * Bootstrap BezierSignature class
41921  * This script refer to:
41922  *    Title: Signature Pad
41923  *    Author: szimek
41924  *    Availability: https://github.com/szimek/signature_pad
41925  *
41926  * @constructor
41927  * Create a new BezierSignature
41928  * @param {Object} config The config object
41929  */
41930
41931 Roo.bootstrap.BezierSignature = function(config){
41932     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
41933     this.addEvents({
41934         "resize" : true
41935     });
41936 };
41937
41938 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
41939 {
41940      
41941     curve_data: [],
41942     
41943     is_empty: true,
41944     
41945     mouse_btn_down: true,
41946     
41947     /**
41948      * @cfg {int} canvas height
41949      */
41950     canvas_height: '200px',
41951     
41952     /**
41953      * @cfg {float|function} Radius of a single dot.
41954      */ 
41955     dot_size: false,
41956     
41957     /**
41958      * @cfg {float} Minimum width of a line. Defaults to 0.5.
41959      */
41960     min_width: 0.5,
41961     
41962     /**
41963      * @cfg {float} Maximum width of a line. Defaults to 2.5.
41964      */
41965     max_width: 2.5,
41966     
41967     /**
41968      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
41969      */
41970     throttle: 16,
41971     
41972     /**
41973      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
41974      */
41975     min_distance: 5,
41976     
41977     /**
41978      * @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.
41979      */
41980     bg_color: 'rgba(0, 0, 0, 0)',
41981     
41982     /**
41983      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
41984      */
41985     dot_color: 'black',
41986     
41987     /**
41988      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
41989      */ 
41990     velocity_filter_weight: 0.7,
41991     
41992     /**
41993      * @cfg {function} Callback when stroke begin. 
41994      */
41995     onBegin: false,
41996     
41997     /**
41998      * @cfg {function} Callback when stroke end.
41999      */
42000     onEnd: false,
42001     
42002     getAutoCreate : function()
42003     {
42004         var cls = 'roo-signature column';
42005         
42006         if(this.cls){
42007             cls += ' ' + this.cls;
42008         }
42009         
42010         var col_sizes = [
42011             'lg',
42012             'md',
42013             'sm',
42014             'xs'
42015         ];
42016         
42017         for(var i = 0; i < col_sizes.length; i++) {
42018             if(this[col_sizes[i]]) {
42019                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
42020             }
42021         }
42022         
42023         var cfg = {
42024             tag: 'div',
42025             cls: cls,
42026             cn: [
42027                 {
42028                     tag: 'div',
42029                     cls: 'roo-signature-body',
42030                     cn: [
42031                         {
42032                             tag: 'canvas',
42033                             cls: 'roo-signature-body-canvas',
42034                             height: this.canvas_height,
42035                             width: this.canvas_width
42036                         }
42037                     ]
42038                 },
42039                 {
42040                     tag: 'input',
42041                     type: 'file',
42042                     style: 'display: none'
42043                 }
42044             ]
42045         };
42046         
42047         return cfg;
42048     },
42049     
42050     initEvents: function() 
42051     {
42052         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
42053         
42054         var canvas = this.canvasEl();
42055         
42056         // mouse && touch event swapping...
42057         canvas.dom.style.touchAction = 'none';
42058         canvas.dom.style.msTouchAction = 'none';
42059         
42060         this.mouse_btn_down = false;
42061         canvas.on('mousedown', this._handleMouseDown, this);
42062         canvas.on('mousemove', this._handleMouseMove, this);
42063         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
42064         
42065         if (window.PointerEvent) {
42066             canvas.on('pointerdown', this._handleMouseDown, this);
42067             canvas.on('pointermove', this._handleMouseMove, this);
42068             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
42069         }
42070         
42071         if ('ontouchstart' in window) {
42072             canvas.on('touchstart', this._handleTouchStart, this);
42073             canvas.on('touchmove', this._handleTouchMove, this);
42074             canvas.on('touchend', this._handleTouchEnd, this);
42075         }
42076         
42077         Roo.EventManager.onWindowResize(this.resize, this, true);
42078         
42079         // file input event
42080         this.fileEl().on('change', this.uploadImage, this);
42081         
42082         this.clear();
42083         
42084         this.resize();
42085     },
42086     
42087     resize: function(){
42088         
42089         var canvas = this.canvasEl().dom;
42090         var ctx = this.canvasElCtx();
42091         var img_data = false;
42092         
42093         if(canvas.width > 0) {
42094             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
42095         }
42096         // setting canvas width will clean img data
42097         canvas.width = 0;
42098         
42099         var style = window.getComputedStyle ? 
42100             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
42101             
42102         var padding_left = parseInt(style.paddingLeft) || 0;
42103         var padding_right = parseInt(style.paddingRight) || 0;
42104         
42105         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
42106         
42107         if(img_data) {
42108             ctx.putImageData(img_data, 0, 0);
42109         }
42110     },
42111     
42112     _handleMouseDown: function(e)
42113     {
42114         if (e.browserEvent.which === 1) {
42115             this.mouse_btn_down = true;
42116             this.strokeBegin(e);
42117         }
42118     },
42119     
42120     _handleMouseMove: function (e)
42121     {
42122         if (this.mouse_btn_down) {
42123             this.strokeMoveUpdate(e);
42124         }
42125     },
42126     
42127     _handleMouseUp: function (e)
42128     {
42129         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
42130             this.mouse_btn_down = false;
42131             this.strokeEnd(e);
42132         }
42133     },
42134     
42135     _handleTouchStart: function (e) {
42136         
42137         e.preventDefault();
42138         if (e.browserEvent.targetTouches.length === 1) {
42139             // var touch = e.browserEvent.changedTouches[0];
42140             // this.strokeBegin(touch);
42141             
42142              this.strokeBegin(e); // assume e catching the correct xy...
42143         }
42144     },
42145     
42146     _handleTouchMove: function (e) {
42147         e.preventDefault();
42148         // var touch = event.targetTouches[0];
42149         // _this._strokeMoveUpdate(touch);
42150         this.strokeMoveUpdate(e);
42151     },
42152     
42153     _handleTouchEnd: function (e) {
42154         var wasCanvasTouched = e.target === this.canvasEl().dom;
42155         if (wasCanvasTouched) {
42156             e.preventDefault();
42157             // var touch = event.changedTouches[0];
42158             // _this._strokeEnd(touch);
42159             this.strokeEnd(e);
42160         }
42161     },
42162     
42163     reset: function () {
42164         this._lastPoints = [];
42165         this._lastVelocity = 0;
42166         this._lastWidth = (this.min_width + this.max_width) / 2;
42167         this.canvasElCtx().fillStyle = this.dot_color;
42168     },
42169     
42170     strokeMoveUpdate: function(e)
42171     {
42172         this.strokeUpdate(e);
42173         
42174         if (this.throttle) {
42175             this.throttleStroke(this.strokeUpdate, this.throttle);
42176         }
42177         else {
42178             this.strokeUpdate(e);
42179         }
42180     },
42181     
42182     strokeBegin: function(e)
42183     {
42184         var newPointGroup = {
42185             color: this.dot_color,
42186             points: []
42187         };
42188         
42189         if (typeof this.onBegin === 'function') {
42190             this.onBegin(e);
42191         }
42192         
42193         this.curve_data.push(newPointGroup);
42194         this.reset();
42195         this.strokeUpdate(e);
42196     },
42197     
42198     strokeUpdate: function(e)
42199     {
42200         var rect = this.canvasEl().dom.getBoundingClientRect();
42201         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
42202         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
42203         var lastPoints = lastPointGroup.points;
42204         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
42205         var isLastPointTooClose = lastPoint
42206             ? point.distanceTo(lastPoint) <= this.min_distance
42207             : false;
42208         var color = lastPointGroup.color;
42209         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
42210             var curve = this.addPoint(point);
42211             if (!lastPoint) {
42212                 this.drawDot({color: color, point: point});
42213             }
42214             else if (curve) {
42215                 this.drawCurve({color: color, curve: curve});
42216             }
42217             lastPoints.push({
42218                 time: point.time,
42219                 x: point.x,
42220                 y: point.y
42221             });
42222         }
42223     },
42224     
42225     strokeEnd: function(e)
42226     {
42227         this.strokeUpdate(e);
42228         if (typeof this.onEnd === 'function') {
42229             this.onEnd(e);
42230         }
42231     },
42232     
42233     addPoint:  function (point) {
42234         var _lastPoints = this._lastPoints;
42235         _lastPoints.push(point);
42236         if (_lastPoints.length > 2) {
42237             if (_lastPoints.length === 3) {
42238                 _lastPoints.unshift(_lastPoints[0]);
42239             }
42240             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
42241             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
42242             _lastPoints.shift();
42243             return curve;
42244         }
42245         return null;
42246     },
42247     
42248     calculateCurveWidths: function (startPoint, endPoint) {
42249         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
42250             (1 - this.velocity_filter_weight) * this._lastVelocity;
42251
42252         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
42253         var widths = {
42254             end: newWidth,
42255             start: this._lastWidth
42256         };
42257         
42258         this._lastVelocity = velocity;
42259         this._lastWidth = newWidth;
42260         return widths;
42261     },
42262     
42263     drawDot: function (_a) {
42264         var color = _a.color, point = _a.point;
42265         var ctx = this.canvasElCtx();
42266         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
42267         ctx.beginPath();
42268         this.drawCurveSegment(point.x, point.y, width);
42269         ctx.closePath();
42270         ctx.fillStyle = color;
42271         ctx.fill();
42272     },
42273     
42274     drawCurve: function (_a) {
42275         var color = _a.color, curve = _a.curve;
42276         var ctx = this.canvasElCtx();
42277         var widthDelta = curve.endWidth - curve.startWidth;
42278         var drawSteps = Math.floor(curve.length()) * 2;
42279         ctx.beginPath();
42280         ctx.fillStyle = color;
42281         for (var i = 0; i < drawSteps; i += 1) {
42282         var t = i / drawSteps;
42283         var tt = t * t;
42284         var ttt = tt * t;
42285         var u = 1 - t;
42286         var uu = u * u;
42287         var uuu = uu * u;
42288         var x = uuu * curve.startPoint.x;
42289         x += 3 * uu * t * curve.control1.x;
42290         x += 3 * u * tt * curve.control2.x;
42291         x += ttt * curve.endPoint.x;
42292         var y = uuu * curve.startPoint.y;
42293         y += 3 * uu * t * curve.control1.y;
42294         y += 3 * u * tt * curve.control2.y;
42295         y += ttt * curve.endPoint.y;
42296         var width = curve.startWidth + ttt * widthDelta;
42297         this.drawCurveSegment(x, y, width);
42298         }
42299         ctx.closePath();
42300         ctx.fill();
42301     },
42302     
42303     drawCurveSegment: function (x, y, width) {
42304         var ctx = this.canvasElCtx();
42305         ctx.moveTo(x, y);
42306         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
42307         this.is_empty = false;
42308     },
42309     
42310     clear: function()
42311     {
42312         var ctx = this.canvasElCtx();
42313         var canvas = this.canvasEl().dom;
42314         ctx.fillStyle = this.bg_color;
42315         ctx.clearRect(0, 0, canvas.width, canvas.height);
42316         ctx.fillRect(0, 0, canvas.width, canvas.height);
42317         this.curve_data = [];
42318         this.reset();
42319         this.is_empty = true;
42320     },
42321     
42322     fileEl: function()
42323     {
42324         return  this.el.select('input',true).first();
42325     },
42326     
42327     canvasEl: function()
42328     {
42329         return this.el.select('canvas',true).first();
42330     },
42331     
42332     canvasElCtx: function()
42333     {
42334         return this.el.select('canvas',true).first().dom.getContext('2d');
42335     },
42336     
42337     getImage: function(type)
42338     {
42339         if(this.is_empty) {
42340             return false;
42341         }
42342         
42343         // encryption ?
42344         return this.canvasEl().dom.toDataURL('image/'+type, 1);
42345     },
42346     
42347     drawFromImage: function(img_src)
42348     {
42349         var img = new Image();
42350         
42351         img.onload = function(){
42352             this.canvasElCtx().drawImage(img, 0, 0);
42353         }.bind(this);
42354         
42355         img.src = img_src;
42356         
42357         this.is_empty = false;
42358     },
42359     
42360     selectImage: function()
42361     {
42362         this.fileEl().dom.click();
42363     },
42364     
42365     uploadImage: function(e)
42366     {
42367         var reader = new FileReader();
42368         
42369         reader.onload = function(e){
42370             var img = new Image();
42371             img.onload = function(){
42372                 this.reset();
42373                 this.canvasElCtx().drawImage(img, 0, 0);
42374             }.bind(this);
42375             img.src = e.target.result;
42376         }.bind(this);
42377         
42378         reader.readAsDataURL(e.target.files[0]);
42379     },
42380     
42381     // Bezier Point Constructor
42382     Point: (function () {
42383         function Point(x, y, time) {
42384             this.x = x;
42385             this.y = y;
42386             this.time = time || Date.now();
42387         }
42388         Point.prototype.distanceTo = function (start) {
42389             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42390         };
42391         Point.prototype.equals = function (other) {
42392             return this.x === other.x && this.y === other.y && this.time === other.time;
42393         };
42394         Point.prototype.velocityFrom = function (start) {
42395             return this.time !== start.time
42396             ? this.distanceTo(start) / (this.time - start.time)
42397             : 0;
42398         };
42399         return Point;
42400     }()),
42401     
42402     
42403     // Bezier Constructor
42404     Bezier: (function () {
42405         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42406             this.startPoint = startPoint;
42407             this.control2 = control2;
42408             this.control1 = control1;
42409             this.endPoint = endPoint;
42410             this.startWidth = startWidth;
42411             this.endWidth = endWidth;
42412         }
42413         Bezier.fromPoints = function (points, widths, scope) {
42414             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42415             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42416             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42417         };
42418         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42419             var dx1 = s1.x - s2.x;
42420             var dy1 = s1.y - s2.y;
42421             var dx2 = s2.x - s3.x;
42422             var dy2 = s2.y - s3.y;
42423             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42424             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42425             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42426             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42427             var dxm = m1.x - m2.x;
42428             var dym = m1.y - m2.y;
42429             var k = l2 / (l1 + l2);
42430             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42431             var tx = s2.x - cm.x;
42432             var ty = s2.y - cm.y;
42433             return {
42434                 c1: new scope.Point(m1.x + tx, m1.y + ty),
42435                 c2: new scope.Point(m2.x + tx, m2.y + ty)
42436             };
42437         };
42438         Bezier.prototype.length = function () {
42439             var steps = 10;
42440             var length = 0;
42441             var px;
42442             var py;
42443             for (var i = 0; i <= steps; i += 1) {
42444                 var t = i / steps;
42445                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42446                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42447                 if (i > 0) {
42448                     var xdiff = cx - px;
42449                     var ydiff = cy - py;
42450                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42451                 }
42452                 px = cx;
42453                 py = cy;
42454             }
42455             return length;
42456         };
42457         Bezier.prototype.point = function (t, start, c1, c2, end) {
42458             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42459             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42460             + (3.0 * c2 * (1.0 - t) * t * t)
42461             + (end * t * t * t);
42462         };
42463         return Bezier;
42464     }()),
42465     
42466     throttleStroke: function(fn, wait) {
42467       if (wait === void 0) { wait = 250; }
42468       var previous = 0;
42469       var timeout = null;
42470       var result;
42471       var storedContext;
42472       var storedArgs;
42473       var later = function () {
42474           previous = Date.now();
42475           timeout = null;
42476           result = fn.apply(storedContext, storedArgs);
42477           if (!timeout) {
42478               storedContext = null;
42479               storedArgs = [];
42480           }
42481       };
42482       return function wrapper() {
42483           var args = [];
42484           for (var _i = 0; _i < arguments.length; _i++) {
42485               args[_i] = arguments[_i];
42486           }
42487           var now = Date.now();
42488           var remaining = wait - (now - previous);
42489           storedContext = this;
42490           storedArgs = args;
42491           if (remaining <= 0 || remaining > wait) {
42492               if (timeout) {
42493                   clearTimeout(timeout);
42494                   timeout = null;
42495               }
42496               previous = now;
42497               result = fn.apply(storedContext, storedArgs);
42498               if (!timeout) {
42499                   storedContext = null;
42500                   storedArgs = [];
42501               }
42502           }
42503           else if (!timeout) {
42504               timeout = window.setTimeout(later, remaining);
42505           }
42506           return result;
42507       };
42508   }
42509   
42510 });
42511
42512  
42513
42514